Add layers to Gui API so specific elements can be drawn on top of others and react to mouse clicks first

This commit is contained in:
johni0702
2015-07-11 01:37:31 +02:00
parent 39558701bf
commit 219277f623
8 changed files with 193 additions and 68 deletions

View File

@@ -45,10 +45,6 @@ public class OffsetGuiRenderer implements GuiRenderer {
this.renderer = renderer; this.renderer = renderer;
this.position = position; this.position = position;
this.size = size; this.size = size;
if (size.getHeight() > renderer.getSize().getHeight()
|| size.getWidth() > renderer.getSize().getWidth()) {
throw new IllegalArgumentException("Size must not be larger than size of renderer.");
}
} }
@Override @Override

View File

@@ -31,9 +31,14 @@ public class RenderInfo {
public final float partialTick; public final float partialTick;
public final int mouseX; public final int mouseX;
public final int mouseY; public final int mouseY;
public final int layer;
public RenderInfo offsetMouse(int minusX, int minusY) { public RenderInfo offsetMouse(int minusX, int minusY) {
return new RenderInfo(partialTick, mouseX - minusX, mouseY - minusY); return new RenderInfo(partialTick, mouseX - minusX, mouseY - minusY, layer);
}
public RenderInfo layer(int layer) {
return new RenderInfo(partialTick, mouseX, mouseY, layer);
} }
public void addTo(CrashReport crashReport) { public void addTo(CrashReport crashReport) {
@@ -41,5 +46,6 @@ public class RenderInfo {
category.addCrashSection("Partial Tick", partialTick); category.addCrashSection("Partial Tick", partialTick);
category.addCrashSection("Mouse X", mouseX); category.addCrashSection("Mouse X", mouseX);
category.addCrashSection("Mouse Y", mouseY); category.addCrashSection("Mouse Y", mouseY);
category.addCrashSection("Layer", layer);
} }
} }

View File

