diff --git a/src/main/java/codechicken/lib/gui/modular/elements/GuiElement.java b/src/main/java/codechicken/lib/gui/modular/elements/GuiElement.java index f692118d..4ea5e1ac 100644 --- a/src/main/java/codechicken/lib/gui/modular/elements/GuiElement.java +++ b/src/main/java/codechicken/lib/gui/modular/elements/GuiElement.java @@ -226,6 +226,10 @@ public T setEnableToolTip(Supplier enableToolTip) { return SneakyUtils.unsafeCast(this); } + public boolean isTooltipEnabled() { + return enableToolTip.get(); + } + @Override public boolean blockMouseOver(GuiElement element, double mouseX, double mouseY) { return getParent().blockMouseOver(element, mouseX, mouseY); @@ -410,8 +414,12 @@ public boolean renderOverlay(GuiRender render, double mouseX, double mouseY, flo return consumed || (showToolTip() && renderTooltip(render, mouseX, mouseY)); } - private boolean showToolTip() { - return isMouseOver() && enableToolTip.get() && hoverTime() >= getTooltipDelay(); + /** + * @return true if all conditions are bet for this element to be rendering its tool tip. + * Meaning mouse is over the element, hover time has elapsed and tooltip is enabled. + */ + public boolean showToolTip() { + return isMouseOver() && isTooltipEnabled() && hoverTime() >= getTooltipDelay(); } /** diff --git a/src/main/java/codechicken/lib/gui/modular/elements/GuiSlots.java b/src/main/java/codechicken/lib/gui/modular/elements/GuiSlots.java index b798be25..aa21af09 100644 --- a/src/main/java/codechicken/lib/gui/modular/elements/GuiSlots.java +++ b/src/main/java/codechicken/lib/gui/modular/elements/GuiSlots.java @@ -7,9 +7,12 @@ import codechicken.lib.gui.modular.lib.geometry.Constraint; import codechicken.lib.gui.modular.lib.geometry.GeoParam; import codechicken.lib.gui.modular.lib.geometry.GuiParent; +import codechicken.lib.gui.modular.lib.geometry.Position; import codechicken.lib.gui.modular.sprite.CCGuiTextures; import codechicken.lib.gui.modular.sprite.Material; +import net.covers1624.quack.collection.FastStream; import net.minecraft.world.inventory.Slot; +import org.apache.logging.log4j.util.TriConsumer; import org.jetbrains.annotations.NotNull; import java.util.function.Function; @@ -38,9 +41,10 @@ public class GuiSlots extends GuiElement implements BackgroundRender { private final SlotGroup slots; private final ContainerScreenAccess screenAccess; - private Material slotTexture = CCGuiTextures.getUncached("widgets/slot"); - private Function slotIcons = slot -> null; - private Function highlightColour = slot -> 0x80ffffff; + private Function slotTexture = slot -> CCGuiTextures.getUncached("widgets/slot"); + private Function slotIcons = slot -> null; + private Function highlightColour = slot -> 0x80ffffff; + private TriConsumer slotOverlay = null; private int xSlotSpacing = 0; private int ySlotSpacing = 0; @@ -78,6 +82,7 @@ public GuiSlots(@NotNull GuiParent parent, ContainerScreenAccess screenAcc } updateSlots(parent.getModularGui().getRoot()); + setZStacking(false); } //=== Construction Helpers ===// @@ -126,7 +131,7 @@ public static PlayerWithArmor playerWithArmor(@NotNull GuiParent parent, Cont GuiSlots armor = new GuiSlots(container, screenAccess, armorSlots, 1) .setYSlotSpacing(groupSpacing / 3) - .setEmptyIcon(index -> slotIcons ? ARMOR_SLOTS[index] : null) + .setEmptyIconI(index -> slotIcons ? ARMOR_SLOTS[index] : null) .constrain(TOP, Constraint.midPoint(container.get(TOP), container.get(BOTTOM), height / -2D)) .constrain(LEFT, Constraint.midPoint(container.get(LEFT), container.get(RIGHT), width / -2D)); @@ -155,7 +160,7 @@ public static PlayerAll playerAllSlots(@NotNull GuiParent parent, ContainerSc GuiSlots armor = new GuiSlots(container, screenAccess, armorSlots, 1) .setYSlotSpacing(groupSpacing / 3) - .setEmptyIcon(index -> slotIcons ? ARMOR_SLOTS[index] : null) + .setEmptyIconI(index -> slotIcons ? ARMOR_SLOTS[index] : null) .constrain(TOP, Constraint.midPoint(container.get(TOP), container.get(BOTTOM), height / -2D)) .constrain(LEFT, Constraint.midPoint(container.get(LEFT), container.get(RIGHT), width / -2D)); @@ -168,7 +173,7 @@ public static PlayerAll playerAllSlots(@NotNull GuiParent parent, ContainerSc .constrain(LEFT, match(main.get(LEFT))); GuiSlots offHand = new GuiSlots(container, screenAccess, offhandSlots, 1) - .setEmptyIcon(index -> slotIcons ? OFF_HAND_SLOT : null) + .setEmptyIconI(index -> slotIcons ? OFF_HAND_SLOT : null) .constrain(TOP, match(bar.get(TOP))) .constrain(LEFT, relative(bar.get(RIGHT), groupSpacing)); @@ -181,10 +186,28 @@ public static PlayerAll playerAllSlots(@NotNull GuiParent parent, ContainerSc * Allows you to use a custom slot texture, The default is the standard vanilla slot. */ public GuiSlots setSlotTexture(Material slotTexture) { + this.slotTexture = e -> slotTexture; + return this; + } + + /** + * Allows you to use a custom per-slot slot textures, The default is the standard vanilla texture for all slots. + */ + public GuiSlots setSlotTexture(Function slotTexture) { this.slotTexture = slotTexture; return this; } + /** + * Allows you to use a custom per-slot slot textures, The default is the standard vanilla texture for all slots. + *

+ * Similar to {@link #setSlotTexture(Function)} except you are given the index of the slot within the {@link GuiSlots} element. + */ + public GuiSlots setSlotTextureI(Function slotTexture) { + this.slotTexture = slot -> slotTexture.apply(slots.indexOf(slot) - firstSlot); + return this; + } + /** * Sets a custom slot highlight colour (The highlight you get when your cursor is over a slot.) */ @@ -196,11 +219,22 @@ public GuiSlots setHighlightColour(int highlightColour) { * Allows you to set per-slot highlight colours, The integer passed to the function is the * index of the slot within the {@link SlotGroup} */ - public GuiSlots setHighlightColour(Function highlightColour) { + public GuiSlots setHighlightColour(Function highlightColour) { this.highlightColour = highlightColour; return this; } + /** + * Allows you to set per-slot highlight colours, The integer passed to the function is the + * index of the slot within the {@link SlotGroup} + *

+ * Similar to {@link #setHighlightColour(Function)} except you are given the index of the slot within the {@link GuiSlots} element. + */ + public GuiSlots setHighlightColourI(Function highlightColour) { + this.highlightColour = slot -> highlightColour.apply(slots.indexOf(slot) - firstSlot); + return this; + } + /** * Applies a single empty slot icon to all slots. * Recommended texture size is 16x16 @@ -215,11 +249,48 @@ public GuiSlots setEmptyIcon(Material texture) { * * @param slotIcons A function that is given the slot index within the {@link SlotGroup}, and should return a material or null. */ - public GuiSlots setEmptyIcon(Function slotIcons) { + public GuiSlots setEmptyIcon(Function slotIcons) { this.slotIcons = slotIcons; return this; } + /** + * Allows you to provide a texture to be rendered in each slot when the slot is empty. + * Recommended texture size is 16x16 + *

+ * Similar to {@link #setEmptyIcon(Function)} except you are given the index of the slot within the {@link GuiSlots} element. + * + * @param slotIcons A function that is given the slot index within the {@link SlotGroup}, and should return a material or null. + */ + public GuiSlots setEmptyIconI(Function slotIcons) { + this.slotIcons = slot -> slotIcons.apply(slots.indexOf(slot) - firstSlot); + return this; + } + + /** + * Allows you to attach an overlay renderer that will get called for each slot, after all slots have been rendered. + * This can be used to render pretty much anything you want to overtop the slot. + * + * @param slotOverlay Render callback providing the slot, screen position of the slot (top-left corner) and the active GuiRender. + */ + public GuiSlots setSlotOverlay(TriConsumer slotOverlay) { + this.slotOverlay = slotOverlay; + return this; + } + + /** + * Allows you to attach an overlay renderer that will get called for each slot, after all slots have been rendered. + * This can be used to render pretty much anything you want to overtop the slot. + *

+ * Similar to {@link #setSlotOverlay(TriConsumer)} except you are given the index of the slot within the {@link GuiSlots} element. + * + * @param slotOverlay Render callback providing the slot, screen position of the slot (top-left corner) and the active GuiRender. + */ + public GuiSlots setSlotOverlayI(TriConsumer slotOverlay) { + this.slotOverlay = (slot, position, render) -> slotOverlay.accept(slots.indexOf(slot) - firstSlot, position, render); + return this; + } + public GuiSlots setXSlotSpacing(int xSlotSpacing) { this.xSlotSpacing = xSlotSpacing; return this; @@ -275,7 +346,7 @@ public void renderBackground(GuiRender render, double mouseX, double mouseY, flo for (int index = 0; index < slotCount; index++) { Slot slot = slots.getSlot(index + firstSlot); - render.texRect(slotTexture, slot.x + root.xMin() - 1, slot.y + root.yMin() - 1, 18, 18); + render.texRect(slotTexture.apply(slot), slot.x + root.xMin() - 1, slot.y + root.yMin() - 1, 18, 18); } render.pose().translate(0, 0, 0.4); @@ -284,7 +355,7 @@ public void renderBackground(GuiRender render, double mouseX, double mouseY, flo Slot slot = slots.getSlot(index + firstSlot); if (!slot.isActive()) continue; if (!slot.hasItem()) { - Material icon = slotIcons.apply(index + firstSlot); + Material icon = slotIcons.apply(slot); if (icon != null) { render.texRect(icon, slot.x + root.xMin(), slot.y + root.yMin(), 16, 16); } @@ -296,17 +367,38 @@ public void renderBackground(GuiRender render, double mouseX, double mouseY, flo } } + render.pose().translate(0, 0, getBackgroundDepth() - 0.8); + + if (slotOverlay != null) { + for (int index = 0; index < slotCount; index++) { + Slot slot = slots.getSlot(index + firstSlot); + if (!slot.isActive()) continue; + slotOverlay.accept(slot, Position.create(slot.x + root.xMin(), slot.y + root.yMin()), render); + } + } + if (highlightSlot != null) { - render.pose().translate(0, 0, getBackgroundDepth() - 0.8); - render.rect(highlightSlot.x + root.xMin(), highlightSlot.y + root.yMin(), 16, 16, highlightColour.apply(slots.indexOf(highlightSlot))); + render.rect(highlightSlot.x + root.xMin(), highlightSlot.y + root.yMin(), 16, 16, highlightColour.apply(highlightSlot)); } render.pose().popPose(); } - public record Player(GuiElement container, GuiSlots main, GuiSlots hotBar) {} + public record Player(GuiElement container, GuiSlots main, GuiSlots hotBar) { + public FastStream stream() { + return FastStream.of(main, hotBar); + } + } - public record PlayerWithArmor(GuiElement container, GuiSlots main, GuiSlots hotBar, GuiSlots armor) {} + public record PlayerWithArmor(GuiElement container, GuiSlots main, GuiSlots hotBar, GuiSlots armor) { + public FastStream stream() { + return FastStream.of(main, hotBar, armor); + } + } - public record PlayerAll(GuiElement container, GuiSlots main, GuiSlots hotBar, GuiSlots armor, GuiSlots offHand) {} + public record PlayerAll(GuiElement container, GuiSlots main, GuiSlots hotBar, GuiSlots armor, GuiSlots offHand) { + public FastStream stream() { + return FastStream.of(main, hotBar, armor, offHand); + } + } }