338 lines
9.3 KiB
Java
338 lines
9.3 KiB
Java
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<GuiRenderState.Node> strata = new ArrayList();
|
|
private int firstStratumAfterBlur = Integer.MAX_VALUE;
|
|
private GuiRenderState.Node current;
|
|
private final Set<Object> 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<? extends ScreenArea> 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<Object> 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<GuiItemRenderState> 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<GuiTextRenderState> 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<PictureInPictureRenderState> 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<GuiElementRenderState> comparator) {
|
|
this.traverse(node -> {
|
|
if (node.elementStates != null) {
|
|
node.elementStates.sort(comparator);
|
|
}
|
|
}, GuiRenderState.TraverseRange.ALL);
|
|
}
|
|
|
|
private void traverse(Consumer<GuiRenderState.Node> 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<GuiRenderState.Node> 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<GuiElementRenderState> elementStates;
|
|
@Nullable
|
|
public List<GuiElementRenderState> glyphStates;
|
|
@Nullable
|
|
public List<GuiItemRenderState> itemStates;
|
|
@Nullable
|
|
public List<GuiTextRenderState> textStates;
|
|
@Nullable
|
|
public List<PictureInPictureRenderState> 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;
|
|
}
|
|
}
|