@@ -26,6 +26,7 @@ import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.OffsetGuiRenderer; import de.johni0702.minecraft.gui.OffsetGuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.element.AbstractComposedGuiElement; import de.johni0702.minecraft.gui.element.AbstractComposedGuiElement;
import de.johni0702.minecraft.gui.element.ComposedGuiElement;
import de.johni0702.minecraft.gui.element.GuiElement; import de.johni0702.minecraft.gui.element.GuiElement;
import de.johni0702.minecraft.gui.layout.Layout; import de.johni0702.minecraft.gui.layout.Layout;
import de.johni0702.minecraft.gui.layout.LayoutData; import de.johni0702.minecraft.gui.layout.LayoutData;
@@ -129,6 +130,16 @@ public abstract class AbstractGuiContainer<T extends AbstractGuiContainer<T>>
throw new ReportedException(crashReport); throw new ReportedException(crashReport);
} }
for (final Map.Entry<GuiElement, Pair<ReadablePoint, ReadableDimension>> e : layedOutElements.entrySet()) { for (final Map.Entry<GuiElement, Pair<ReadablePoint, ReadableDimension>> e : layedOutElements.entrySet()) {
GuiElement element = e.getKey();
if (element instanceof ComposedGuiElement) {
if (((ComposedGuiElement) element).getMaxLayer() < renderInfo.layer) {
continue;
}
} else {
if (element.getLayer() != renderInfo.layer) {
continue;
}
}
final ReadablePoint ePosition = e.getValue().getLeft(); final ReadablePoint ePosition = e.getValue().getLeft();
final ReadableDimension eSize = e.getValue().getRight(); final ReadableDimension eSize = e.getValue().getRight();
try { try {

View File

@@ -67,67 +67,70 @@ public abstract class AbstractGuiScreen<T extends AbstractGuiScreen<T>> extends
@Override @Override
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) { public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
if (drawBackground) { if (renderInfo.layer == 0) {
wrapped.drawDefaultBackground(); if (drawBackground) {
} wrapped.drawDefaultBackground();
if (title != null) { }
ReadableDimension titleSize = title.getMinSize(); if (title != null) {
int x = screenSize.getWidth() / 2 - titleSize.getWidth() / 2; ReadableDimension titleSize = title.getMinSize();
OffsetGuiRenderer eRenderer = new OffsetGuiRenderer(renderer, new Point(x, 10), new Dimension(0, 0)); int x = screenSize.getWidth() / 2 - titleSize.getWidth() / 2;
title.draw(eRenderer, titleSize, null); OffsetGuiRenderer eRenderer = new OffsetGuiRenderer(renderer, new Point(x, 10), new Dimension(0, 0));
title.draw(eRenderer, titleSize, null);
}
} }
super.draw(renderer, size, renderInfo); super.draw(renderer, size, renderInfo);
if (renderInfo.layer == getMaxLayer()) {
final GuiElement tooltip = forEach(GuiElement.class).getTooltip(renderInfo); final GuiElement tooltip = forEach(GuiElement.class).getTooltip(renderInfo);
if (tooltip != null) { if (tooltip != null) {
final ReadableDimension tooltipSize = tooltip.getMinSize(); final ReadableDimension tooltipSize = tooltip.getMinSize();
int dx, dy; int dx, dy;
if (renderInfo.mouseX + 8 + tooltipSize.getWidth() < screenSize.getWidth()) { if (renderInfo.mouseX + 8 + tooltipSize.getWidth() < screenSize.getWidth()) {
dx = 8; dx = 8;
} else { } else {
dx = -8; dx = -8;
} }
if (renderInfo.mouseY + 8 + tooltipSize.getHeight() < screenSize.getHeight()) { if (renderInfo.mouseY + 8 + tooltipSize.getHeight() < screenSize.getHeight()) {
dy = 8; dy = 8;
} else { } else {
dy = -8; dy = -8;
} }
final ReadablePoint position = new Point(renderInfo.mouseX + dx, renderInfo.mouseY + dy); final ReadablePoint position = new Point(renderInfo.mouseX + dx, renderInfo.mouseY + dy);
try { try {
OffsetGuiRenderer eRenderer = new OffsetGuiRenderer(renderer, position, tooltipSize); OffsetGuiRenderer eRenderer = new OffsetGuiRenderer(renderer, position, tooltipSize);
tooltip.draw(eRenderer, tooltipSize, renderInfo.offsetMouse(position.getX(), position.getY())); tooltip.draw(eRenderer, tooltipSize, renderInfo.offsetMouse(position.getX(), position.getY()));
} catch (Exception ex) { } catch (Exception ex) {
CrashReport crashReport = CrashReport.makeCrashReport(ex, "Rendering Gui Tooltip"); CrashReport crashReport = CrashReport.makeCrashReport(ex, "Rendering Gui Tooltip");
renderInfo.addTo(crashReport); renderInfo.addTo(crashReport);
CrashReportCategory category = crashReport.makeCategory("Gui container details"); CrashReportCategory category = crashReport.makeCategory("Gui container details");
category.addCrashSectionCallable("Container", new Callable() { category.addCrashSectionCallable("Container", new Callable() {
@Override @Override
public Object call() throws Exception { public Object call() throws Exception {
return this; return this;
} }
}); });
category.addCrashSection("Width", size.getWidth()); category.addCrashSection("Width", size.getWidth());
category.addCrashSection("Height", size.getHeight()); category.addCrashSection("Height", size.getHeight());
category = crashReport.makeCategory("Tooltip details"); category = crashReport.makeCategory("Tooltip details");
category.addCrashSectionCallable("Element", new Callable() { category.addCrashSectionCallable("Element", new Callable() {
@Override @Override
public Object call() throws Exception { public Object call() throws Exception {
return tooltip; return tooltip;
} }
}); });
category.addCrashSectionCallable("Position", new Callable() { category.addCrashSectionCallable("Position", new Callable() {
@Override @Override
public Object call() throws Exception { public Object call() throws Exception {
return position; return position;
} }
}); });
category.addCrashSectionCallable("Size", new Callable() { category.addCrashSectionCallable("Size", new Callable() {
@Override @Override
public Object call() throws Exception { public Object call() throws Exception {
return tooltipSize; return tooltipSize;
} }
}); });
throw new ReportedException(crashReport); throw new ReportedException(crashReport);
}
} }
} }
} }
@@ -159,7 +162,10 @@ public abstract class AbstractGuiScreen<T extends AbstractGuiScreen<T>> extends
@Override @Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) { public void drawScreen(int mouseX, int mouseY, float partialTicks) {
draw(renderer, screenSize, new RenderInfo(partialTicks, mouseX, mouseY)); int layers = getMaxLayer();
for (int layer = 0; layer <= layers; layer++) {
draw(renderer, screenSize, new RenderInfo(partialTicks, mouseX, mouseY, layer));
}
} }
@Override @Override

View File

@@ -30,6 +30,8 @@ import net.minecraft.util.ReportedException;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
public abstract class AbstractComposedGuiElement<T extends AbstractComposedGuiElement<T>> public abstract class AbstractComposedGuiElement<T extends AbstractComposedGuiElement<T>>
@@ -41,19 +43,103 @@ public abstract class AbstractComposedGuiElement<T extends AbstractComposedGuiEl
super(container); super(container);
} }
@Override
public int getMaxLayer() {
int maxLayer = getLayer();
for (GuiElement element : getChildren()) {
int elementMaxLayer;
if (element instanceof ComposedGuiElement) {
elementMaxLayer = ((ComposedGuiElement) element).getMaxLayer();
} else {
elementMaxLayer = element.getLayer();
}
if (elementMaxLayer > maxLayer) {
maxLayer = elementMaxLayer;
}
}
return maxLayer;
}
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <C> C forEach(final Class<C> ofType) { public <C> C forEach(final Class<C> ofType) {
int maxLayer = getMaxLayer();
final List<C> layers = new ArrayList<C>(maxLayer + 1);
for (int i = 0; i <= maxLayer; i++) {
layers.add(forEach(i, ofType));
}
return (C) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ofType}, new InvocationHandler() { return (C) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ofType}, new InvocationHandler() {
@Override @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
boolean isGetter = method.getName().startsWith("get"); boolean isGetter = method.getName().startsWith("get");
Object handled = null; Object handled = method.getReturnType().equals(Boolean.class) ? false : null;
for (final C layer : layers) {
handled = method.invoke(layer, args);
if (handled != null) {
if (handled instanceof Boolean) {
if (Boolean.TRUE.equals(handled)) {
break;
}
} else if (isGetter) {
return handled;
}
}
}
return handled;
}
});
}
@Override
@SuppressWarnings("unchecked")
public <C> C forEach(final int layer, final Class<C> ofType) {
return (C) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ofType}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
boolean isGetter = method.getName().startsWith("get");
Object handled = method.getReturnType().equals(boolean.class) ? false : null;
final AbstractComposedGuiElement self = AbstractComposedGuiElement.this;
if (ofType.isInstance(self) && self.getLayer() == layer) {
try {
handled = method.invoke(self, args);
} catch (Exception e) {
CrashReport crash = CrashReport.makeCrashReport(e, "Calling Gui method");
CrashReportCategory category = crash.makeCategory("Gui");
category.addCrashSection("Method", method);
category.addCrashSectionCallable("ComposedElement", new Callable() {
@Override
public Object call() throws Exception {
return self;
}
});
category.addCrashSectionCallable("Element", new Callable() {
@Override
public Object call() throws Exception {
return self;
}
});
throw new ReportedException(crash);
}
if (handled != null) {
if (handled instanceof Boolean) {
if (Boolean.TRUE.equals(handled)) {
return true;
}
} else if (isGetter) {
return handled;
}
}
}
for (final GuiElement element : getChildren()) { for (final GuiElement element : getChildren()) {
try { try {
if (element instanceof ComposedGuiElement) { if (element instanceof ComposedGuiElement) {
handled = method.invoke(((ComposedGuiElement) element).forEach(ofType), args); ComposedGuiElement composed = (ComposedGuiElement) element;
} else if (ofType.isInstance(element)) { if (layer <= composed.getMaxLayer()) {
Object elementProxy = composed.forEach(layer, ofType);
handled = method.invoke(elementProxy, args);
}
} else if (ofType.isInstance(element) && element.getLayer() == layer) {
handled = method.invoke(element, args); handled = method.invoke(element, args);
} }
if (handled != null) { if (handled != null) {

View File

@@ -147,6 +147,10 @@ public abstract class AbstractGuiElement<T extends AbstractGuiElement<T>> implem
return getThis(); return getThis();
} }
public int getLayer() {
return 0;
}
@Override @Override
public ReadableDimension getMaxSize() { public ReadableDimension getMaxSize() {
return maxSize == null ? getPreferredSize() : maxSize; return maxSize == null ? getPreferredSize() : maxSize;

View File

@@ -28,4 +28,13 @@ public interface ComposedGuiElement<T extends ComposedGuiElement<T>> extends Gui
Collection<GuiElement> getChildren(); Collection<GuiElement> getChildren();
<C> C forEach(Class<C> ofType); <C> C forEach(Class<C> ofType);
<C> C forEach(int layer, Class<C> ofType);
/**
* Returns the highest layer this element or any of its children take part in.
* Events will be called for this composed gui element for all layers between
* layer 0 (inclusive) and the returned maximum layer (inclusive).
* @return Highest layer relevant to this element
*/
int getMaxLayer();
} }

View File

@@ -52,4 +52,11 @@ public interface GuiElement<T extends GuiElement<T>> {
GuiElement getTooltip(RenderInfo renderInfo); GuiElement getTooltip(RenderInfo renderInfo);
T setTooltip(GuiElement tooltip); T setTooltip(GuiElement tooltip);
/**
* Returns the layer this element takes part in.
* The standard layer is layer 0. Event handlers will be called for this layer.
* @return The layer of this element
*/
int getLayer();
} }