package net.minecraft.client.gui.render.state; import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Consumer; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.client.gui.render.state.pip.PictureInPictureRenderState; import org.apache.commons.lang3.mutable.MutableInt; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public class GuiRenderState { private static final int DEBUG_RECTANGLE_COLOR = 2000962815; private final List strata = new ArrayList(); private int firstStratumAfterBlur = Integer.MAX_VALUE; private GuiRenderState.Node current; private final Set itemModelIdentities = new HashSet(); @Nullable private ScreenRectangle lastElementBounds; public GuiRenderState() { this.nextStratum(); } public void nextStratum() { this.current = new GuiRenderState.Node(null); this.strata.add(this.current); } public void blurBeforeThisStratum() { if (this.firstStratumAfterBlur != Integer.MAX_VALUE) { throw new IllegalStateException("Can only blur once per frame"); } else { this.firstStratumAfterBlur = this.strata.size() - 1; } } public void up() { if (this.current.up == null) { this.current.up = new GuiRenderState.Node(this.current); } this.current = this.current.up; } public void down() { if (this.current.down == null) { this.current.down = new GuiRenderState.Node(this.current); } this.current = this.current.down; } public void submitItem(GuiItemRenderState renderState) { if (this.findAppropriateNode(renderState)) { this.itemModelIdentities.add(renderState.itemStackRenderState().getModelIdentity()); this.current.submitItem(renderState); this.sumbitDebugRectangleIfEnabled(renderState.bounds()); } } public void submitText(GuiTextRenderState renderState) { if (this.findAppropriateNode(renderState)) { this.current.submitText(renderState); this.sumbitDebugRectangleIfEnabled(renderState.bounds()); } } public void submitPicturesInPictureState(PictureInPictureRenderState renderState) { if (this.findAppropriateNode(renderState)) { this.current.submitPicturesInPictureState(renderState); this.sumbitDebugRectangleIfEnabled(renderState.bounds()); } } public void submitGuiElement(GuiElementRenderState renderState) { if (this.findAppropriateNode(renderState)) { this.current.submitGuiElement(renderState); this.sumbitDebugRectangleIfEnabled(renderState.bounds()); } } private void sumbitDebugRectangleIfEnabled(@Nullable ScreenRectangle debugRectangle) { } private boolean findAppropriateNode(ScreenArea screenArea) { ScreenRectangle screenRectangle = screenArea.bounds(); if (screenRectangle == null) { return false; } else { if (this.lastElementBounds != null && this.lastElementBounds.encompasses(screenRectangle)) { this.up(); } else { this.navigateToAboveHighestElementWithIntersectingBounds(screenRectangle); } this.lastElementBounds = screenRectangle; return true; } } private void navigateToAboveHighestElementWithIntersectingBounds(ScreenRectangle rectangle) { GuiRenderState.Node node = (GuiRenderState.Node)this.strata.getLast(); while (node.up != null) { node = node.up; } boolean bl = false; while (!bl) { bl = this.hasIntersection(rectangle, node.elementStates) || this.hasIntersection(rectangle, node.itemStates) || this.hasIntersection(rectangle, node.textStates) || this.hasIntersection(rectangle, node.picturesInPictureStates); if (node.parent == null) { break; } if (!bl) { node = node.parent; } } this.current = node; if (bl) { this.up(); } } private boolean hasIntersection(ScreenRectangle rectangle, @Nullable List screenAreas) { if (screenAreas != null) { for (ScreenArea screenArea : screenAreas) { ScreenRectangle screenRectangle = screenArea.bounds(); if (screenRectangle != null && screenRectangle.intersects(rectangle)) { return true; } } } return false; } public void submitBlitToCurrentLayer(BlitRenderState renderState) { this.current.submitGuiElement(renderState); } public void submitGlyphToCurrentLayer(GuiElementRenderState renderState) { this.current.submitGlyph(renderState); } public Set getItemModelIdentities() { return this.itemModelIdentities; } public void forEachElement(GuiRenderState.LayeredElementConsumer action, GuiRenderState.TraverseRange traverseRange) { MutableInt mutableInt = new MutableInt(0); this.traverse(node -> { if (node.elementStates != null || node.glyphStates != null) { int i = mutableInt.incrementAndGet(); if (node.elementStates != null) { for (GuiElementRenderState guiElementRenderState : node.elementStates) { action.accept(guiElementRenderState, i); } } if (node.glyphStates != null) { for (GuiElementRenderState guiElementRenderState : node.glyphStates) { action.accept(guiElementRenderState, i); } } } }, traverseRange); } public void forEachItem(Consumer action) { GuiRenderState.Node node = this.current; this.traverse(nodex -> { if (nodex.itemStates != null) { this.current = nodex; for (GuiItemRenderState guiItemRenderState : nodex.itemStates) { action.accept(guiItemRenderState); } } }, GuiRenderState.TraverseRange.ALL); this.current = node; } public void forEachText(Consumer action) { GuiRenderState.Node node = this.current; this.traverse(nodex -> { if (nodex.textStates != null) { for (GuiTextRenderState guiTextRenderState : nodex.textStates) { this.current = nodex; action.accept(guiTextRenderState); } } }, GuiRenderState.TraverseRange.ALL); this.current = node; } public void forEachPictureInPicture(Consumer action) { GuiRenderState.Node node = this.current; this.traverse(nodex -> { if (nodex.picturesInPictureStates != null) { this.current = nodex; for (PictureInPictureRenderState pictureInPictureRenderState : nodex.picturesInPictureStates) { action.accept(pictureInPictureRenderState); } } }, GuiRenderState.TraverseRange.ALL); this.current = node; } public void sortElements(Comparator comparator) { this.traverse(node -> { if (node.elementStates != null) { node.elementStates.sort(comparator); } }, GuiRenderState.TraverseRange.ALL); } private void traverse(Consumer action, GuiRenderState.TraverseRange traverseRange) { int i = 0; int j = this.strata.size(); if (traverseRange == GuiRenderState.TraverseRange.BEFORE_BLUR) { j = Math.min(this.firstStratumAfterBlur, this.strata.size()); } else if (traverseRange == GuiRenderState.TraverseRange.AFTER_BLUR) { i = this.firstStratumAfterBlur; } for (int k = i; k < j; k++) { GuiRenderState.Node node = (GuiRenderState.Node)this.strata.get(k); this.traverse(node, action); } } private void traverse(GuiRenderState.Node node, Consumer action) { if (node.down != null) { this.traverse(node.down, action); } action.accept(node); if (node.up != null) { this.traverse(node.up, action); } } public void reset() { this.itemModelIdentities.clear(); this.strata.clear(); this.firstStratumAfterBlur = Integer.MAX_VALUE; this.nextStratum(); } @Environment(EnvType.CLIENT) public interface LayeredElementConsumer { void accept(GuiElementRenderState guiElementRenderState, int i); } @Environment(EnvType.CLIENT) static class Node { @Nullable public final GuiRenderState.Node parent; @Nullable public GuiRenderState.Node up; @Nullable public GuiRenderState.Node down; @Nullable public List elementStates; @Nullable public List glyphStates; @Nullable public List itemStates; @Nullable public List textStates; @Nullable public List picturesInPictureStates; Node(@Nullable GuiRenderState.Node parent) { this.parent = parent; } public void submitItem(GuiItemRenderState renderState) { if (this.itemStates == null) { this.itemStates = new ArrayList(); } this.itemStates.add(renderState); } public void submitText(GuiTextRenderState renderState) { if (this.textStates == null) { this.textStates = new ArrayList(); } this.textStates.add(renderState); } public void submitPicturesInPictureState(PictureInPictureRenderState renderState) { if (this.picturesInPictureStates == null) { this.picturesInPictureStates = new ArrayList(); } this.picturesInPictureStates.add(renderState); } public void submitGuiElement(GuiElementRenderState renderState) { if (this.elementStates == null) { this.elementStates = new ArrayList(); } this.elementStates.add(renderState); } public void submitGlyph(GuiElementRenderState renderState) { if (this.glyphStates == null) { this.glyphStates = new ArrayList(); } this.glyphStates.add(renderState); } } @Environment(EnvType.CLIENT) public static enum TraverseRange { ALL, BEFORE_BLUR, AFTER_BLUR; } }