From 219277f623d4bf273886296d36560beee0908f2d Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 11 Jul 2015 01:37:31 +0200 Subject: [PATCH] Add layers to Gui API so specific elements can be drawn on top of others and react to mouse clicks first --- .../minecraft/gui/OffsetGuiRenderer.java | 4 - .../johni0702/minecraft/gui/RenderInfo.java | 8 +- .../gui/container/AbstractGuiContainer.java | 11 ++ .../gui/container/AbstractGuiScreen.java | 126 +++++++++--------- .../element/AbstractComposedGuiElement.java | 92 ++++++++++++- .../gui/element/AbstractGuiElement.java | 4 + .../gui/element/ComposedGuiElement.java | 9 ++ .../minecraft/gui/element/GuiElement.java | 7 + 8 files changed, 193 insertions(+), 68 deletions(-) diff --git a/src/main/java/de/johni0702/minecraft/gui/OffsetGuiRenderer.java b/src/main/java/de/johni0702/minecraft/gui/OffsetGuiRenderer.java index 65c44302..6d64bc8c 100644 --- a/src/main/java/de/johni0702/minecraft/gui/OffsetGuiRenderer.java +++ b/src/main/java/de/johni0702/minecraft/gui/OffsetGuiRenderer.java @@ -45,10 +45,6 @@ public class OffsetGuiRenderer implements GuiRenderer { this.renderer = renderer; this.position = position; 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 diff --git a/src/main/java/de/johni0702/minecraft/gui/RenderInfo.java b/src/main/java/de/johni0702/minecraft/gui/RenderInfo.java index 8019685f..98fb0ec6 100644 --- a/src/main/java/de/johni0702/minecraft/gui/RenderInfo.java +++ b/src/main/java/de/johni0702/minecraft/gui/RenderInfo.java @@ -31,9 +31,14 @@ public class RenderInfo { public final float partialTick; public final int mouseX; public final int mouseY; + public final int layer; 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) { @@ -41,5 +46,6 @@ public class RenderInfo { category.addCrashSection("Partial Tick", partialTick); category.addCrashSection("Mouse X", mouseX); category.addCrashSection("Mouse Y", mouseY); + category.addCrashSection("Layer", layer); } } diff --git a/src/main/java/de/johni0702/minecraft/gui/container/AbstractGuiContainer.java b/src/main/java/de/johni0702/minecraft/gui/container/AbstractGuiContainer.java index aea4420f..6b0e9255 100644 --- a/src/main/java/de/johni0702/minecraft/gui/container/AbstractGuiContainer.java +++ b/src/main/java/de/johni0702/minecraft/gui/container/AbstractGuiContainer.java @@ -26,6 +26,7 @@ import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.OffsetGuiRenderer; import de.johni0702.minecraft.gui.RenderInfo; 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.layout.Layout; import de.johni0702.minecraft.gui.layout.LayoutData; @@ -129,6 +130,16 @@ public abstract class AbstractGuiContainer> throw new ReportedException(crashReport); } for (final Map.Entry> 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 ReadableDimension eSize = e.getValue().getRight(); try { diff --git a/src/main/java/de/johni0702/minecraft/gui/container/AbstractGuiScreen.java b/src/main/java/de/johni0702/minecraft/gui/container/AbstractGuiScreen.java index 51c33170..520d214e 100644 --- a/src/main/java/de/johni0702/minecraft/gui/container/AbstractGuiScreen.java +++ b/src/main/java/de/johni0702/minecraft/gui/container/AbstractGuiScreen.java @@ -67,67 +67,70 @@ public abstract class AbstractGuiScreen> extends @Override public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) { - if (drawBackground) { - wrapped.drawDefaultBackground(); - } - if (title != null) { - ReadableDimension titleSize = title.getMinSize(); - int x = screenSize.getWidth() / 2 - titleSize.getWidth() / 2; - OffsetGuiRenderer eRenderer = new OffsetGuiRenderer(renderer, new Point(x, 10), new Dimension(0, 0)); - title.draw(eRenderer, titleSize, null); + if (renderInfo.layer == 0) { + if (drawBackground) { + wrapped.drawDefaultBackground(); + } + if (title != null) { + ReadableDimension titleSize = title.getMinSize(); + int x = screenSize.getWidth() / 2 - titleSize.getWidth() / 2; + OffsetGuiRenderer eRenderer = new OffsetGuiRenderer(renderer, new Point(x, 10), new Dimension(0, 0)); + title.draw(eRenderer, titleSize, null); + } } super.draw(renderer, size, renderInfo); - - final GuiElement tooltip = forEach(GuiElement.class).getTooltip(renderInfo); - if (tooltip != null) { - final ReadableDimension tooltipSize = tooltip.getMinSize(); - int dx, dy; - if (renderInfo.mouseX + 8 + tooltipSize.getWidth() < screenSize.getWidth()) { - dx = 8; - } else { - dx = -8; - } - if (renderInfo.mouseY + 8 + tooltipSize.getHeight() < screenSize.getHeight()) { - dy = 8; - } else { - dy = -8; - } - final ReadablePoint position = new Point(renderInfo.mouseX + dx, renderInfo.mouseY + dy); - try { - OffsetGuiRenderer eRenderer = new OffsetGuiRenderer(renderer, position, tooltipSize); - tooltip.draw(eRenderer, tooltipSize, renderInfo.offsetMouse(position.getX(), position.getY())); - } catch (Exception ex) { - CrashReport crashReport = CrashReport.makeCrashReport(ex, "Rendering Gui Tooltip"); - renderInfo.addTo(crashReport); - CrashReportCategory category = crashReport.makeCategory("Gui container details"); - category.addCrashSectionCallable("Container", new Callable() { - @Override - public Object call() throws Exception { - return this; - } - }); - category.addCrashSection("Width", size.getWidth()); - category.addCrashSection("Height", size.getHeight()); - category = crashReport.makeCategory("Tooltip details"); - category.addCrashSectionCallable("Element", new Callable() { - @Override - public Object call() throws Exception { - return tooltip; - } - }); - category.addCrashSectionCallable("Position", new Callable() { - @Override - public Object call() throws Exception { - return position; - } - }); - category.addCrashSectionCallable("Size", new Callable() { - @Override - public Object call() throws Exception { - return tooltipSize; - } - }); - throw new ReportedException(crashReport); + if (renderInfo.layer == getMaxLayer()) { + final GuiElement tooltip = forEach(GuiElement.class).getTooltip(renderInfo); + if (tooltip != null) { + final ReadableDimension tooltipSize = tooltip.getMinSize(); + int dx, dy; + if (renderInfo.mouseX + 8 + tooltipSize.getWidth() < screenSize.getWidth()) { + dx = 8; + } else { + dx = -8; + } + if (renderInfo.mouseY + 8 + tooltipSize.getHeight() < screenSize.getHeight()) { + dy = 8; + } else { + dy = -8; + } + final ReadablePoint position = new Point(renderInfo.mouseX + dx, renderInfo.mouseY + dy); + try { + OffsetGuiRenderer eRenderer = new OffsetGuiRenderer(renderer, position, tooltipSize); + tooltip.draw(eRenderer, tooltipSize, renderInfo.offsetMouse(position.getX(), position.getY())); + } catch (Exception ex) { + CrashReport crashReport = CrashReport.makeCrashReport(ex, "Rendering Gui Tooltip"); + renderInfo.addTo(crashReport); + CrashReportCategory category = crashReport.makeCategory("Gui container details"); + category.addCrashSectionCallable("Container", new Callable() { + @Override + public Object call() throws Exception { + return this; + } + }); + category.addCrashSection("Width", size.getWidth()); + category.addCrashSection("Height", size.getHeight()); + category = crashReport.makeCategory("Tooltip details"); + category.addCrashSectionCallable("Element", new Callable() { + @Override + public Object call() throws Exception { + return tooltip; + } + }); + category.addCrashSectionCallable("Position", new Callable() { + @Override + public Object call() throws Exception { + return position; + } + }); + category.addCrashSectionCallable("Size", new Callable() { + @Override + public Object call() throws Exception { + return tooltipSize; + } + }); + throw new ReportedException(crashReport); + } } } } @@ -159,7 +162,10 @@ public abstract class AbstractGuiScreen> extends @Override 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 diff --git a/src/main/java/de/johni0702/minecraft/gui/element/AbstractComposedGuiElement.java b/src/main/java/de/johni0702/minecraft/gui/element/AbstractComposedGuiElement.java index 02de7b89..c4c5c860 100644 --- a/src/main/java/de/johni0702/minecraft/gui/element/AbstractComposedGuiElement.java +++ b/src/main/java/de/johni0702/minecraft/gui/element/AbstractComposedGuiElement.java @@ -30,6 +30,8 @@ import net.minecraft.util.ReportedException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Callable; public abstract class AbstractComposedGuiElement> @@ -41,19 +43,103 @@ public abstract class AbstractComposedGuiElement maxLayer) { + maxLayer = elementMaxLayer; + } + } + return maxLayer; + } + @Override @SuppressWarnings("unchecked") public C forEach(final Class ofType) { + int maxLayer = getMaxLayer(); + final List layers = new ArrayList(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() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 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 forEach(final int layer, final Class 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()) { try { if (element instanceof ComposedGuiElement) { - handled = method.invoke(((ComposedGuiElement) element).forEach(ofType), args); - } else if (ofType.isInstance(element)) { + ComposedGuiElement composed = (ComposedGuiElement) 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); } if (handled != null) { diff --git a/src/main/java/de/johni0702/minecraft/gui/element/AbstractGuiElement.java b/src/main/java/de/johni0702/minecraft/gui/element/AbstractGuiElement.java index 974d9fe6..835ac12a 100644 --- a/src/main/java/de/johni0702/minecraft/gui/element/AbstractGuiElement.java +++ b/src/main/java/de/johni0702/minecraft/gui/element/AbstractGuiElement.java @@ -147,6 +147,10 @@ public abstract class AbstractGuiElement> implem return getThis(); } + public int getLayer() { + return 0; + } + @Override public ReadableDimension getMaxSize() { return maxSize == null ? getPreferredSize() : maxSize; diff --git a/src/main/java/de/johni0702/minecraft/gui/element/ComposedGuiElement.java b/src/main/java/de/johni0702/minecraft/gui/element/ComposedGuiElement.java index fddcf9d8..02dc3741 100644 --- a/src/main/java/de/johni0702/minecraft/gui/element/ComposedGuiElement.java +++ b/src/main/java/de/johni0702/minecraft/gui/element/ComposedGuiElement.java @@ -28,4 +28,13 @@ public interface ComposedGuiElement> extends Gui Collection getChildren(); C forEach(Class ofType); + C forEach(int layer, Class 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(); } diff --git a/src/main/java/de/johni0702/minecraft/gui/element/GuiElement.java b/src/main/java/de/johni0702/minecraft/gui/element/GuiElement.java index 108b7e50..2c9b5dc8 100644 --- a/src/main/java/de/johni0702/minecraft/gui/element/GuiElement.java +++ b/src/main/java/de/johni0702/minecraft/gui/element/GuiElement.java @@ -52,4 +52,11 @@ public interface GuiElement> { GuiElement getTooltip(RenderInfo renderInfo); 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(); + }