diff --git a/src/main/java/com/cleanroommc/modularui/api/widget/IPositioned.java b/src/main/java/com/cleanroommc/modularui/api/widget/IPositioned.java index 51632ab21..622f49290 100644 --- a/src/main/java/com/cleanroommc/modularui/api/widget/IPositioned.java +++ b/src/main/java/com/cleanroommc/modularui/api/widget/IPositioned.java @@ -47,6 +47,21 @@ default W coverChildren() { return coverChildrenWidth().coverChildrenHeight(); } + /** + * Sets if this resizer is decoration. Decoration will be ignored during coverChildren and margin/padding calculations. + * + * @param decoration true if this resizer is decoration + * @return this + */ + default W decoration(boolean decoration) { + resizer().decoration(decoration); + return getThis(); + } + + default W decoration() { + return decoration(true); + } + default W expanded() { resizer().expanded(); return getThis(); @@ -353,6 +368,11 @@ default W posRel(float x, float y) { return getThis(); } + default W posRel(Alignment alignment) { + leftRel(alignment.x).topRel(alignment.y); + return getThis(); + } + default W size(int w, int h) { width(w).height(h); return getThis(); @@ -403,44 +423,59 @@ default W anchorBottom(float val) { return getThis(); } + /** + * @deprecated This will get removed due to this method being missused often. + */ + @ApiStatus.ScheduledForRemoval(inVersion = "3.3.0") + @Deprecated default W anchor(Alignment alignment) { resizer().anchor(alignment); return getThis(); } + @ApiStatus.ScheduledForRemoval(inVersion = "3.3.0") + @Deprecated default W alignX(float val) { leftRel(val).anchorLeft(val); return getThis(); } + @ApiStatus.ScheduledForRemoval(inVersion = "3.3.0") + @Deprecated default W alignX(Alignment alignment) { return alignX(alignment.x); } + @ApiStatus.ScheduledForRemoval(inVersion = "3.3.0") + @Deprecated default W alignY(float val) { topRel(val).anchorTop(val); return getThis(); } + @ApiStatus.ScheduledForRemoval(inVersion = "3.3.0") + @Deprecated default W alignY(Alignment alignment) { return alignY(alignment.y); } + @ApiStatus.ScheduledForRemoval(inVersion = "3.3.0") + @Deprecated default W align(Alignment alignment) { return alignX(alignment). alignY(alignment); } default W horizontalCenter() { - return alignX(Alignment.CENTER); + return leftRel(0.5f); } default W verticalCenter() { - return alignY(Alignment.CENTER); + return topRel(0.5f); } default W center() { - return align(Alignment.Center); + return horizontalCenter().verticalCenter(); } default W resizer(Consumer flexConsumer) { diff --git a/src/main/java/com/cleanroommc/modularui/drawable/AdaptableUITexture.java b/src/main/java/com/cleanroommc/modularui/drawable/AdaptableUITexture.java index 870926534..5ef6c73a1 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/AdaptableUITexture.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/AdaptableUITexture.java @@ -8,6 +8,8 @@ import com.google.gson.JsonObject; +import java.util.Objects; + /** * This class is a 9-slice texture. It can be created using * {@link UITexture.Builder#adaptable(int, int, int, int)}. @@ -32,6 +34,11 @@ public class AdaptableUITexture extends UITexture { this.tiled = tiled; } + @Override + public AdaptableUITexture register(String name) { + return (AdaptableUITexture) super.register(name); + } + @Override public AdaptableUITexture getSubArea(float uStart, float vStart, float uEnd, float vEnd) { return new AdaptableUITexture(this.location, lerpU(uStart), lerpV(vStart), lerpU(uEnd), lerpV(vEnd), this.colorType, this.nonOpaque, @@ -167,9 +174,8 @@ public void drawTiled(float x, float y, float width, float height) { } @Override - public boolean saveToJson(JsonObject json) { + protected void saveTextureToJson(JsonObject json) { super.saveToJson(json); - if (json.entrySet().size() == 1) return true; json.addProperty("imageWidth", this.imageWidth); json.addProperty("imageHeight", this.imageHeight); json.addProperty("bl", this.bl); @@ -177,7 +183,6 @@ public boolean saveToJson(JsonObject json) { json.addProperty("bt", this.bt); json.addProperty("bb", this.bb); json.addProperty("tiled", this.tiled); - return true; } @Override @@ -189,4 +194,19 @@ protected AdaptableUITexture copy() { public AdaptableUITexture withColorOverride(int color) { return (AdaptableUITexture) super.withColorOverride(color); } + + @Override + public boolean equals(Object o) { + return o != null && getClass() == o.getClass() && isEqual((AdaptableUITexture) o); + } + + protected boolean isEqual(AdaptableUITexture texture) { + return super.isEqual(texture) && imageWidth == texture.imageWidth && imageHeight == texture.imageHeight && + bl == texture.bl && bt == texture.bt && br == texture.br && bb == texture.bb && tiled == texture.tiled; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), imageWidth, imageHeight, bl, bt, br, bb, tiled); + } } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/ColorType.java b/src/main/java/com/cleanroommc/modularui/drawable/ColorType.java index 88870ebbf..67f54e9e6 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/ColorType.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/ColorType.java @@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import java.util.Map; +import java.util.Objects; import java.util.function.ToIntFunction; public class ColorType { @@ -35,4 +36,16 @@ public String getName() { public int getColor(WidgetTheme theme) { return colorGetter.applyAsInt(theme); } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ColorType colorType = (ColorType) o; + return Objects.equals(name, colorType.name); + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/DrawableSerialization.java b/src/main/java/com/cleanroommc/modularui/drawable/DrawableSerialization.java index ec29b304e..9af6b6d0a 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/DrawableSerialization.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/DrawableSerialization.java @@ -1,6 +1,7 @@ package com.cleanroommc.modularui.drawable; import com.cleanroommc.modularui.ModularUI; +import com.cleanroommc.modularui.ModularUIConfig; import com.cleanroommc.modularui.api.IJsonSerializable; import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.api.drawable.IKey; @@ -20,6 +21,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.lang.reflect.Type; import java.util.ArrayList; @@ -34,15 +36,59 @@ public class DrawableSerialization implements JsonSerializer, JsonDes private static final Map TEXTURES = new Object2ObjectOpenHashMap<>(); private static final Map REVERSE_TEXTURES = new Object2ObjectOpenHashMap<>(); - public static void registerTexture(String s, UITexture texture) { - TEXTURES.put(s, texture); - REVERSE_TEXTURES.put(texture, s); + private static void registerTextureInternal(String name, UITexture texture) { + if (texture == null) return; + UITexture current = TEXTURES.put(name, texture); + REVERSE_TEXTURES.put(texture, name); + if (current != null && (ModularUI.isDevEnv || ModularUIConfig.guiDebugMode)) { + ModularUI.LOGGER.warn("[DEBUG] Replacing texture with name '{}' and location '{}' with texture with location '{}'", name, current.location, texture.location); + } + } + + public static void registerTexture(String name, UITexture texture) { + String current = REVERSE_TEXTURES.get(texture); + if (current != null) { + if (name != null && !current.equals(name)) { + TEXTURES.put(name, texture); + REVERSE_TEXTURES.put(texture, name); + TEXTURES.remove(current); + } + return; + } + if (name == null) { + registerTextureAutoName(texture); + } else { + registerTextureInternal(name, texture); + } + } + + public static void registerTextureAutoName(UITexture texture) { + if (texture == null) return; + String[] p = texture.location.getResourcePath().split("/"); + p = p[p.length - 1].split("\\."); + String baseName = texture.location.getResourceDomain() + ":" + p[0]; + String name = baseName; + int number = 0; + UITexture current; + while ((current = TEXTURES.get(name)) != null) { + if (current.equals(texture)) return; + number++; + name = baseName + "_" + number; + if (number == 20 && (ModularUI.isDevEnv || ModularUIConfig.guiDebugMode)) { + ModularUI.LOGGER.warn("[DEBUG] Trying to register a UITexture with location '{}' for at least 20 times. This is likely a bug and should be fixed.", texture.location); + } else if (number == 10000) { + throw new IllegalStateException("Trying to register a UITexture with location '" + texture.location + "' 10000 times."); + } + } + registerTexture(name, texture); } + @Nullable public static UITexture getTexture(String s) { return TEXTURES.get(s); } + @Nullable public static String getTextureId(UITexture texture) { return REVERSE_TEXTURES.get(texture); } @@ -70,11 +116,11 @@ public static void init() { } public static IDrawable deserialize(JsonElement json) { - return JsonHelper.deserialize(json, IDrawable.class); + return JsonHelper.DESERIALIZER.deserialize(json, IDrawable.class); } public static JsonElement serialize(IDrawable drawable) { - return JsonHelper.serialize(drawable); + return JsonHelper.SERIALIZER.serialize(drawable, IDrawable.class); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/drawable/ItemDrawable.java b/src/main/java/com/cleanroommc/modularui/drawable/ItemDrawable.java index 48f8e16fe..fa7e26cea 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/ItemDrawable.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/ItemDrawable.java @@ -32,7 +32,7 @@ public class ItemDrawable implements IDrawable, IJsonSerializable { public ItemDrawable() {} - public ItemDrawable(@NotNull ItemStack item) { + public ItemDrawable(@Nullable ItemStack item) { setItem(item); } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/TiledUITexture.java b/src/main/java/com/cleanroommc/modularui/drawable/TiledUITexture.java index 84ebe7cd3..91b574c46 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/TiledUITexture.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/TiledUITexture.java @@ -4,6 +4,8 @@ import com.google.gson.JsonObject; +import java.util.Objects; + public class TiledUITexture extends UITexture { private final int imageWidth, imageHeight; @@ -18,6 +20,11 @@ public class TiledUITexture extends UITexture { this.imageHeight = imageHeight; } + @Override + public TiledUITexture register(String name) { + return (TiledUITexture) super.register(name); + } + @Override public void draw(float x, float y, float width, float height) { if (width == this.imageWidth && height == this.imageHeight) { @@ -28,12 +35,11 @@ public void draw(float x, float y, float width, float height) { } @Override - public boolean saveToJson(JsonObject json) { + protected void saveTextureToJson(JsonObject json) { super.saveToJson(json); - if (json.entrySet().size() > 1) { - json.addProperty("tiled", true); - } - return true; + json.addProperty("imageWidth", this.imageWidth); + json.addProperty("imageHeight", this.imageHeight); + json.addProperty("tiled", true); } @Override @@ -45,4 +51,18 @@ protected TiledUITexture copy() { public TiledUITexture withColorOverride(int color) { return (TiledUITexture) super.withColorOverride(color); } + + @Override + public boolean equals(Object o) { + return o != null && getClass() == o.getClass() && isEqual((TiledUITexture) o); + } + + protected boolean isEqual(TiledUITexture texture) { + return super.isEqual(texture) && imageWidth == texture.imageWidth && imageHeight == texture.imageHeight; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), imageWidth, imageHeight); + } } diff --git a/src/main/java/com/cleanroommc/modularui/drawable/UITexture.java b/src/main/java/com/cleanroommc/modularui/drawable/UITexture.java index f31b96f38..e9355f450 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/UITexture.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/UITexture.java @@ -19,6 +19,8 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; +import java.util.Objects; + public class UITexture implements IDrawable, IJsonSerializable { public static final UITexture DEFAULT = fullImage("gui/options_background", ColorType.DEFAULT); @@ -120,6 +122,11 @@ public static UITexture fullImage(String mod, String location, ColorType colorTy return fullImage(new ResourceLocation(mod, location), colorType); } + public UITexture register(String name) { + DrawableSerialization.registerTexture(name, this); + return this; + } + public UITexture getSubArea(Area bounds) { return getSubArea(bounds.x, bounds.y, bounds.ex(), bounds.ey()); } @@ -189,6 +196,8 @@ public static UITexture parseFromJson(JsonObject json) { if (name != null) { UITexture drawable = DrawableSerialization.getTexture(name); if (drawable != null) return drawable; + ModularUI.LOGGER.error("Tried to parse UITexture from json, but no texture with name '{}' is registered!", name); + return GuiTextures.HELP; } Builder builder = builder(); builder.location(JsonHelper.getString(json, ModularUI.ID + ":gui/widgets/error", "location")) @@ -237,6 +246,11 @@ public boolean saveToJson(JsonObject json) { json.addProperty("id", name); return true; } + saveTextureToJson(json); + return true; + } + + protected void saveTextureToJson(JsonObject json) { json.addProperty("location", this.location.toString()); json.addProperty("u0", this.u0); json.addProperty("v0", this.v0); @@ -244,7 +258,22 @@ public boolean saveToJson(JsonObject json) { json.addProperty("v1", this.v1); if (this.colorType != null) json.addProperty("colorType", this.colorType.getName()); json.addProperty("colorOverride", this.colorOverride); - return true; + } + + @Override + public boolean equals(Object o) { + return o != null && getClass() == o.getClass() && isEqual((UITexture) o); + } + + protected boolean isEqual(UITexture texture) { + return Objects.equals(location, texture.location) && Float.compare(u0, texture.u0) == 0 && Float.compare(v0, texture.v0) == 0 && + Float.compare(u1, texture.u1) == 0 && Float.compare(v1, texture.v1) == 0 && nonOpaque == texture.nonOpaque && + colorOverride == texture.colorOverride && Objects.equals(colorType, texture.colorType); + } + + @Override + public int hashCode() { + return Objects.hash(location, u0, v0, u1, v1, colorType, nonOpaque, colorOverride); } protected UITexture copy() { @@ -542,14 +571,6 @@ public Builder nonOpaque() { */ public UITexture build() { UITexture texture = create(); - if (this.name == null) { - String[] p = texture.location.getResourcePath().split("/"); - p = p[p.length - 1].split("\\."); - this.name = texture.location.getResourceDomain().equals(ModularUI.ID) ? p[0] : texture.location.getResourceDomain() + ":" + p[0]; - if (DrawableSerialization.getTexture(this.name) != null) { - return texture; - } - } DrawableSerialization.registerTexture(this.name, texture); return texture; } diff --git a/src/main/java/com/cleanroommc/modularui/screen/ModularContainer.java b/src/main/java/com/cleanroommc/modularui/screen/ModularContainer.java index ed9875da0..2a29216aa 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/ModularContainer.java +++ b/src/main/java/com/cleanroommc/modularui/screen/ModularContainer.java @@ -10,6 +10,7 @@ import com.cleanroommc.modularui.utils.item.SlotItemHandler; import com.cleanroommc.modularui.value.sync.ModularSyncManager; import com.cleanroommc.modularui.widgets.slot.ModularSlot; +import com.cleanroommc.modularui.widgets.slot.PlayerSlotGroup; import com.cleanroommc.modularui.widgets.slot.SlotGroup; import net.minecraft.client.gui.inventory.GuiContainer; @@ -358,6 +359,16 @@ public ItemStack slotClick(int slotId, int mouseButton, int mode, EntityPlayer p } detectAndSendChanges(); return returnable; + } else if (clickTypeIn == ClickType.SWAP && mouseButton >= 0 && mouseButton < 9) { + // minecraft does not check if the hotbar slot can actually take and put items + Slot hotbarSlot = findPlayerSlot(player, mouseButton); // mouseButton is the slot index here + if (hotbarSlot != null) { + Slot fromSlot = getSlot(slotId); + ItemStack fromItem = fromSlot.getStack(); + ItemStack hotbarStack = hotbarSlot.getStack(); + if (fromItem != null && !hotbarSlot.isItemValid(fromItem)) return null; + if (hotbarStack != null && !hotbarSlot.canTakeStack(player)) return null; + } } return superSlotClick(slotId, mouseButton, mode, player); @@ -381,6 +392,33 @@ public static int stackLimit(Slot slot, ItemStack stack) { return super.slotClick(slotId, mouseButton, mode, player); } + protected Slot findPlayerSlot(EntityPlayer player, int index) { + if (player == this.player || player.getEntityId() == this.player.getEntityId()) { + // if we want a slot of the player who opened the ui, we can just use the slot group + SlotGroup slotGroup = this.syncManager.getSlotGroup(PlayerSlotGroup.NAME); + if (slotGroup == null) return findExternalPlayerSlot(player, index); + for (Slot slot : slotGroup.getSlots()) { + if (slot.getSlotIndex() == index) { + return slot; + } + } + } + return findExternalPlayerSlot(player, index); + } + + protected Slot findExternalPlayerSlot(EntityPlayer player, int index) { + // go through all slots and find a slot with a matching player and index + for (Slot slot : this.inventorySlots) { + EntityPlayer slotPlayer = ModularSlot.getPlayerSlotPlayer(slot); + if (slotPlayer != null && + (player == slotPlayer || player.getEntityId() == slotPlayer.getEntityId()) && + slot.getSlotIndex() == index) { + return slot; + } + } + return null; + } + public final ItemStack handleQuickMove(EntityPlayer player, int slotId, Slot fromSlot) { // looping so that crafting works properly ItemStack returnable; diff --git a/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java b/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java index 9dbd43311..9cb50f1f8 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java +++ b/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java @@ -71,8 +71,8 @@ public static ModularPanel defaultPanel(@NotNull String name, int width, int hei private final String name; private ModularScreen screen; private IPanelHandler panelHandler; - private State state = State.IDLE; - private boolean cantDisposeNow = false; + private State state = State.CLOSED; + private boolean cantCloseNow = false; private final ObjectList hovering = ObjectList.create(); private final Input keyboard = new Input(); private final Input mouse = new Input(); @@ -134,7 +134,7 @@ public void setPanelSyncHandler(PanelSyncHandler syncHandler) { * @return true if this panel is currently open on a screen */ public boolean isOpen() { - return this.state == State.OPEN; + return this.state.open; } /** @@ -143,6 +143,14 @@ public boolean isOpen() { */ public void closeIfOpen() { if (!isOpen()) return; + if (this.cantCloseNow) { + if (!this.state.disposing) { + this.state = State.WAIT_CLOSING; + } else { + this.state = State.WAIT_CLOSING_AND_DISPOSING; + } + return; + } closeSubPanels(); if (isMainPanel()) { // close screen and let NEA handle animation @@ -266,12 +274,16 @@ public void onClose() { @MustBeInvokedByOverriders @Override public void dispose() { - if (this.state == State.DISPOSED) return; - if (this.state != State.CLOSED && this.state != State.WAIT_DISPOSING) { + if (this.state.disposing) return; + if (this.state.open && !this.state.closing) { throw new IllegalStateException("Panel must be closed before disposing!"); } - if (this.cantDisposeNow) { - this.state = State.WAIT_DISPOSING; + if (this.cantCloseNow) { + if (this.state.closing) { + this.state = State.WAIT_CLOSING_AND_DISPOSING; + } else { + this.state = State.WAIT_DISPOSING; + } return; } super.dispose(); @@ -292,12 +304,18 @@ public final T doSafe(Supplier runnable) { if (this.state == State.DISPOSED) return null; // make sure the screen is also not disposed return getScreen().getPanelManager().doSafe(() -> { - this.cantDisposeNow = true; + this.cantCloseNow = true; T t = runnable.get(); - this.cantDisposeNow = false; - if (this.state == State.WAIT_DISPOSING) { - this.state = State.CLOSED; - dispose(); + this.cantCloseNow = false; + if (this.state.open) { + if (this.state.closing) { + this.state = State.OPEN; + closeIfOpen(); + } + if (this.state.disposing) { + this.state = State.CLOSED; + dispose(); + } } return t; }); @@ -864,26 +882,39 @@ public State getState() { } public enum State { - /** - * Initial state of any panel. - */ - IDLE, + /** * State after the panel opened. */ - OPEN, + OPEN(true, false, false), /** * State after panel closed. Panel can still be reopened in this state. */ - CLOSED, + CLOSED(false, true, false), /** * State after panel disposed. The panel is now lost and has to be rebuilt, when reopening it. */ - DISPOSED, + DISPOSED(false, true, true), + /** + * Panel is open and is waiting to be closed. + */ + WAIT_CLOSING(true, true, false), /** * Panel is closed and is waiting to be disposed. */ - WAIT_DISPOSING + WAIT_DISPOSING(true, false, true), + /** + * Panel is open abd is waiting to close and then to be disposed. + */ + WAIT_CLOSING_AND_DISPOSING(true, true, true); + + public final boolean open, closing, disposing; + + State(boolean open, boolean closing, boolean disposing) { + this.open = open; + this.closing = closing; + this.disposing = disposing; + } } /** diff --git a/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java b/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java index 7e444c804..b13969ca8 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java +++ b/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java @@ -18,6 +18,7 @@ import com.cleanroommc.modularui.widget.sizer.Area; import com.cleanroommc.modularui.widget.sizer.ResizeNode; import com.cleanroommc.modularui.widget.sizer.ScreenResizeNode; +import com.cleanroommc.modularui.widgets.menu.MenuPanel; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; @@ -371,12 +372,21 @@ public final boolean onMouseInputPre(int button, boolean pressed) { * @return true if the action was consumed and further processing should be canceled */ public boolean onMousePressed(int mouseButton) { + // call all action listeners for (IGuiAction.MousePressed action : getGuiActionListeners(IGuiAction.MousePressed.class)) { action.press(mouseButton); } + // check if any context menu is open and close them if they or their children are not hovered + for (ModularPanel panel : this.panelManager.getOpenPanels()) { + if (panel instanceof MenuPanel menuPanel) { + menuPanel.closeAllMenus(false, true); + } + } + // handle dragging of draggable widgets if (this.context.onMousePressed(mouseButton)) { return true; } + // finally click hovered widgets for (ModularPanel panel : this.panelManager.getOpenPanels()) { if (panel.onMousePressed(mouseButton)) { return true; diff --git a/src/main/java/com/cleanroommc/modularui/screen/PanelManager.java b/src/main/java/com/cleanroommc/modularui/screen/PanelManager.java index 39b23055d..1a2aed17b 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/PanelManager.java +++ b/src/main/java/com/cleanroommc/modularui/screen/PanelManager.java @@ -196,7 +196,7 @@ public void openPanel(@NotNull ModularPanel panel, @NotNull IPanelHandler panelH openPanel(panel, true); } - public void closePanel(@NotNull ModularPanel panel) { + void closePanel(@NotNull ModularPanel panel) { if (!hasOpenPanel(panel)) { throw new IllegalArgumentException("Panel '" + panel.getName() + "' is open in this screen!"); } diff --git a/src/main/java/com/cleanroommc/modularui/test/ItemEditorGui.java b/src/main/java/com/cleanroommc/modularui/test/ItemEditorGui.java index b4d9e4068..c0672181b 100644 --- a/src/main/java/com/cleanroommc/modularui/test/ItemEditorGui.java +++ b/src/main/java/com/cleanroommc/modularui/test/ItemEditorGui.java @@ -13,8 +13,7 @@ import com.cleanroommc.modularui.value.sync.IntSyncValue; import com.cleanroommc.modularui.value.sync.PanelSyncManager; import com.cleanroommc.modularui.value.sync.StringSyncValue; -import com.cleanroommc.modularui.widgets.layout.Column; -import com.cleanroommc.modularui.widgets.layout.Row; +import com.cleanroommc.modularui.widgets.layout.Flow; import com.cleanroommc.modularui.widgets.slot.ItemSlot; import com.cleanroommc.modularui.widgets.slot.ModularSlot; import com.cleanroommc.modularui.widgets.textfield.TextFieldWidget; @@ -65,13 +64,13 @@ public ModularPanel buildUI(GuiData data, PanelSyncManager syncManager, UISettin } ModularPanel panel = ModularPanel.defaultPanel("item_editor"); return panel.bindPlayerInventory() - .child(new Column() + .child(Flow.col() .crossAxisAlignment(Alignment.CrossAxis.START) .sizeRel(1f) .margin(7) .child(IKey.str("Item Editor").asWidget().marginTop(7).marginBottom(3)) .child(new ItemSlot().slot(new ModularSlot(this.stackHandler, 0))) - .child(new Row() + .child(Flow.row() .crossAxisAlignment(Alignment.CrossAxis.CENTER) .height(16) .margin(0, 4) diff --git a/src/main/java/com/cleanroommc/modularui/test/TestEventHandler.java b/src/main/java/com/cleanroommc/modularui/test/TestEventHandler.java index 09584febc..2390d7755 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestEventHandler.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestEventHandler.java @@ -25,9 +25,12 @@ import com.cleanroommc.modularui.widgets.ButtonWidget; import com.cleanroommc.modularui.widgets.TextWidget; +import cpw.mods.fml.common.registry.GameData; import net.minecraft.client.gui.GuiMainMenu; import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.creativetab.CreativeTabs; import net.minecraft.init.Items; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import cpw.mods.fml.common.eventhandler.SubscribeEvent; @@ -37,6 +40,9 @@ import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; public class TestEventHandler { @@ -63,6 +69,18 @@ public void draw(GuiContext context, int x, int y, int width, int height, Widget } }.asIcon().height(3); + private static List allItems = null; + + public static ItemStack getRandomItem() { + if (allItems == null) { + allItems = new ArrayList<>(); + for (Item item : GameData.getItemRegistry().typeSafeIterable()) { + item.getSubItems(item, CreativeTabs.tabAllSearch, allItems); + } + } + return allItems.get(new Random().nextInt(allItems.size())).copy(); + } + @SideOnly(Side.CLIENT) @SubscribeEvent public void onItemUse(PlayerInteractEvent event) { diff --git a/src/main/java/com/cleanroommc/modularui/test/TestGui.java b/src/main/java/com/cleanroommc/modularui/test/TestGui.java index 45ef77308..11ab89fd8 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestGui.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestGui.java @@ -13,8 +13,8 @@ import com.cleanroommc.modularui.widgets.ButtonWidget; import com.cleanroommc.modularui.widgets.Dialog; import com.cleanroommc.modularui.widgets.SortableListWidget; +import com.cleanroommc.modularui.widgets.layout.Flow; import com.cleanroommc.modularui.widgets.layout.Grid; -import com.cleanroommc.modularui.widgets.layout.Row; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; @@ -55,7 +55,7 @@ public void onClose() { for (String line : this.lines) { items.put(line, new SortableListWidget.Item<>(line) .name("item_" + line) - .child(item -> new Row().name("row_" + line) + .child(item -> Flow.row().name("row_" + line) .child(new Widget<>() .addTooltipLine(line) .widgetTheme(IThemeApi.BUTTON) @@ -119,7 +119,7 @@ private static class AvailableElement extends ButtonWidget { private final IDrawable background = GuiTextures.SLOT_FLUID; @Override - public AvailableElement background(IDrawable... background) { + public AvailableElement backgroundOverlay(IDrawable... background) { throw new UnsupportedOperationException("Use overlay()"); } diff --git a/src/main/java/com/cleanroommc/modularui/test/TestGuis.java b/src/main/java/com/cleanroommc/modularui/test/TestGuis.java index 538751a97..f70d6d824 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestGuis.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestGuis.java @@ -36,24 +36,28 @@ import com.cleanroommc.modularui.utils.fakeworld.FakeEntity; import com.cleanroommc.modularui.utils.fakeworld.ISchema; import com.cleanroommc.modularui.value.BoolValue; +import com.cleanroommc.modularui.value.DoubleValue; import com.cleanroommc.modularui.value.IntValue; import com.cleanroommc.modularui.value.ObjectValue; import com.cleanroommc.modularui.value.StringValue; import com.cleanroommc.modularui.widget.DraggableWidget; +import com.cleanroommc.modularui.widget.ParentWidget; import com.cleanroommc.modularui.widget.Widget; import com.cleanroommc.modularui.widgets.ButtonWidget; import com.cleanroommc.modularui.widgets.ColorPickerDialog; +import com.cleanroommc.modularui.widgets.CycleButtonWidget; +import com.cleanroommc.modularui.widgets.ItemDisplayWidget; import com.cleanroommc.modularui.widgets.ListWidget; +import com.cleanroommc.modularui.widgets.ProgressWidget; import com.cleanroommc.modularui.widgets.RichTextWidget; import com.cleanroommc.modularui.widgets.SchemaWidget; import com.cleanroommc.modularui.widgets.ScrollingTextWidget; +import com.cleanroommc.modularui.widgets.SlotGroupWidget; import com.cleanroommc.modularui.widgets.TextWidget; import com.cleanroommc.modularui.widgets.ToggleButton; import com.cleanroommc.modularui.widgets.TransformWidget; -import com.cleanroommc.modularui.widgets.layout.Column; import com.cleanroommc.modularui.widgets.layout.Flow; import com.cleanroommc.modularui.widgets.layout.Grid; -import com.cleanroommc.modularui.widgets.layout.Row; import com.cleanroommc.modularui.widgets.menu.ContextMenuButton; import com.cleanroommc.modularui.widgets.menu.DropdownWidget; import com.cleanroommc.modularui.widgets.textfield.TextFieldWidget; @@ -92,6 +96,13 @@ public class TestGuis extends CustomModularScreen { .filter(c -> Color.getHSLSaturation(c) > 0.4f) .collect(IntArrayList::new, IntList::add, IntList::addAll); + public static final IntList DARK_COLORS = ColorShade.getAll().stream() + .filter(cs -> !cs.name.contains("accent")) + .filter(cs -> cs.darkerShadeCount() > 1) + .mapToInt(cs -> cs.darker(1)) + .filter(c -> Color.getHSLSaturation(c) > 0.4f) + .collect(IntArrayList::new, IntList::add, IntList::addAll); + public static boolean withCode = false; public TestGuis() { @@ -238,10 +249,10 @@ public void onInit() { Random rnd = new Random(); return new ModularPanel("main") .coverChildren() - .child(new Column() + .child(Flow.col() .margin(12) .coverChildren() - .child(new Row() + .child(Flow.row() .coverChildren() .child(IKey.str("Post ").asWidget() .transform((widget, stack) -> stack.translate(post.getValue(), 0))) @@ -279,7 +290,7 @@ public void onInit() { .child(new DraggableWidget<>() //.background(new SpriteDrawable(sprite)) .size(20) - .alignX(0.5f) + .horizontalCenter() .top(20) .tooltipBuilder(tooltip -> { tooltip.addLine( @@ -305,7 +316,7 @@ public void draw(GuiContext context, int x, int y, int width, int height, Widget GlStateManager.rotate(360 * (Minecraft.getSystemTime() % period) / period, 0, 1, 0); }, null); } - }.asWidget().alignX(0.5f).bottom(10).size(100, 75)); + }.asWidget().horizontalCenter().bottom(10).size(100, 75)); } public static @NotNull ModularPanel buildRichTextUI() { @@ -556,7 +567,7 @@ public static ModularPanel buildCollapseDisabledChildrenUI() { public static @NotNull ModularPanel buildViewportTransformUI() { return new TestPanel("viewport_transform") .child(new Widget<>() - .align(Alignment.Center) + .center() .size(50, 50) .background(GuiTextures.MC_BUTTON) .hoverBackground(GuiTextures.MC_BUTTON_HOVERED)); @@ -625,7 +636,7 @@ public static ModularPanel buildContextMenu() { return new ModularPanel("aspect_ratio") .coverChildren() .padding(10) - .child(new Row() + .child(Flow.row() .childPadding(10) .coverChildren() .child(new Rectangle().color(Color.BLUE_ACCENT.main) @@ -661,7 +672,7 @@ public static ModularPanel buildContextMenu() { .mainAxisAlignment(Alignment.MainAxis.CENTER) .childPadding(2) .crossAxisChildPadding(2) - .background(rndRect(colors, rnd)) + .background(new Rectangle().color(DARK_COLORS.getInt(rnd.nextInt(DARK_COLORS.size())))) .children(5, i -> { IWidget widget = rndRect(colors, rnd).asWidget() .width(rnd.nextInt(maxRectSize - minRectSize) + minRectSize) @@ -672,6 +683,60 @@ public static ModularPanel buildContextMenu() { })); } + public static @NotNull ModularPanel buildMachineLikeUI() { + return new ModularPanel("machine_like") + .coverChildren() + .padding(7) + .child(Flow.col() + .coverChildren() + .childPadding(8) + .child(Flow.row().name("machine_inventory") + .coverChildren() + .childPadding(8) + .child(SlotGroupWidget.builder() + .matrix("II", "II") + .key('I', i -> new ItemDisplayWidget().item(TestEventHandler.getRandomItem())) + .build() + .coverChildren()) + .child(new ProgressWidget() + .size(20) + .texture(GuiTextures.PROGRESS_ARROW, 20) + .value(new DoubleValue.Dynamic(() -> Minecraft.getSystemTime() % 5000 / 5000.0, null))) + .child(SlotGroupWidget.builder() + .matrix("II", "II") + .key('I', i -> new ItemDisplayWidget().item(TestEventHandler.getRandomItem())) + .build() + .coverChildren())) + .child(SlotGroupWidget.builder() + .matrix("IIIIII", "IIIIII") + .key('I', i -> new ItemDisplayWidget().item((ItemStack) null)) + .build() + .coverChildren() + .name("play_inventory_mimic"))) + .child(new ParentWidget<>() + .coverChildren() + .decoration() + .padding(3) + .background(GuiTextures.MC_BACKGROUND.getSubArea(0, 0, 1, 0.5f)) + .horizontalCenter() + .anchorTop(1) + .child(IKey.str("Machine Name").asWidget()) + .name("title")) + .child(new ParentWidget<>() + .coverChildren() + .decoration() + .padding(3) + .paddingLeft(1) + .background(GuiTextures.MC_BACKGROUND.getSubArea(0.5f, 0, 1, 1f)) + .bottom(7) + .rightRelAnchor(0, 1f) + .child(new CycleButtonWidget() + .value(new IntValue(0)) + .stateCount(3) + .stateOverlay(GuiTextures.CYCLE_BUTTON_DEMO)) + .name("side_options")); + } + private static Rectangle rndRect(IntList colors, Random random) { int i = random.nextInt(colors.size()); int c = colors.removeInt(i); @@ -684,7 +749,7 @@ private static class TestPanel extends ModularPanel { public TestPanel(String name) { super(name); //background(GuiTextures.BACKGROUND); - align(Alignment.Center).size(100, 100); + size(100, 100); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/test/TestItem.java b/src/main/java/com/cleanroommc/modularui/test/TestItem.java index f422f319f..838cb5e33 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestItem.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestItem.java @@ -9,7 +9,6 @@ import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.ModularScreen; import com.cleanroommc.modularui.screen.UISettings; -import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.utils.ISimpleBauble; import com.cleanroommc.modularui.utils.ItemStackItemHandler; import com.cleanroommc.modularui.utils.Platform; @@ -18,7 +17,7 @@ import com.cleanroommc.modularui.value.sync.SyncHandlers; import com.cleanroommc.modularui.widget.ParentWidget; import com.cleanroommc.modularui.widgets.SlotGroupWidget; -import com.cleanroommc.modularui.widgets.layout.Column; +import com.cleanroommc.modularui.widgets.layout.Flow; import com.cleanroommc.modularui.widgets.slot.ItemSlot; import com.cleanroommc.modularui.widgets.slot.ModularSlot; @@ -55,20 +54,19 @@ public ModularPanel buildUI(PlayerInventoryGuiData guiData, PanelSyncManager gui new ModularSlot(inv, index)); } ModularPanel panel = ModularPanel.defaultPanel("knapping_gui").resizeableOnDrag(true); - panel.child(new Column().margin(7) + panel.child(Flow.col().margin(7) .child(new ParentWidget<>().widthRel(1f).expanded() .child(SlotGroupWidget.builder() .row("II") .row("II") .key('I', index -> new ItemSlot().slot(SyncHandlers.itemSlot(itemHandler, index) - .ignoreMaxStackSize(true) - .slotGroup("mixer_items") + .ignoreMaxStackSize(true) + .slotGroup("mixer_items") // do not allow putting items which can hold other items into the item // some mods don't do this on their backpacks, so it won't catch those cases // TODO 1.7.10 //.filter(stack -> !stack.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null)) )) - .build() - .align(Alignment.Center))) + .build())) .child(SlotGroupWidget.playerInventory(false))); return panel; diff --git a/src/main/java/com/cleanroommc/modularui/test/TestTile.java b/src/main/java/com/cleanroommc/modularui/test/TestTile.java index 12f39a5e4..095c022d1 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestTile.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestTile.java @@ -26,6 +26,7 @@ import com.cleanroommc.modularui.value.sync.IntSyncValue; import com.cleanroommc.modularui.value.sync.ItemSlotSH; import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.value.sync.StringSyncValue; import com.cleanroommc.modularui.widget.EmptyWidget; import com.cleanroommc.modularui.widget.ParentWidget; import com.cleanroommc.modularui.widgets.ButtonWidget; @@ -39,15 +40,14 @@ import com.cleanroommc.modularui.widgets.ProgressWidget; import com.cleanroommc.modularui.widgets.SlotGroupWidget; import com.cleanroommc.modularui.widgets.ToggleButton; -import com.cleanroommc.modularui.widgets.layout.Column; import com.cleanroommc.modularui.widgets.layout.Flow; -import com.cleanroommc.modularui.widgets.layout.Row; import com.cleanroommc.modularui.widgets.slot.FluidSlot; import com.cleanroommc.modularui.widgets.slot.ItemSlot; import com.cleanroommc.modularui.widgets.slot.ModularCraftingSlot; import com.cleanroommc.modularui.widgets.slot.ModularSlot; import com.cleanroommc.modularui.widgets.slot.PhantomItemSlot; import com.cleanroommc.modularui.widgets.slot.SlotGroup; +import com.cleanroommc.modularui.widgets.textfield.TextFieldWidget; import net.minecraft.init.Blocks; import net.minecraft.init.Items; @@ -56,7 +56,6 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.PacketBuffer; import net.minecraft.tileentity.TileEntity; -import cpw.mods.fml.common.registry.GameData; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; @@ -64,7 +63,6 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Random; @@ -89,6 +87,7 @@ public class TestTile extends TileEntity implements IGuiHolder { private final int duration = 80; private int progress = 0; private int cycleState = 0; + private String s = ""; private List serverInts = new ArrayList<>(); private ItemStack displayItem = new ItemStack(Items.diamond); private final ItemStackHandler storage = new ItemStackHandler(9); @@ -110,13 +109,7 @@ public int getSlotLimit(int slot) { public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager syncManager, UISettings settings) { settings.customContainer(() -> new CraftingModularContainer(3, 3, this.craftingInventory)); settings.customGui(() -> TestGuiContainer::new); - - syncManager.addOpenListener(player -> { - ModularUI.LOGGER.info("Test Tile panel open by {} on {}", player.getGameProfile().getName(), Thread.currentThread().getName()); - }); - syncManager.addCloseListener(player -> { - ModularUI.LOGGER.info("Test Tile panel closed by {} on {}", player.getGameProfile().getName(), Thread.currentThread().getName()); - }); + syncManager.registerSlotGroup("item_inv", 3); IntSyncValue cycleStateValue = new IntSyncValue(() -> this.cycleState, val -> this.cycleState = val); syncManager.getHyperVisor().syncValue("cycle_state", cycleStateValue); @@ -151,7 +144,7 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager syncManager, UI DynamicLinkedSyncHandler> dynamicLinkedSyncHandler = new DynamicLinkedSyncHandler<>(numberListSyncHandler) .widgetProvider((syncManager1, value1) -> { List vals = value1.getValue(); - return new Row() + return Flow.row() .widthRel(1f) .coverChildrenHeight() .mainAxisAlignment(Alignment.MainAxis.SPACE_AROUND) @@ -163,11 +156,8 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager syncManager, UI IPanelHandler panelSyncHandler = syncManager.syncedPanel("other_panel", true, this::openSecondWindow); PagedWidget.Controller tabController = new PagedWidget.Controller(); - panel.resizer() // returns object which is responsible for sizing - .size(176, 210) // set a static size for the main panel - .align(Alignment.Center); // center the panel in the screen - panel - .child(new Row() + panel.size(176, 210) + .child(Flow.row() .name("Tab row") .coverChildren() .topRel(0f, 4, 1f) @@ -252,12 +242,21 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager syncManager, UI return true; }) .overlay(IKey.str("Open Sub Panel").scale(0.75f))) - .child(new Row() + .child(Flow.row() .name("cycle_button_row") .coverChildrenWidth().height(18) .reverseLayout(false) .child(new ToggleButton() .valueWrapped(cycleStateValue, 0) + .tooltipBuilder(false, t -> { + t.addLine("Wow! This button sure isnt selected! Not one bit!"); + if (this.oversizedStorage.getStackInSlot(this.cycleState) != null) { + t.addLine("Here is a cool item: "); + t.add(new ItemDrawable(this.oversizedStorage.getStackInSlot(this.cycleState))); + } + }) + .tooltipBuilder(true, t -> t.addLine("Wow! This button IS selected! How cool!")) + .tooltipAutoUpdate(true) .overlay(GuiTextures.CYCLE_BUTTON_DEMO.getSubArea(0, 0, 1, 1 / 3f))) .child(new ToggleButton() .valueWrapped(cycleStateValue, 1) @@ -265,7 +264,7 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager syncManager, UI .child(new ToggleButton() .valueWrapped(cycleStateValue, 2) .overlay(GuiTextures.CYCLE_BUTTON_DEMO.getSubArea(0, 2 / 3f, 1, 1)))) - .child(new Row() + .child(Flow.row() .name("progress_row") .height(18) .mainAxisAlignment(Alignment.MainAxis.SPACE_AROUND) @@ -307,7 +306,7 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager syncManager, UI .addPage(new ParentWidget<>() .name("dynamic_sync_page") .sizeRel(1f) - .child(new Column() + .child(Flow.col() .name("page 4 col, dynamic widgets") .child(IKey.str("Dynamic synced widget demo. Items act as keys to a unique storage with different amount of slots.").asWidget().scale(0.7f)) .child(new ItemSlot() @@ -345,6 +344,9 @@ public ModularPanel openSecondWindow(PanelSyncManager syncManager, IPanelHandler IPanelHandler panelSyncHandler = syncManager.syncedPanel("other_panel_2", true, (syncManager1, syncHandler1) -> openThirdWindow(syncManager1, syncHandler1, number)); IntSyncValue num = syncManager.getHyperVisor().findSyncHandler("cycle_state", IntSyncValue.class); + panel.child(new TextFieldWidget() + .size(60, 14).pos(10, 80) + .value(new StringSyncValue(() -> s, v -> s = v))); panel.child(ButtonWidget.panelCloseButton()) .child(new ButtonWidget<>() .size(10).top(14).right(4) @@ -390,10 +392,8 @@ public ModularPanel openThirdWindow(PanelSyncManager syncManager, IPanelHandler public void updateEntity() { if (!this.worldObj.isRemote) { if (this.time++ % 20 == 0) { - Collection vals = GameData.getItemRegistry().getKeys(); - String s = vals.stream().skip(new Random().nextInt(vals.size())).findFirst().orElse("minecraft:diamond"); - Item item = GameData.getItemRegistry().getObject(s); - this.displayItem = new ItemStack(item, 26735987); + this.displayItem = TestEventHandler.getRandomItem(); + this.displayItem.stackSize = 26735987; } if (++this.time % 60 == 0) { Random rnd = new Random(); diff --git a/src/main/java/com/cleanroommc/modularui/utils/JsonHelper.java b/src/main/java/com/cleanroommc/modularui/utils/JsonHelper.java index 536e408cb..051bb1c2b 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/JsonHelper.java +++ b/src/main/java/com/cleanroommc/modularui/utils/JsonHelper.java @@ -5,14 +5,17 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.google.gson.JsonSerializationContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.InputStream; import java.io.InputStreamReader; +import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.function.Consumer; @@ -20,20 +23,33 @@ public class JsonHelper { - public static final Gson gson = new GsonBuilder() + public static final Gson GSON = new GsonBuilder() .setPrettyPrinting() .registerTypeAdapter(IDrawable.class, new DrawableSerialization()) .registerTypeAdapter(Alignment.class, new Alignment.Json()) .create(); + public static final JsonDeserializationContext DESERIALIZER = GSON::fromJson; + public static final JsonSerializationContext SERIALIZER = new JsonSerializationContext() { + @Override + public JsonElement serialize(Object o) { + return GSON.toJsonTree(o); + } + + @Override + public JsonElement serialize(Object o, Type type) { + return GSON.toJsonTree(o, type); + } + }; + public static final JsonParser parser = new JsonParser(); public static JsonElement serialize(Object object) { - return gson.toJsonTree(object); + return GSON.toJsonTree(object); } public static T deserialize(JsonElement json, Class clazz) { - return gson.fromJson(json, clazz); + return GSON.fromJson(json, clazz); } public static T deserialize(JsonObject json, Class clazz, T defaultValue, String... keys) { diff --git a/src/main/java/com/cleanroommc/modularui/utils/TreeUtil.java b/src/main/java/com/cleanroommc/modularui/utils/TreeUtil.java index 1514c669a..1585fe3c3 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/TreeUtil.java +++ b/src/main/java/com/cleanroommc/modularui/utils/TreeUtil.java @@ -3,7 +3,6 @@ import com.cleanroommc.modularui.ModularUI; import com.cleanroommc.modularui.api.ITreeNode; import com.cleanroommc.modularui.api.widget.IWidget; -import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.widget.sizer.ResizeNode; import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; @@ -289,6 +288,7 @@ public static , R extends ITreeNode> List flatListB * @param test test which the widget has to pass * @return the first matching widget */ + @Nullable public static > T findFirst(T parent, @NotNull Predicate test) { return foreachChildWithResult(parent, w -> { if (test.test(w)) { @@ -306,6 +306,7 @@ public static > T findFirst(T parent, @NotNull Predicate< * @param test test which the widget has to pass * @return the first matching widget */ + @Nullable @SuppressWarnings("unchecked") public static , R extends ITreeNode> R findFirst(T parent, Class type, @Nullable Predicate test) { return foreachChildWithResult(parent, t -> { @@ -318,31 +319,32 @@ public static , R extends ITreeNode> R findFirst(T par }, true); } + @Nullable public static > T findParent(T parent, Predicate filter) { - if (parent == null) return null; - while (!(parent instanceof ModularPanel)) { + while (parent != null) { if (filter.test(parent)) { return parent; } parent = parent.getParent(); } - return filter.test(parent) ? parent : null; + return null; } + @Nullable public static , R extends ITreeNode> R findParent(T parent, Class type) { return findParent(parent, type, null); } + @Nullable @SuppressWarnings("unchecked") public static , R extends ITreeNode> R findParent(T parent, Class type, @Nullable Predicate test) { - if (parent == null) return null; - while (!(parent instanceof ModularPanel)) { + while (parent != null) { if (type.isAssignableFrom(parent.getClass()) && (test == null || test.test((R) parent))) { return (R) parent; } parent = parent.getParent(); } - return type.isAssignableFrom(parent.getClass()) && (test == null || test.test((R) parent)) ? (R) parent : null; + return null; } /** diff --git a/src/main/java/com/cleanroommc/modularui/utils/item/CombinedInvWrapper.java b/src/main/java/com/cleanroommc/modularui/utils/item/CombinedInvWrapper.java index e69c101b4..725cc4355 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/item/CombinedInvWrapper.java +++ b/src/main/java/com/cleanroommc/modularui/utils/item/CombinedInvWrapper.java @@ -1,10 +1,15 @@ package com.cleanroommc.modularui.utils.item; +import com.google.common.collect.Iterators; + import net.minecraft.item.ItemStack; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class CombinedInvWrapper implements IItemHandlerModifiable { +import java.util.Iterator; + +public class CombinedInvWrapper implements IItemHandlerModifiable, Iterable { protected final IItemHandlerModifiable[] itemHandler; // the handlers protected final int[] baseIndex; // index-offsets of the different handlers @@ -104,4 +109,9 @@ public boolean isItemValid(int slot, @Nullable ItemStack stack) { int localSlot = getSlotFromIndex(slot, index); return handler.isItemValid(localSlot, stack); } + + @Override + public @NotNull Iterator iterator() { + return Iterators.forArray(this.itemHandler); + } } diff --git a/src/main/java/com/cleanroommc/modularui/value/sync/DynamicLinkedSyncHandler.java b/src/main/java/com/cleanroommc/modularui/value/sync/DynamicLinkedSyncHandler.java index b7cfdc86c..ae33da177 100644 --- a/src/main/java/com/cleanroommc/modularui/value/sync/DynamicLinkedSyncHandler.java +++ b/src/main/java/com/cleanroommc/modularui/value/sync/DynamicLinkedSyncHandler.java @@ -24,7 +24,6 @@ public class DynamicLinkedSyncHandler> extends Syn private IWidgetProvider widgetProvider; private Consumer onWidgetUpdate; - private boolean updateQueued; private IWidget lastRejectedWidget; private final S linkedValue; @@ -52,10 +51,7 @@ public void readOnServer(int id, PacketBuffer buf) throws IOException { @Override public void init(String key, PanelSyncManager syncManager) { super.init(key, syncManager); - if (this.updateQueued) { - notifyUpdate(true); - this.updateQueued = false; - } + notifyUpdate(false); } private IWidget parseWidget() { @@ -64,7 +60,7 @@ private IWidget parseWidget() { getSyncManager().allowTemporarySyncHandlerRegistration(false); // collects any unregistered sync handlers // since the sync manager is currently locked and we no longer allow bypassing the lock it will crash if it finds any - int unregistered = WidgetTree.countUnregisteredSyncHandlers(widget); + int unregistered = WidgetTree.countUnregisteredSyncHandlers(getSyncManager(), widget); if (unregistered > 0) { throw new IllegalStateException("Widgets created by DynamicSyncHandler can't have implicitly registered sync handlers. All" + "sync handlers must be registered with a variant of 'PanelSyncManager#getOrCreateSyncHandler(...)'."); @@ -89,11 +85,7 @@ private void updateWidget(IWidget widget) { * initialised is effective. */ private void notifyUpdate(boolean sync) { - if (!isValid()) { - // sync handler not yet initialised - this.updateQueued = true; - return; - } + if (!isValid()) return; IWidget widget = parseWidget(); if (getSyncManager().isClient()) { updateWidget(widget); diff --git a/src/main/java/com/cleanroommc/modularui/value/sync/DynamicSyncHandler.java b/src/main/java/com/cleanroommc/modularui/value/sync/DynamicSyncHandler.java index ce963e69e..25e62acf8 100644 --- a/src/main/java/com/cleanroommc/modularui/value/sync/DynamicSyncHandler.java +++ b/src/main/java/com/cleanroommc/modularui/value/sync/DynamicSyncHandler.java @@ -60,7 +60,7 @@ private IWidget parseWidget(PacketBuffer buf) { getSyncManager().allowTemporarySyncHandlerRegistration(false); // collects any unregistered sync handlers // since the sync manager is currently locked and we no longer allow bypassing the lock it will crash if it finds any - int unregistered = WidgetTree.countUnregisteredSyncHandlers(widget); + int unregistered = WidgetTree.countUnregisteredSyncHandlers(getSyncManager(), widget); if (unregistered > 0) { throw new IllegalStateException("Widgets created by DynamicSyncHandler can't have implicitly registered sync handlers. All" + "sync handlers must be registered with a variant of 'PanelSyncManager#getOrCreateSyncHandler(...)'."); diff --git a/src/main/java/com/cleanroommc/modularui/value/sync/ISyncRegistrar.java b/src/main/java/com/cleanroommc/modularui/value/sync/ISyncRegistrar.java index 00eaa6e99..961341990 100644 --- a/src/main/java/com/cleanroommc/modularui/value/sync/ISyncRegistrar.java +++ b/src/main/java/com/cleanroommc/modularui/value/sync/ISyncRegistrar.java @@ -119,6 +119,18 @@ default T getOrCreateSyncHandler(String name, Class c T getOrCreateSyncHandler(String name, int id, Class clazz, Supplier supplier); + default T getOrCreateSH(String name, Class clazz, Supplier supplier) { + return getOrCreateSyncHandler(name, 0, clazz, supplier); + } + + default T getOrCreateSH(String name, int id, Class clazz, Supplier supplier) { + return getOrCreateSyncHandler(name, id, clazz, supplier); + } + + default ItemSlotSH getOrCreateSlot(String name, Supplier slotSupplier) { + return getOrCreateSlot(name, 0, slotSupplier); + } + default ItemSlotSH getOrCreateSlot(String name, int id, Supplier slotSupplier) { return getOrCreateSyncHandler(name, id, ItemSlotSH.class, () -> new ItemSlotSH(slotSupplier.get())); } diff --git a/src/main/java/com/cleanroommc/modularui/value/sync/PanelSyncManager.java b/src/main/java/com/cleanroommc/modularui/value/sync/PanelSyncManager.java index 7d1cdb1fd..7e1243301 100644 --- a/src/main/java/com/cleanroommc/modularui/value/sync/PanelSyncManager.java +++ b/src/main/java/com/cleanroommc/modularui/value/sync/PanelSyncManager.java @@ -166,7 +166,8 @@ public void setCursorItem(ItemStack stack) { @Override public boolean hasSyncHandler(SyncHandler syncHandler) { - return syncHandler.isValid() && syncHandler.getSyncManager() == this && this.reverseSyncHandlers.containsKey(syncHandler); + if (this.reverseSyncHandlers.containsKey(syncHandler)) return true; + return this != getHyperVisor() && getHyperVisor().hasSyncHandler(syncHandler); } private void putSyncValue(String name, int id, SyncHandler syncHandler) { diff --git a/src/main/java/com/cleanroommc/modularui/value/sync/ShortSyncValue.java b/src/main/java/com/cleanroommc/modularui/value/sync/ShortSyncValue.java index f67f857a1..341e4695c 100644 --- a/src/main/java/com/cleanroommc/modularui/value/sync/ShortSyncValue.java +++ b/src/main/java/com/cleanroommc/modularui/value/sync/ShortSyncValue.java @@ -4,7 +4,6 @@ import com.cleanroommc.modularui.api.value.sync.IShortSyncValue; import com.cleanroommc.modularui.api.value.sync.IStringSyncValue; import com.cleanroommc.modularui.network.NetworkUtils; - import com.cleanroommc.modularui.value.ShortValue; import net.minecraft.network.PacketBuffer; diff --git a/src/main/java/com/cleanroommc/modularui/value/sync/SyncHandler.java b/src/main/java/com/cleanroommc/modularui/value/sync/SyncHandler.java index 943b725e3..b58c6e891 100644 --- a/src/main/java/com/cleanroommc/modularui/value/sync/SyncHandler.java +++ b/src/main/java/com/cleanroommc/modularui/value/sync/SyncHandler.java @@ -173,8 +173,11 @@ public PanelSyncManager getSyncManager() { return this.syncManager; } - public final boolean isRegistered() { - return isValid() && this.syncManager.hasSyncHandler(this); + public final boolean isRegistered(PanelSyncManager syncManager) { + if (isValid() && this.syncManager.hasSyncHandler(this)) { + return true; + } + return syncManager.hasSyncHandler(this); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/widget/AbstractScrollWidget.java b/src/main/java/com/cleanroommc/modularui/widget/AbstractScrollWidget.java index 23d5edf5c..32effe439 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/AbstractScrollWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widget/AbstractScrollWidget.java @@ -31,6 +31,8 @@ public abstract class AbstractScrollWidget scrollbarTheme = getPanel().getTheme().getScrollbarTheme(); this.scroll.drawScrollbar(context, scrollbarTheme.getTheme(isHovering()), scrollbarTheme.getTheme().getBackground()); + if (this.showScrollShadows) this.scroll.drawScrollShadow(context); } } @@ -149,4 +152,13 @@ public int getScrollX() { public int getScrollY() { return this.scroll.getScrollY() != null ? this.scroll.getScrollY().getScroll() : 0; } + + public boolean isShowScrollShadows() { + return showScrollShadows; + } + + public W showScrollShadows(boolean showScrollShadows) { + this.showScrollShadows = showScrollShadows; + return getThis(); + } } diff --git a/src/main/java/com/cleanroommc/modularui/widget/Widget.java b/src/main/java/com/cleanroommc/modularui/widget/Widget.java index 60249b7f7..7b4d5b52e 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/Widget.java +++ b/src/main/java/com/cleanroommc/modularui/widget/Widget.java @@ -55,6 +55,8 @@ public class Widget> extends AbstractWidget implements IPosi @Nullable private String syncKey; @Nullable private SyncHandler syncHandler; // rendering + private boolean disableThemeBackground = false; + private boolean disableHoverThemeBackground = false; @Nullable private IDrawable shadow = null; @Nullable private IDrawable background = null; @Nullable private IDrawable overlay = null; @@ -147,12 +149,19 @@ public void dispose() { */ @Override public void drawBackground(ModularGuiContext context, WidgetThemeEntry widgetTheme) { + WidgetTheme theme = getActiveWidgetTheme(widgetTheme, isHovering()); if (this.shadow != null) { - this.shadow.drawAtZero(context, getArea().width, getArea().height, getActiveWidgetTheme(widgetTheme, isHovering())); + this.shadow.drawAtZero(context, getArea(), theme); } - IDrawable bg = getCurrentBackground(getPanel().getTheme(), widgetTheme); + if (!this.disableThemeBackground || !this.disableHoverThemeBackground) { + IDrawable bg = getThemeBackground(widgetTheme, theme); + if (bg != null) { + bg.drawAtZero(context, getArea(), theme); + } + } + IDrawable bg = getCurrentBackground(widgetTheme); if (bg != null) { - bg.drawAtZero(context, getArea().width, getArea().height, getActiveWidgetTheme(widgetTheme, isHovering())); + bg.drawAtZero(context, getArea(), theme); } } @@ -178,7 +187,7 @@ public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) {} */ @Override public void drawOverlay(ModularGuiContext context, WidgetThemeEntry widgetTheme) { - IDrawable bg = getCurrentOverlay(getPanel().getTheme(), widgetTheme); + IDrawable bg = getCurrentOverlay(widgetTheme); if (bg != null) { bg.drawAtZeroPadded(context, getArea(), getActiveWidgetTheme(widgetTheme, isHovering())); } @@ -198,6 +207,18 @@ public void drawForeground(ModularGuiContext context) { } } + public boolean isDisableThemeBackground() { + return disableThemeBackground; + } + + public boolean isDisableHoverThemeBackground() { + return disableHoverThemeBackground; + } + + public @Nullable IDrawable getShadow() { + return shadow; + } + /** * The current set background. This is not an accurate representation of what is actually being displayed currently. * Usually background is handled by the theme, which is when this is null. @@ -237,33 +258,49 @@ public void drawForeground(ModularGuiContext context) { return this.hoverOverlay; } + public @Nullable IDrawable getThemeBackground(WidgetThemeEntry widgetTheme) { + return getThemeBackground(widgetTheme, getActiveWidgetTheme(widgetTheme, isHovering())); + } + + public @Nullable IDrawable getThemeBackground(WidgetThemeEntry widgetTheme, WidgetTheme activeTheme) { + IDrawable bg = null; + if (isHovering()) { + bg = activeTheme.getBackground(); + if (bg == null || bg == IDrawable.NONE || this.disableHoverThemeBackground) { + bg = getActiveWidgetTheme(widgetTheme, false).getBackground(); + } + } else if (!this.disableThemeBackground) { + bg = activeTheme.getBackground(); + } + return bg; + } + /** * Returns the actual currently displayed background. * - * @param theme current theme * @param widgetTheme widget theme which is used by this widget * @return currently displayed background */ - public @Nullable IDrawable getCurrentBackground(ITheme theme, WidgetThemeEntry widgetTheme) { + public @Nullable IDrawable getCurrentBackground(WidgetThemeEntry widgetTheme) { if (isHovering()) { IDrawable hoverBackground = getHoverBackground(); - if (hoverBackground == null) hoverBackground = getActiveWidgetTheme(widgetTheme, true).getBackground(); if (hoverBackground != null && hoverBackground != IDrawable.NONE) return hoverBackground; } - IDrawable background = getBackground(); - return background == null ? getActiveWidgetTheme(widgetTheme, false).getBackground() : background; + return getBackground(); } /** * Returns the actual currently displayed overlay. * - * @param theme current theme * @param widgetTheme widget theme which is used by this widget * @return currently displayed background */ - public @Nullable IDrawable getCurrentOverlay(ITheme theme, WidgetThemeEntry widgetTheme) { - IDrawable hoverBackground = getHoverOverlay(); - return hoverBackground != null && hoverBackground != IDrawable.NONE && isHovering() ? hoverBackground : getOverlay(); + public @Nullable IDrawable getCurrentOverlay(WidgetThemeEntry widgetTheme) { + if (isHovering()) { + IDrawable hoverBackground = getHoverOverlay(); + if (hoverBackground != null && hoverBackground != IDrawable.NONE) return hoverBackground; + } + return getOverlay(); } /** @@ -359,18 +396,44 @@ public final WidgetThemeEntry getWidgetTheme(ITheme t } /** - * Sets a background override. Ideally this is set in the used theme. Also consider using {@link #overlay(IDrawable...)} instead. - * Using {@link IDrawable#EMPTY} will make the background invisible while still overriding the widget theme. - * Background are drawn before the widget and overlays are drawn. + * Sets a shadow. Shadows are drawn before the background and don't actually have to be shadows. This doesn't effect theme backgrounds + * and doesn't change when hovered. + * + * @param shadow background to use. + * @return this + */ + public W shadow(IDrawable... shadow) { + this.shadow = IDrawable.of(shadow); + return getThis(); + } + + /** + * Sets a background. The theme background will be drawn before this override. + *

+ * NOTE: This will NOT disable the theme background. Disable it separately with {@link #disableThemeBackground(boolean)}. + *

+ * This method is meant for unique textures. For generic backgrounds please use themes. Also consider using + * {@link #overlay(IDrawable...)} instead. Using {@link IDrawable#EMPTY} will make the background invisible while still overriding + * the widget theme. Background are drawn before the widget and overlays are drawn. * * @param background background to use. * @return this */ - public W background(IDrawable... background) { + public W backgroundOverlay(IDrawable... background) { this.background = IDrawable.of(background); return getThis(); } + /** + * Sets a background and disables the theme background. See {@link #backgroundOverlay(IDrawable...)} for more information. + * + * @param background background + * @return this + */ + public W background(IDrawable... background) { + return backgroundOverlay(background).disableThemeBackground(true); + } + /** * Sets an overlay. Does not interfere with themes. Overlays are drawn after the widget and backgrounds. * @@ -383,26 +446,42 @@ public W overlay(IDrawable... overlay) { } /** - * Sets a hover background override. Ideally this is set in the used theme. Also consider using {@link #hoverOverlay(IDrawable...)} instead. - * Using {@link IDrawable#EMPTY} will make the background invisible while still overriding the widget theme. - * Background are drawn before the widget and overlays are drawn. + * Sets a hover background. The hover theme background will be drawn before this override. + *

+ * NOTE: This will NOT disable the hover theme background. Disable it separately with {@link #disableHoverThemeBackground(boolean)}. + *

+ *

+ * This method is meant for unique textures. For generic backgrounds please use themes. Also consider using + * {@link #hoverOverlay(IDrawable...)} instead. Using {@link IDrawable#EMPTY} will make the background invisible while still + * overriding the widget theme. Background are drawn before the widget and overlays are drawn. *

* Following argument special cases should be considered: *

    *
  • {@code null} will fallback to {@link WidgetThemeEntry#getHoverTheme()}
  • *
  • {@link IDrawable#EMPTY} will make the hover background invisible
  • - *
  • {@link IDrawable#NONE} will use the normal background instead (which is also achieved using {@link #disableHoverBackground()})
  • + *
  • {@link IDrawable#NONE} will use the normal background instead (which is also achieved using {@link #disableHoverBackground()}) + * (note that this won't disable the hover theme background)
  • *
  • multiple drawables, will result in them being drawn on top of each other in the order they are passed to the method
  • *
* * @param background hover background to use. * @return this */ - public W hoverBackground(IDrawable... background) { + public W hoverBackgroundOverlay(IDrawable... background) { this.hoverBackground = IDrawable.of(background); return getThis(); } + /** + * Sets a background and disables the theme background. See {@link #backgroundOverlay(IDrawable...)} for more information. + * + * @param background background + * @return this + */ + public W hoverBackground(IDrawable... background) { + return hoverBackgroundOverlay(background).disableHoverThemeBackground(true); + } + /** * Sets a hover overlay. * Using {@link IDrawable#EMPTY} will make the background invisible while still overriding the widget theme. @@ -411,7 +490,7 @@ public W hoverBackground(IDrawable... background) { * Following argument special cases should be considered: *
    *
  • {@link IDrawable#EMPTY} will make the hover overlay invisible
  • - *
  • {@code null} and {@link IDrawable#NONE} will use the normal overlay instead (which is also achieved using {@link #disableHoverOverlay()} ()})
  • + *
  • {@code null} and {@link IDrawable#NONE} will use the normal overlay instead (which is also achieved using {@link #disableHoverOverlay()})
  • *
  • multiple drawables, will result in them being drawn on top of each other in the order they are passed to the method
  • *
* @@ -423,13 +502,23 @@ public W hoverOverlay(IDrawable... overlay) { return getThis(); } + public W disableThemeBackground(boolean b) { + this.disableThemeBackground = b; + return getThis(); + } + + public W disableHoverThemeBackground(boolean b) { + this.disableHoverThemeBackground = b; + return getThis(); + } + /** - * Forces the hover background to use the normal background instead. + * Forces the hover background to use the normal background instead. This also diables the hover theme background. * * @return this */ public W disableHoverBackground() { - return hoverBackground(IDrawable.NONE); + return hoverBackgroundOverlay(IDrawable.NONE).disableHoverThemeBackground(true); } /** @@ -467,7 +556,7 @@ public W widgetTheme(WidgetThemeKey s) { } public W invisible() { - return background(IDrawable.EMPTY) + return disableThemeBackground(true) .disableHoverBackground(); } diff --git a/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java b/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java index fe07e400b..55ab8f68a 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java +++ b/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java @@ -195,7 +195,7 @@ public static void collectSyncValues(PanelSyncManager syncManager, String panelN String syncKey = ModularSyncManager.AUTO_SYNC_PREFIX + panelName; foreachChildBFS(panel, widget -> { if (widget instanceof ISynced synced) { - if (synced.isSynced() && !synced.getSyncHandler().isRegistered()) { + if (synced.isSynced() && !synced.getSyncHandler().isRegistered(syncManager)) { syncManager.syncValue(syncKey, id.intValue(), synced.getSyncHandler()); id.increment(); } @@ -204,10 +204,10 @@ public static void collectSyncValues(PanelSyncManager syncManager, String panelN }, includePanel); } - public static int countUnregisteredSyncHandlers(IWidget parent) { + public static int countUnregisteredSyncHandlers(PanelSyncManager syncManager, IWidget parent) { MutableInt count = new MutableInt(); foreachChildBFS(parent, widget -> { - if (widget instanceof ISynced synced && synced.isSynced() && !synced.getSyncHandler().isRegistered()) { + if (widget instanceof ISynced synced && synced.isSynced() && !synced.getSyncHandler().isRegistered(syncManager)) { count.increment(); } return true; diff --git a/src/main/java/com/cleanroommc/modularui/widget/scroll/HorizontalScrollData.java b/src/main/java/com/cleanroommc/modularui/widget/scroll/HorizontalScrollData.java index 60ccefd99..c17dd4216 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/scroll/HorizontalScrollData.java +++ b/src/main/java/com/cleanroommc/modularui/widget/scroll/HorizontalScrollData.java @@ -5,6 +5,8 @@ import com.cleanroommc.modularui.drawable.GuiDraw; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.utils.MathUtils; public class HorizontalScrollData extends ScrollData { @@ -86,4 +88,33 @@ public void drawScrollbar(ScrollArea area, ModularGuiContext context, WidgetThem w = l; drawScrollBar(context, x, y, w, h, widgetTheme, texture); } + + @Override + public void drawScrollShadow(ScrollArea area, ModularGuiContext context) { + int min = 0, max = getScrollSize() - getFullVisibleSize(area); + int s = getScroll(); + final float maxOpacityScroll = 30f; + final int maxShadowSize = 12; + + int endColor = 0; + int startColorFull = Color.BLACK.brighter(4); + + ScrollPadding sp = area.getScrollPadding(); + int y = sp.getScrollPaddingTop(); + int h = area.height - sp.verticalScrollPadding(); + final int maxShadowSizeLimit = (area.w() - sp.horizontalScrollPadding()) / 3; + + if (s > min) { + float prog = MathUtils.clamp(s / maxOpacityScroll, 0, 1); + int startColor = Color.withAlpha(startColorFull, prog * 0.8f); + int size = Math.min((int) (prog * maxShadowSize), maxShadowSizeLimit); + GuiDraw.drawHorizontalGradientRect(sp.getScrollPaddingLeft(), y, size, h, startColor, endColor); + } + if (s < max) { + float prog = MathUtils.clamp((max - s) / maxOpacityScroll, 0, 1); + int startColor = Color.withAlpha(startColorFull, prog * 0.8f); + int size = Math.min((int) (prog * maxShadowSize), maxShadowSizeLimit); + GuiDraw.drawHorizontalGradientRect(area.w() - size - sp.getScrollPaddingRight(), y, size, h, endColor, startColor); + } + } } diff --git a/src/main/java/com/cleanroommc/modularui/widget/scroll/ScrollArea.java b/src/main/java/com/cleanroommc/modularui/widget/scroll/ScrollArea.java index aab345a25..773645257 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/scroll/ScrollArea.java +++ b/src/main/java/com/cleanroommc/modularui/widget/scroll/ScrollArea.java @@ -227,4 +227,16 @@ public void drawScrollbar(ModularGuiContext context, WidgetTheme widgetTheme, ID this.scrollY.drawScrollbar(this, context, widgetTheme, texture); } } + + @SideOnly(Side.CLIENT) + public void drawScrollShadow(ModularGuiContext context) { + boolean isXActive = false; // micro optimisation + if (this.scrollX != null && this.scrollX.isScrollBarActive(this, false)) { + isXActive = true; + this.scrollX.drawScrollShadow(this, context); + } + if (this.scrollY != null && this.scrollY.isScrollBarActive(this, isXActive)) { + this.scrollY.drawScrollShadow(this, context); + } + } } diff --git a/src/main/java/com/cleanroommc/modularui/widget/scroll/ScrollData.java b/src/main/java/com/cleanroommc/modularui/widget/scroll/ScrollData.java index 5e69369aa..9ab7600c7 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/scroll/ScrollData.java +++ b/src/main/java/com/cleanroommc/modularui/widget/scroll/ScrollData.java @@ -298,6 +298,9 @@ protected void drawScrollBar(ModularGuiContext context, int x, int y, int w, int drawable.draw(context, x, y, w, h, widgetTheme); } + @SideOnly(Side.CLIENT) + public abstract void drawScrollShadow(ScrollArea area, ModularGuiContext context); + public boolean onMouseClicked(ScrollArea area, int mainAxisPos, int crossAxisPos, int button) { if (isOnAxisStart() ? crossAxisPos <= getThickness() : crossAxisPos >= area.getSize(this.axis.getOther()) - getThickness()) { this.dragging = true; diff --git a/src/main/java/com/cleanroommc/modularui/widget/scroll/ScrollPadding.java b/src/main/java/com/cleanroommc/modularui/widget/scroll/ScrollPadding.java index 2becc44a9..32bcc49b8 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/scroll/ScrollPadding.java +++ b/src/main/java/com/cleanroommc/modularui/widget/scroll/ScrollPadding.java @@ -74,40 +74,72 @@ public ScrollPadding setScrollPadding(ScrollPadding box) { return scrollPaddingAll(box.scrollPaddingLeft, box.scrollPaddingRight, box.scrollPaddingTop, box.scrollPaddingBottom); } + @Override public int getLeft() { return this.left + this.scrollPaddingLeft; } + @Override public int getRight() { return this.right + this.scrollPaddingRight; } + @Override public int getTop() { return this.top + this.scrollPaddingTop; } + @Override public int getBottom() { return this.bottom + this.scrollPaddingBottom; } + public int getScrollPaddingLeft() { + return scrollPaddingLeft; + } + + public int getScrollPaddingRight() { + return scrollPaddingRight; + } + + public int getScrollPaddingTop() { + return scrollPaddingTop; + } + + public int getScrollPaddingBottom() { + return scrollPaddingBottom; + } + + @Override public int vertical() { return super.vertical() + this.scrollPaddingTop + this.scrollPaddingBottom; } + @Override public int horizontal() { return super.horizontal() + this.scrollPaddingLeft + this.scrollPaddingRight; } + public int verticalScrollPadding() { + return this.scrollPaddingTop + this.scrollPaddingBottom; + } + + public int horizontalScrollPadding() { + return this.scrollPaddingLeft + this.scrollPaddingRight; + } + + @Override public int getStart(GuiAxis axis) { return axis.isHorizontal() ? this.left + this.scrollPaddingLeft : this.top + this.scrollPaddingTop; } + @Override public int getEnd(GuiAxis axis) { return axis.isHorizontal() ? this.right + this.scrollPaddingRight : this.bottom + this.scrollPaddingBottom; } public int getTotalScrollPadding(GuiAxis axis) { - return axis.isHorizontal() ? this.scrollPaddingLeft + this.scrollPaddingRight : this.scrollPaddingTop + this.scrollPaddingBottom; + return axis.isHorizontal() ? horizontalScrollPadding() : verticalScrollPadding(); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/widget/scroll/VerticalScrollData.java b/src/main/java/com/cleanroommc/modularui/widget/scroll/VerticalScrollData.java index 87efb6ae8..e7f30e3e5 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/scroll/VerticalScrollData.java +++ b/src/main/java/com/cleanroommc/modularui/widget/scroll/VerticalScrollData.java @@ -5,6 +5,8 @@ import com.cleanroommc.modularui.drawable.GuiDraw; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.utils.MathUtils; public class VerticalScrollData extends ScrollData { @@ -85,4 +87,33 @@ public void drawScrollbar(ScrollArea area, ModularGuiContext context, WidgetThem h = l; drawScrollBar(context, x, y, w, h, widgetTheme, texture); } + + @Override + public void drawScrollShadow(ScrollArea area, ModularGuiContext context) { + int min = 0, max = getScrollSize() - getFullVisibleSize(area); + int s = getScroll(); + final float maxOpacityScroll = 30f; + final int maxShadowSize = 10; + + int endColor = 0; + int startColorFull = Color.BLACK.brighter(4); + + ScrollPadding sp = area.getScrollPadding(); + int x = sp.getScrollPaddingLeft(); + int w = area.width - sp.horizontalScrollPadding(); + final int maxShadowSizeLimit = (area.h() - sp.verticalScrollPadding()) / 3; + + if (s > min) { + float prog = MathUtils.clamp(s / maxOpacityScroll, 0, 1); + int startColor = Color.withAlpha(startColorFull, prog * 0.8f); + int size = Math.min((int) (prog * maxShadowSize), maxShadowSizeLimit); + GuiDraw.drawVerticalGradientRect(x, sp.getScrollPaddingTop(), w, size, startColor, endColor); + } + if (s < max) { + float prog = MathUtils.clamp((max - s) / maxOpacityScroll, 0, 1); + int startColor = Color.withAlpha(startColorFull, prog * 0.8f); + int size = Math.min((int) (prog * maxShadowSize), maxShadowSizeLimit); + GuiDraw.drawVerticalGradientRect(x, area.h() - size - sp.getScrollPaddingBottom(), w, size, endColor, startColor); + } + } } diff --git a/src/main/java/com/cleanroommc/modularui/widget/sizer/DimensionSizer.java b/src/main/java/com/cleanroommc/modularui/widget/sizer/DimensionSizer.java index b03fbe8ba..75fb7f163 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/DimensionSizer.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/DimensionSizer.java @@ -173,14 +173,15 @@ private boolean needsSize(Unit unit) { return unit.isRelative() && unit.getAnchor() != 0; } - public void apply(Area area, ResizeNode relativeTo, IntSupplier defaultSize) { + public void apply(ResizeNode resizer, ResizeNode relativeTo, IntSupplier defaultSize) { boolean sizeCalculated = isSizeCalculated(); boolean posCalculated = isPosCalculated(); if (sizeCalculated && posCalculated) return; int p, s; int parentSize = relativeTo.getArea().getSize(this.axis); boolean calcParent = relativeTo.isSizeCalculated(this.axis); - Box padding = relativeTo.getArea().getPadding(); + Box padding = resizer.isDecoration() ? Box.ZERO : relativeTo.getArea().getPadding(); + Area area = resizer.getArea(); if (sizeCalculated) { // pos not calculated // size was calculated before @@ -263,40 +264,50 @@ public void apply(Area area, ResizeNode relativeTo, IntSupplier defaultSize) { area.setSize(this.axis, s); } - public int postApply(Area area, Area relativeTo, int p0, int p1) { + public int postApply(ResizeNode resizer, ResizeNode relativeTo, int p0, int p1) { // only called when the widget cover its children int moveAmount = 0; // calculate width and recalculate x based on the new width int s = p1 - p0, p; + Area area = resizer.getArea(); area.setSize(this.axis, s); this.sizeCalculated = true; if (!isPosCalculated()) { - if (this.start != null) { - p = calcPoint(this.start, relativeTo.getPadding(), s, relativeTo.getSize(this.axis), true); - } else if (this.end != null) { - p = calcPoint(this.end, relativeTo.getPadding(), s, relativeTo.getSize(this.axis), true) - s; - } else { - p = area.getRelativePoint(this.axis) + p0/* + area.getMargin().getStart(this.axis)*/; - if (!this.cancelAutoMovement) { - moveAmount = -p0; + Area relativeArea = relativeTo.getArea(); + Box padding = resizer.isDecoration() ? Box.ZERO : relativeArea.getPadding(); + int parentSize = relativeArea.getSize(this.axis); + boolean parentCalculated = relativeTo.isSizeCalculated(this.axis); + Unit point = getPoint(); + if (point == null || parentCalculated || !pointRequiresParentSize(point)) { + if (this.start != null) { + p = calcPoint(this.start, padding, s, parentSize, parentCalculated); + } else if (this.end != null) { + p = calcPoint(this.end, padding, s, parentSize, parentCalculated) - s; + } else { + p = area.getRelativePoint(this.axis) + p0/* + area.getMargin().getStart(this.axis)*/; + if (!this.cancelAutoMovement) { + moveAmount = -p0; + } } + area.setRelativePoint(this.axis, p); + this.posCalculated = true; } - area.setRelativePoint(this.axis, p); - this.posCalculated = true; } return moveAmount; } - public void coverChildrenForEmpty(Area area, Area relativeTo) { + public void coverChildrenForEmpty(ResizeNode resizer, Area relativeTo) { int s = 0; + Area area = resizer.getArea(); area.setSize(this.axis, s); this.sizeCalculated = true; if (!isPosCalculated()) { + Box padding = resizer.isDecoration() ? Box.ZERO : relativeTo.getPadding(); int p; if (this.start != null) { - p = calcPoint(this.start, relativeTo.getPadding(), s, relativeTo.getSize(this.axis), true); + p = calcPoint(this.start, padding, s, relativeTo.getSize(this.axis), true); } else if (this.end != null) { - p = calcPoint(this.end, relativeTo.getPadding(), s, relativeTo.getSize(this.axis), true) - s; + p = calcPoint(this.end, padding, s, relativeTo.getSize(this.axis), true) - s; } else { p = area.getRelativePoint(this.axis); } @@ -356,7 +367,7 @@ private int calcSize(Unit s, Box padding, int parentSize, boolean parentSizeCalc public int calcPoint(Unit p, Box padding, int width, int parentSize, boolean parentSizeCalculated) { float val = p.getValue(); - if (!parentSizeCalculated && (p == this.end || p.isRelative())) return (int) val; + if (!parentSizeCalculated && pointRequiresParentSize(p)) return (int) val; if (p.isRelative()) { val *= parentSize + padding.getTotal(this.axis); float anchor = p.getAnchor(); @@ -372,6 +383,14 @@ public int calcPoint(Unit p, Box padding, int width, int parentSize, boolean par return (int) val; } + public boolean pointRequiresParentSize(Unit p) { + return p == this.end || p.isRelative(); + } + + protected Unit getPoint() { + return this.start != null ? this.start : this.end; + } + public void detectConflictingConfiguration() { if (this.expanded && this.coverChildren) { ModularUI.LOGGER.warn("Resizer '{}' has expanded() and coverChildren() on {} axis. This conflicts and may cause layout issues.", this.resizer, this.axis); diff --git a/src/main/java/com/cleanroommc/modularui/widget/sizer/ResizeNode.java b/src/main/java/com/cleanroommc/modularui/widget/sizer/ResizeNode.java index 12692547c..7abe7fa77 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/ResizeNode.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/ResizeNode.java @@ -214,6 +214,10 @@ public boolean postLayoutChildren() { @ApiStatus.Internal public void checkExpanded(@Nullable GuiAxis axis) {} + public boolean isDecoration() { + return false; + } + public abstract boolean hasYPos(); public abstract boolean hasXPos(); diff --git a/src/main/java/com/cleanroommc/modularui/widget/sizer/StandardResizer.java b/src/main/java/com/cleanroommc/modularui/widget/sizer/StandardResizer.java index 04952ea21..8b7e4b43b 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/StandardResizer.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/StandardResizer.java @@ -4,12 +4,12 @@ import com.cleanroommc.modularui.ModularUI; import com.cleanroommc.modularui.api.GuiAxis; import com.cleanroommc.modularui.api.layout.ILayoutWidget; -import com.cleanroommc.modularui.api.layout.IResizeable; import com.cleanroommc.modularui.api.widget.IDelegatingWidget; import com.cleanroommc.modularui.api.widget.IPositioned; import com.cleanroommc.modularui.api.widget.IVanillaSlot; import com.cleanroommc.modularui.api.widget.IWidget; import com.cleanroommc.modularui.utils.Alignment; +import com.cleanroommc.modularui.utils.TreeUtil; import com.cleanroommc.modularui.widgets.layout.IExpander; import net.minecraft.inventory.Slot; @@ -30,6 +30,8 @@ public class StandardResizer extends WidgetResizeNode implements IPositioned getWidget().getDefaultWidth()); - this.y.apply(area, relativeTo, () -> getWidget().getDefaultHeight()); + this.x.apply(this, relativeTo, () -> getWidget().getDefaultWidth()); + this.y.apply(this, relativeTo, () -> getWidget().getDefaultHeight()); return isSelfFullyCalculated(isParentLayout); } @@ -162,21 +168,33 @@ public boolean postResize() { boolean coverHeight = this.y.dependsOnChildren(); if (!coverWidth && !coverHeight) return isSelfFullyCalculated(); IWidget widget = getWidget(); - if (!widget.hasChildren()) { + List children = widget.getChildren(); // TODO cover resizer children instead? + int decoCount = 0; + if (!children.isEmpty()) { + for (IWidget child : widget.getChildren()) { + if (child.resizer().isDecoration()) { + decoCount++; + } + } + } + if (decoCount == children.size()) { coverChildrenForEmpty(); return isSelfFullyCalculated(); } if (getWidget() instanceof ILayoutWidget layout) { // layout widgets handle widget layout's themselves, so we only need to fit the right and bottom border - coverChildrenForLayout(layout, widget); + coverChildrenForLayout(layout, widget, children); return isSelfFullyCalculated(); } + return doCoverChildren(widget, children, coverWidth, coverHeight) && isSelfFullyCalculated(); + } + + protected boolean doCoverChildren(IWidget widget, List children, boolean coverWidth, boolean coverHeight) { // non layout widgets can have their children in any position // we try to wrap all edges as close as possible to all widgets // this means for each edge there is at least one widget that touches it (plus padding and margin) // children are now calculated and now this area can be calculated if it requires childrens area - List children = widget.getChildren(); // TODO cover resizer children instead? int moveChildrenX = 0, moveChildrenY = 0; Box padding = getWidget().getArea().getPadding(); @@ -186,8 +204,9 @@ public boolean postResize() { boolean hasIndependentChildX = false; boolean hasIndependentChildY = false; for (IWidget child : children) { + StandardResizer resizeable = child.resizer(); + if (resizeable.isDecoration()) continue; Box margin = child.getArea().getMargin(); - ResizeNode resizeable = child.resizer(); Area area = child.getArea(); if (coverWidth) { if (!resizeable.dependsOnParentX()) { @@ -197,7 +216,7 @@ public boolean postResize() { x0 = Math.min(x0, area.rx - padding.getLeft() - margin.getLeft()); x1 = Math.max(x1, area.rx + area.width + padding.right + margin.right); } else { - return isSelfFullyCalculated(); + return true; } } } @@ -209,7 +228,7 @@ public boolean postResize() { y0 = Math.min(y0, area.ry - padding.getTop() - margin.getTop()); y1 = Math.max(y1, area.ry + area.height + padding.bottom + margin.bottom); } else { - return isSelfFullyCalculated(); + return true; } } } @@ -226,29 +245,29 @@ public boolean postResize() { if (h > y1 - y0) y1 = y0 + h; // now calculate new x, y, width and height based on the children area - Area relativeTo = getParent().getArea(); + ResizeNode relativeTo = getParent(); if (coverWidth) { // apply the size to this widget // the return value is the amount of pixels we need to move the children - moveChildrenX = this.x.postApply(getWidget().getArea(), relativeTo, x0, x1); + moveChildrenX = this.x.postApply(this, relativeTo, x0, x1); } if (coverHeight) { - moveChildrenY = this.y.postApply(getWidget().getArea(), relativeTo, y0, y1); + moveChildrenY = this.y.postApply(this, relativeTo, y0, y1); } // since the edges might have been moved closer to the widgets, the widgets should move back into it's original (absolute) position if (moveChildrenX != 0 || moveChildrenY != 0) { for (IWidget child : children) { + StandardResizer resizeable = child.resizer(); + if (resizeable.isDecoration()) continue; Area area = child.getArea(); - ResizeNode resizeable = child.resizer(); if (resizeable.isXCalculated()) area.rx += moveChildrenX; if (resizeable.isYCalculated()) area.ry += moveChildrenY; } } - return isSelfFullyCalculated(); + return true; } - private void coverChildrenForLayout(ILayoutWidget layout, IWidget widget) { - List children = widget.getChildren(); + protected void coverChildrenForLayout(ILayoutWidget layout, IWidget widget, List children) { Box padding = getWidget().getArea().getPadding(); // first calculate the area the children span int x1 = Integer.MIN_VALUE, y1 = Integer.MIN_VALUE; @@ -262,9 +281,10 @@ private void coverChildrenForLayout(ILayoutWidget layout, IWidget widget) { boolean coverByDefaultSizeY = coverHeight && layout.canCoverByDefaultSize(GuiAxis.Y); for (IWidget child : children) { if (layout.shouldIgnoreChildSize(child)) continue; + StandardResizer resizeable = child.resizer(); + if (resizeable.isDecoration()) continue; Area area = child.getArea(); Box margin = area.getMargin(); - IResizeable resizeable = child.resizer(); if (coverWidth) { if (!child.resizer().dependsOnParentX()) { hasIndependentChildX = true; @@ -309,17 +329,17 @@ private void coverChildrenForLayout(ILayoutWidget layout, IWidget widget) { if (w > x1) x1 = w; if (h > y1) y1 = h; - Area relativeTo = getParent().getArea(); - if (coverWidth) this.x.postApply(getArea(), relativeTo, 0, x1); - if (coverHeight) this.y.postApply(getArea(), relativeTo, 0, y1); + ResizeNode relativeTo = getParent(); + if (coverWidth) this.x.postApply(this, relativeTo, 0, x1); + if (coverHeight) this.y.postApply(this, relativeTo, 0, y1); } - private void coverChildrenForEmpty() { + protected void coverChildrenForEmpty() { if (this.x.dependsOnChildren()) { - this.x.coverChildrenForEmpty(getWidget().getArea(), getParent().getArea()); + this.x.coverChildrenForEmpty(this, getParent().getArea()); } if (this.y.dependsOnChildren()) { - this.y.coverChildrenForEmpty(getWidget().getArea(), getParent().getArea()); + this.y.coverChildrenForEmpty(this, getParent().getArea()); } } @@ -328,9 +348,13 @@ public void preApplyPos() { IWidget widget = getWidget(); Area relativeTo = getParent().getArea(); Area area = widget.getArea(); - // apply margin and padding if not done yet - this.x.applyMarginAndPaddingToPos(widget, area, relativeTo); - this.y.applyMarginAndPaddingToPos(widget, area, relativeTo); + if (isDecoration()) { + setMarginPaddingApplied(true); + } else { + // apply margin and padding if not done yet + this.x.applyMarginAndPaddingToPos(widget, area, relativeTo); + this.y.applyMarginAndPaddingToPos(widget, area, relativeTo); + } // after all widgets x, y, width and height have been calculated we can now calculate the absolute position area.applyPos(relativeTo.x, relativeTo.y); } @@ -477,6 +501,13 @@ public StandardResizer relativeToParent() { @Override public StandardResizer relativeToScreen() { this.relativeToScreen = true; + if (getParent() != null) { + // if this is currently part of a tree, try to find the root and attach ourselves to it + ScreenResizeNode root = TreeUtil.findParent(this, ScreenResizeNode.class); + if (root != null) { + setParentOverride(root); + } + } return this; } @@ -499,6 +530,12 @@ public StandardResizer coverChildrenHeight() { return this; } + @Override + public StandardResizer decoration(boolean decoration) { + this.decoration = decoration; + return this; + } + @ApiStatus.Internal public StandardResizer left(float x, int offset, float anchor, Unit.Measure measure, boolean autoAnchor) { return unit(getLeft(), x, offset, anchor, measure, autoAnchor); @@ -595,34 +632,44 @@ private StandardResizer unitSize(Unit u, DoubleSupplier val, int offset, Unit.Me return this; } + @Override public StandardResizer anchorLeft(float val) { + getLeft().setMeasure(Unit.Measure.RELATIVE); getLeft().setAnchor(val); getLeft().setAutoAnchor(false); scheduleResize(); return this; } + @Override public StandardResizer anchorRight(float val) { + getRight().setMeasure(Unit.Measure.RELATIVE); getRight().setAnchor(1 - val); getRight().setAutoAnchor(false); scheduleResize(); return this; } + @Override public StandardResizer anchorTop(float val) { + getTop().setMeasure(Unit.Measure.RELATIVE); getTop().setAnchor(val); getTop().setAutoAnchor(false); scheduleResize(); return this; } + @Override public StandardResizer anchorBottom(float val) { + getBottom().setMeasure(Unit.Measure.RELATIVE); getBottom().setAnchor(1 - val); getBottom().setAutoAnchor(false); scheduleResize(); return this; } + @ApiStatus.ScheduledForRemoval(inVersion = "3.3.0") + @Deprecated public StandardResizer anchor(Alignment alignment) { if (this.x.hasStart() || !this.x.hasEnd()) { anchorLeft(alignment.x); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java index e0f3bfb2c..439dedc34 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/AbstractCycleButtonWidget.java @@ -182,22 +182,22 @@ public WidgetThemeEntry getWidgetThemeInternal(ITheme theme) { } @Override - public IDrawable getCurrentBackground(ITheme theme, WidgetThemeEntry widgetTheme) { + public IDrawable getCurrentBackground(WidgetThemeEntry widgetTheme) { // make sure texture is up-to-date int state = getState(); if (isHovering() && this.hoverBackground != null && this.hoverBackground[state] != null && this.hoverBackground[state] != IDrawable.NONE) { return this.hoverBackground[state]; } - return this.background != null && this.background[state] != null ? this.background[state] : super.getCurrentBackground(theme, widgetTheme); + return this.background != null && this.background[state] != null ? this.background[state] : super.getCurrentBackground(widgetTheme); } @Override - public IDrawable getCurrentOverlay(ITheme theme, WidgetThemeEntry widgetTheme) { + public IDrawable getCurrentOverlay(WidgetThemeEntry widgetTheme) { int state = getState(); if (isHovering() && this.hoverOverlay != null && this.hoverOverlay[state] != null && this.hoverOverlay[state] != IDrawable.NONE) { return this.hoverOverlay[state]; } - return this.overlay != null && this.overlay[state] != null ? this.overlay[state] : super.getCurrentOverlay(theme, widgetTheme); + return this.overlay != null && this.overlay[state] != null ? this.overlay[state] : super.getCurrentOverlay(widgetTheme); } @Override @@ -234,7 +234,7 @@ public W disableHoverBackground() { Arrays.fill(this.hoverBackground, IDrawable.NONE); } if (getHoverBackground() == null) { - super.hoverBackground(IDrawable.NONE); + super.hoverBackgroundOverlay(IDrawable.NONE); } return getThis(); } @@ -257,7 +257,7 @@ public W invisible() { Arrays.fill(this.background, IDrawable.EMPTY); } if (getBackground() == null) { - super.background(IDrawable.EMPTY); + super.backgroundOverlay(IDrawable.EMPTY); } return disableHoverBackground(); } @@ -480,6 +480,7 @@ public W tooltipDynamic(Consumer tooltipBuilder) { */ @Override public W tooltipAlignment(Alignment alignment) { + super.tooltipAlignment(alignment); expectCount(); for (RichTooltip tooltip : this.tooltip) { tooltip.alignment(alignment); @@ -495,6 +496,7 @@ public W tooltipAlignment(Alignment alignment) { */ @Override public W tooltipPos(RichTooltip.Pos pos) { + super.tooltipPos(pos); expectCount(); for (RichTooltip tooltip : this.tooltip) { tooltip.pos(pos); @@ -511,6 +513,7 @@ public W tooltipPos(RichTooltip.Pos pos) { */ @Override public W tooltipPos(int x, int y) { + super.tooltipPos(x, y); expectCount(); for (RichTooltip tooltip : this.tooltip) { tooltip.pos(x, y); @@ -526,6 +529,7 @@ public W tooltipPos(int x, int y) { */ @Override public W tooltipScale(float scale) { + super.tooltipScale(scale); expectCount(); for (RichTooltip tooltip : this.tooltip) { tooltip.scale(scale); @@ -541,6 +545,7 @@ public W tooltipScale(float scale) { */ @Override public W tooltipTextColor(int textColor) { + super.tooltipTextColor(textColor); expectCount(); for (RichTooltip tooltip : this.tooltip) { tooltip.textColor(textColor); @@ -556,6 +561,7 @@ public W tooltipTextColor(int textColor) { */ @Override public W tooltipTextShadow(boolean textShadow) { + super.tooltipTextShadow(textShadow); expectCount(); for (RichTooltip tooltip : this.tooltip) { tooltip.textShadow(textShadow); @@ -571,6 +577,7 @@ public W tooltipTextShadow(boolean textShadow) { */ @Override public W tooltipShowUpTimer(int showUpTimer) { + super.tooltipShowUpTimer(showUpTimer); expectCount(); for (RichTooltip tooltip : this.tooltip) { tooltip.showUpTimer(showUpTimer); @@ -578,6 +585,22 @@ public W tooltipShowUpTimer(int showUpTimer) { return getThis(); } + /** + * Sets the tooltip auto update value for all states. + * + * @param update true if tooltips should automatically update + * @return this + */ + @Override + public W tooltipAutoUpdate(boolean update) { + super.tooltipAutoUpdate(update); + expectCount(); + for (RichTooltip tooltip : this.tooltip) { + tooltip.setAutoUpdate(update); + } + return getThis(); + } + protected W stateCount(int stateCount) { updateStateCount(stateCount, true); return getThis(); @@ -607,8 +630,8 @@ protected IDrawable[] addToArray(IDrawable[] array, IDrawable drawable, int inde } protected static void splitTexture(UITexture texture, IDrawable[] dest) { + float a = 1f / dest.length; for (int i = 0; i < dest.length; i++) { - float a = 1f / dest.length; dest[i] = texture.getSubArea(0, i * a, 1, i * a + a); } } @@ -634,4 +657,12 @@ public W clickSound(Runnable clickSound) { this.clickSound = clickSound; return getThis(); } + + public int getStateCount() { + return stateCount; + } + + public IIntValue getIntValue() { + return intValue; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/AbstractFluidDisplayWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/AbstractFluidDisplayWidget.java index 47234fad3..f96270a43 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/AbstractFluidDisplayWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/AbstractFluidDisplayWidget.java @@ -159,4 +159,12 @@ public W flipLighterThanAir(boolean flipLighterThanAir) { this.flipLighterThanAir = flipLighterThanAir; return getThis(); } + + public Box getContentPadding() { + return contentPadding; + } + + public SIPrefix getBaseUnitPrefix() { + return baseUnitPrefix; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ButtonWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ButtonWidget.java index fdaa89833..bbb1c2219 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ButtonWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ButtonWidget.java @@ -184,4 +184,17 @@ public W clickSound(Runnable clickSound) { this.clickSound = clickSound; return getThis(); } + + public boolean isPlayClickSound() { + return playClickSound; + } + + public Runnable getClickSound() { + return clickSound; + } + + @Override + public @NotNull InteractionSyncHandler getSyncHandler() { + return syncHandler; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/CategoryList.java b/src/main/java/com/cleanroommc/modularui/widgets/CategoryList.java index 15e338276..3a70a4a38 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/CategoryList.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/CategoryList.java @@ -123,6 +123,26 @@ public CategoryList setExpandedOverlay(IDrawable expandedOverlay) { return this; } + public List getSubCategories() { + return subCategories; + } + + public boolean isExpanded() { + return expanded; + } + + public int getTotalHeight() { + return totalHeight; + } + + public IDrawable getExpandedOverlay() { + return expandedOverlay; + } + + public IDrawable getCollapsedOverlay() { + return collapsedOverlay; + } + public static class Root extends ListWidget { private final List categories = new ArrayList<>(); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ColorPickerDialog.java b/src/main/java/com/cleanroommc/modularui/widgets/ColorPickerDialog.java index e49056204..7ff607eee 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ColorPickerDialog.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ColorPickerDialog.java @@ -12,8 +12,7 @@ import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.value.DoubleValue; import com.cleanroommc.modularui.value.StringValue; -import com.cleanroommc.modularui.widgets.layout.Column; -import com.cleanroommc.modularui.widgets.layout.Row; +import com.cleanroommc.modularui.widgets.layout.Flow; import com.cleanroommc.modularui.widgets.textfield.TextFieldWidget; import java.util.function.Consumer; @@ -52,9 +51,9 @@ public ColorPickerDialog(String name, Consumer resultConsumer, int star updateAll(startColor); size(140, controlAlpha ? 106 : 94).background(GuiTextures.MC_BACKGROUND); PagedWidget.Controller controller = new PagedWidget.Controller(); - child(new Column() + child(Flow.col() .left(5).right(5).top(5).bottom(5) - .child(new Row() + .child(Flow.row() .left(5).right(5).height(14) .child(new PageButton(0, controller) .sizeRel(0.5f, 1f) @@ -62,7 +61,7 @@ public ColorPickerDialog(String name, Consumer resultConsumer, int star .child(new PageButton(1, controller) .sizeRel(0.5f, 1f) .overlay(IKey.str("HSV")))) - .child(new Row().widthRel(1f).height(12).marginTop(4) + .child(Flow.row().widthRel(1f).height(12).marginTop(4) .child(IKey.str("Hex: ").asWidget().heightRel(1f)) .child(new TextFieldWidget() .height(12) @@ -87,7 +86,7 @@ public ColorPickerDialog(String name, Consumer resultConsumer, int star .controller(controller) .addPage(createRGBPage(createAlphaSlider("rgb"))) .addPage(createHSVPage(createAlphaSlider("hsv")))) - .child(new Row() + .child(Flow.row() .left(10).right(10).height(14) .mainAxisAlignment(Alignment.MainAxis.SPACE_BETWEEN) .child(new ButtonWidget<>() @@ -107,57 +106,57 @@ public ColorPickerDialog(String name, Consumer resultConsumer, int star } private IWidget createRGBPage(IWidget alphaSlider) { - return new Column() + return Flow.col() .sizeRel(1f, 1f) - .child(new Row() + .child(Flow.row() .widthRel(1f).height(12) .child(IKey.str("R: ").asWidget().heightRel(1f)) .child(createSlider(this.sliderBackgroundR) .name("red") .bounds(0, 255) .value(new DoubleValue.Dynamic(() -> this.red, this::updateRed)))) - .child(new Row() + .child(Flow.row() .widthRel(1f).height(12) .child(IKey.str("G: ").asWidget().heightRel(1f)) .child(createSlider(this.sliderBackgroundG) .name("green") .bounds(0, 255) .value(new DoubleValue.Dynamic(() -> this.green, this::updateGreen)))) - .child(new Row() + .child(Flow.row() .widthRel(1f).height(12) .child(IKey.str("B: ").asWidget().heightRel(1f)) .child(createSlider(this.sliderBackgroundB) .name("blue") .bounds(0, 255) .value(new DoubleValue.Dynamic(() -> this.blue, this::updateBlue)))) - .childIf(alphaSlider != null, alphaSlider); + .childIf(alphaSlider != null, () -> alphaSlider); } private IWidget createHSVPage(IWidget alphaSlider) { - return new Column() + return Flow.col() .sizeRel(1f, 1f) - .child(new Row() + .child(Flow.row() .widthRel(1f).height(12) .child(IKey.str("H: ").asWidget().heightRel(1f)) .child(createSlider(new HueBar(GuiAxis.X)) .name("hue") .bounds(0, 360) .value(new DoubleValue.Dynamic(() -> this.hue, this::updateHue)))) - .child(new Row() + .child(Flow.row() .widthRel(1f).height(12) .child(IKey.str("S: ").asWidget().heightRel(1f)) .child(createSlider(this.sliderBackgroundS) .name("saturation") .bounds(0, 1) .value(new DoubleValue.Dynamic(() -> this.saturation, this::updateSaturation)))) - .child(new Row() + .child(Flow.row() .widthRel(1f).height(12) .child(IKey.str("V: ").asWidget().heightRel(1f)) .child(createSlider(this.sliderBackgroundV) .name("value") .bounds(0, 1) .value(new DoubleValue.Dynamic(() -> this.value, this::updateValue)))) - .childIf(alphaSlider != null, alphaSlider); + .childIf(alphaSlider != null, () -> alphaSlider); } private static SliderWidget createSlider(IDrawable background) { @@ -170,7 +169,7 @@ private static SliderWidget createSlider(IDrawable background) { } private IWidget createAlphaSlider(String s) { - return controlAlpha ? new Row() + return controlAlpha ? Flow.row() .widthRel(1f).height(12) .child(IKey.str("A: ").asWidget().heightRel(1f)) .child(createSlider(this.sliderBackgroundA) @@ -270,4 +269,40 @@ public void updateColor(int color) { this.sliderBackgroundV.horizontalGradient(Color.withValue(color, 0f), Color.withValue(color, 1f)); this.preview.color(color); } + + public int getColor() { + return color; + } + + public int getRed() { + return red; + } + + public int getGreen() { + return green; + } + + public int getBlue() { + return blue; + } + + public double getHue() { + return hue; + } + + public double getSaturation() { + return saturation; + } + + public double getHSVValue() { + return value; + } + + public int getColorAlpha() { + return alpha; + } + + public boolean isControlAlpha() { + return controlAlpha; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java index e29c00b46..7461de59f 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java @@ -23,12 +23,12 @@ public CycleButtonWidget value(IIntValue value) { public CycleButtonWidget stateBackground(int state, IDrawable drawable) { this.background = addToArray(this.background, drawable, state); - return getThis(); + return disableThemeBackground(true); } public CycleButtonWidget stateHoverBackground(int state, IDrawable drawable) { this.hoverBackground = addToArray(this.hoverBackground, drawable, state); - return getThis(); + return disableHoverThemeBackground(true); } public CycleButtonWidget stateOverlay(int state, IDrawable drawable) { diff --git a/src/main/java/com/cleanroommc/modularui/widgets/DynamicSyncedWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/DynamicSyncedWidget.java index a8523041a..65b45e6c7 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/DynamicSyncedWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/DynamicSyncedWidget.java @@ -83,4 +83,11 @@ public W initialChild(IWidget child) { this.child = child; return getThis(); } + + public @NotNull IDynamicSyncNotifiable getDynamicSyncHandler() { + if (this.syncHandler == null) { + throw new IllegalStateException("Widget is not initialised or not synced!"); + } + return syncHandler; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/Expandable.java b/src/main/java/com/cleanroommc/modularui/widgets/Expandable.java index d0e0e2f15..c065dc7a0 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/Expandable.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/Expandable.java @@ -170,4 +170,24 @@ public Expandable interpolation(IInterpolation interpolation) { this.interpolation = interpolation; return this; } + + public boolean isExpanded() { + return expanded; + } + + public Animator getAnimator() { + return animator; + } + + public BiConsumer getStencilTransform() { + return stencilTransform; + } + + public int getAnimationDuration() { + return animationDuration; + } + + public IInterpolation getInterpolation() { + return interpolation; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/FluidDisplayWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/FluidDisplayWidget.java index 95aa90d92..fec5ca0a0 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/FluidDisplayWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/FluidDisplayWidget.java @@ -85,4 +85,13 @@ public FluidDisplayWidget displayAmount(boolean displayAmount) { public FluidDisplayWidget fluidTooltip(BiConsumer tooltip) { return tooltipAutoUpdate(true).tooltipBuilder(t -> tooltip.accept(t, getFluidStack())); } + + @Override + public @Nullable IValue getValue() { + return value; + } + + public boolean isDisplayAmount() { + return displayAmount; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ItemDisplayWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ItemDisplayWidget.java index de3ef4bb5..7ead135b1 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ItemDisplayWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ItemDisplayWidget.java @@ -86,4 +86,13 @@ public ItemDisplayWidget displayAmount(boolean displayAmount) { public @Nullable ItemStack getStackForRecipeViewer() { return value.getValue(); } + + @Override + public @Nullable IValue getValue() { + return value; + } + + public boolean isDisplayAmount() { + return displayAmount; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java index d8c98587e..5b1d74133 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java @@ -327,4 +327,24 @@ public W reverseLayout(boolean reverseLayout) { this.reverseLayout = reverseLayout; return getThis(); } + + public IIcon getChildSeparator() { + return childSeparator; + } + + public boolean isCollapseDisabledChild() { + return collapseDisabledChild; + } + + public boolean isWrapTight() { + return wrapTight; + } + + public Alignment.CrossAxis getCaa() { + return caa; + } + + public boolean isReverseLayout() { + return reverseLayout; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/PageButton.java b/src/main/java/com/cleanroommc/modularui/widgets/PageButton.java index 78d3c3798..8fcb9aa80 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/PageButton.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/PageButton.java @@ -84,4 +84,16 @@ public PageButton invertSelected(boolean invert) { public boolean invertSelected() { return this.invert; } + + public int getIndex() { + return index; + } + + public PagedWidget.Controller getController() { + return controller; + } + + public boolean isInvert() { + return invert; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java index e1168859f..80ac365ad 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java @@ -14,6 +14,7 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.function.DoubleSupplier; @@ -200,6 +201,31 @@ public ProgressWidget direction(Direction direction) { return this; } + public UITexture[] getFullTexture() { + return fullTexture; + } + + public UITexture getEmptyTexture() { + return emptyTexture; + } + + public Direction getDirection() { + return direction; + } + + public int getImageSize() { + return imageSize; + } + + public IDoubleValue getDoubleValue() { + return doubleValue; + } + + @Override + public @Nullable IDoubleValue getValue() { + return doubleValue; + } + public enum Direction { LEFT, RIGHT, UP, DOWN, CIRCULAR_CW } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/RichTextWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/RichTextWidget.java index a0fd96a39..bbabc227a 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/RichTextWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/RichTextWidget.java @@ -168,4 +168,8 @@ public RichTextWidget textBuilder(Consumer builder) { markDirty(); return this; } + + public boolean isAutoUpdate() { + return autoUpdate; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java index 833d242eb..1e70982c6 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java @@ -144,6 +144,46 @@ public MovingObjectPosition getBlockUnderMouse() { return schema.getLastRayTrace(); } + public float getScale() { + return scale; + } + + public float getPitch() { + return pitch; + } + + public float getYaw() { + return yaw; + } + + public Vector3f getOffset() { + return offset; + } + + public int getLastMouseY() { + return lastMouseY; + } + + public int getLastMouseX() { + return lastMouseX; + } + + public boolean isEnableScaling() { + return enableScaling; + } + + public boolean isEnableTranslation() { + return enableTranslation; + } + + public boolean isEnableRotation() { + return enableRotation; + } + + public BaseSchemaRenderer getSchema() { + return schema; + } + public static class LayerButton extends ButtonWidget { private final int minLayer; diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ScrollingTextWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ScrollingTextWidget.java index db4671355..94c503ad8 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ScrollingTextWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ScrollingTextWidget.java @@ -110,4 +110,24 @@ public ScrollingTextWidget animator(Animator animator) { this.animator = new SequentialAnimator(this.forward, new Wait(500), this.backward, new Wait(1000)).repeatsOnFinish(20); return this; } + + public float getProgress() { + return progress; + } + + public IAnimator getAnimator() { + return animator; + } + + public Animator getForward() { + return forward; + } + + public Animator getBackward() { + return backward; + } + + public int getSpeed() { + return speed; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java index ab7ff1f77..712aaf7cd 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java @@ -285,4 +285,44 @@ public SliderWidget stopperSize(int w, int h) { this.stopperHeight = h; return this; } + + public IDoubleValue getDoubleValue() { + return doubleValue; + } + + public IDrawable getStopperDrawable() { + return stopperDrawable; + } + + public IDrawable getHandleDrawable() { + return handleDrawable; + } + + public GuiAxis getAxis() { + return axis; + } + + public int getStopperWidth() { + return stopperWidth; + } + + public int getStopperHeight() { + return stopperHeight; + } + + public Unit getSliderWidth() { + return sliderWidth; + } + + public Unit getSliderHeight() { + return sliderHeight; + } + + public Area getSliderArea() { + return sliderArea; + } + + public double getEach() { + return each; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/SlotGroupWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/SlotGroupWidget.java index 7c44f3666..fe55a0477 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/SlotGroupWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/SlotGroupWidget.java @@ -157,6 +157,14 @@ public static Builder builder() { return new Builder(); } + public String getSlotGroupName() { + return slotGroupName; + } + + public SlotGroup getSlotGroup() { + return slotGroup; + } + public interface SlotConsumer { ItemSlot apply(int index, ItemSlot widgetSlot); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java index c932102fc..6a6fe702e 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java @@ -159,6 +159,10 @@ public float getScale() { return this.shadow; } + public int getMaxWidth() { + return maxWidth; + } + @Deprecated public W alignment(Alignment alignment) { return textAlign(alignment); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java b/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java index 77735548f..b6e849a55 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ToggleButton.java @@ -58,18 +58,18 @@ public ToggleButton selectedHoverBackground(IDrawable... selectedHoverBackground } @Override - public ToggleButton background(IDrawable... selectedBackground) { + public ToggleButton backgroundOverlay(IDrawable... selectedBackground) { return background(false, selectedBackground); } @Override - public ToggleButton hoverBackground(IDrawable... selectedHoverBackground) { + public ToggleButton hoverBackgroundOverlay(IDrawable... selectedHoverBackground) { return hoverBackground(false, selectedHoverBackground); } public ToggleButton background(boolean selected, IDrawable... background) { this.background = addToArray(this.background, background, selected ? 1 : 0); - return this; + return disableThemeBackground(true); } public ToggleButton overlay(boolean selected, IDrawable... overlay) { @@ -79,7 +79,7 @@ public ToggleButton overlay(boolean selected, IDrawable... overlay) { public ToggleButton hoverBackground(boolean selected, IDrawable... background) { this.hoverBackground = addToArray(this.hoverBackground, background, selected ? 1 : 0); - return this; + return disableHoverThemeBackground(true); } public ToggleButton hoverOverlay(boolean selected, IDrawable... overlay) { @@ -115,4 +115,8 @@ public ToggleButton child(boolean selected, IWidget widget) { public boolean invertSelected() { return this.invert; } + + public boolean isInvert() { + return invert; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/TransformWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/TransformWidget.java index d6d0901da..5ec623c49 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/TransformWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/TransformWidget.java @@ -55,4 +55,8 @@ private static Vector3f vec(float x, float y, float z) { sharedVec.set(x, y, z); return sharedVec; } + + public boolean hasConstTransform() { + return hasConstTransform; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/layout/Column.java b/src/main/java/com/cleanroommc/modularui/widgets/layout/Column.java index e407eb7dc..721c5bddf 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/layout/Column.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/layout/Column.java @@ -2,6 +2,10 @@ import com.cleanroommc.modularui.api.GuiAxis; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.ScheduledForRemoval(inVersion = "3.3.0") +@Deprecated public class Column extends Flow { public Column() { diff --git a/src/main/java/com/cleanroommc/modularui/widgets/layout/Flow.java b/src/main/java/com/cleanroommc/modularui/widgets/layout/Flow.java index e70118ecb..af8edd766 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/layout/Flow.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/layout/Flow.java @@ -269,6 +269,14 @@ public void onChildChangeEnabled(IWidget child, boolean enabled) { } } + public boolean isRow() { + return getAxis().isHorizontal(); + } + + public boolean isColumn() { + return getAxis().isVertical(); + } + public Flow children(Iterable widgets) { for (IWidget widget : widgets) { child(widget); @@ -290,6 +298,34 @@ public Flow children(Iterable it, Function widgetCreator) { return getThis(); } + public Alignment.MainAxis getMaa() { + return maa; + } + + public Alignment.CrossAxis getCaa() { + return caa; + } + + public int getChildPadding() { + return childPadding; + } + + public int getCrossAxisChildPadding() { + return crossAxisChildPadding; + } + + public boolean isCollapseDisabledChild() { + return collapseDisabledChild; + } + + public boolean isReverseLayout() { + return reverseLayout; + } + + public boolean isWrap() { + return wrap; + } + /** * Sets the main axis alignment of this flow. This determines how multiple widgets are laid out along the main axis in this flow. * diff --git a/src/main/java/com/cleanroommc/modularui/widgets/layout/Grid.java b/src/main/java/com/cleanroommc/modularui/widgets/layout/Grid.java index c94e7ca40..bd6520f03 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/layout/Grid.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/layout/Grid.java @@ -29,8 +29,8 @@ public class Grid extends AbstractScrollWidget implements ILayout private final Box minElementMargin = new Box(); private int minRowHeight = 5, minColWidth = 5; private Alignment alignment = Alignment.Center; - private boolean dirty = false; private boolean collapseDisabledChild = false; + private boolean dirty = false; public Grid() { super(null, null); @@ -318,6 +318,26 @@ public static List> mapToMatrix(int rowLength, int s return matrix; } + public Box getMinElementMargin() { + return minElementMargin; + } + + public int getMinRowHeight() { + return minRowHeight; + } + + public int getMinColWidth() { + return minColWidth; + } + + public Alignment getAlignment() { + return alignment; + } + + public boolean isCollapseDisabledChild() { + return collapseDisabledChild; + } + public interface IndexedElementMapper { I apply(int index, T value); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/layout/Row.java b/src/main/java/com/cleanroommc/modularui/widgets/layout/Row.java index f92b3b049..94c3c23b5 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/layout/Row.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/layout/Row.java @@ -2,6 +2,10 @@ import com.cleanroommc.modularui.api.GuiAxis; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.ScheduledForRemoval(inVersion = "3.3.0") +@Deprecated public class Row extends Flow { public Row() { diff --git a/src/main/java/com/cleanroommc/modularui/widgets/menu/AbstractMenuButton.java b/src/main/java/com/cleanroommc/modularui/widgets/menu/AbstractMenuButton.java index a2c66bb66..97b994247 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/menu/AbstractMenuButton.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/menu/AbstractMenuButton.java @@ -62,11 +62,11 @@ public boolean isOpen() { /** * @return true if the menu is currently soft open (opened by hovering) */ - protected boolean isSoftOpen() { + public boolean isSoftOpen() { return softOpen; } - protected void toggleMenu(boolean soft) { + public void toggleMenu(boolean soft) { if (this.open) { if (this.softOpen) { if (soft) { @@ -86,7 +86,7 @@ protected void toggleMenu(boolean soft) { } } - protected void openMenu(boolean soft) { + public void openMenu(boolean soft) { if (this.open) { if (this.softOpen && !soft) { this.softOpen = false; @@ -102,11 +102,11 @@ protected void openMenu(boolean soft) { this.softOpen = soft; } - protected void closeMenu(boolean soft) { + public void closeMenu(boolean soft) { if (!this.open || (!this.softOpen && soft)) return; - if (getPanel() instanceof MenuPanel menuPanel) { + if (isValid() && getPanel() instanceof MenuPanel menuPanel) { menuPanel.remove(getMenu()); - } else { + } else if (getPanelHandler().isPanelOpen()) { getPanelHandler().closePanel(); } this.open = false; @@ -181,15 +181,15 @@ protected boolean forEachSiblingMenuButton(Predicate> test @Override public void onMouseLeaveArea() { super.onMouseLeaveArea(); - checkClose(); + checkClose(true, true); } - protected void checkClose() { - if (this.openOnHover && !isSelfOrChildHovered()) { - closeMenu(true); + protected void checkClose(boolean soft, boolean requireNoHover) { + if ((this.openOnHover || !soft) && !isSelfOrChildHovered()) { + closeMenu(soft); Menu menuParent = WidgetTree.findParent(this, Menu.class); if (menuParent != null) { - menuParent.checkClose(); + menuParent.checkClose(soft, requireNoHover); } } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/menu/DropdownWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/menu/DropdownWidget.java index fc984c452..bcf43f069 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/menu/DropdownWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/menu/DropdownWidget.java @@ -9,6 +9,7 @@ import com.cleanroommc.modularui.widgets.ListWidget; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; @@ -208,6 +209,19 @@ public W directionDown() { return getThis(); } + public int getMaxListSize() { + return maxListSize; + } + + @Override + public @Nullable IValue getValue() { + return value; + } + + public Class getValueType() { + return valueType; + } + public interface ToWidget { /** diff --git a/src/main/java/com/cleanroommc/modularui/widgets/menu/Menu.java b/src/main/java/com/cleanroommc/modularui/widgets/menu/Menu.java index 2b577e7f5..bac25435b 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/menu/Menu.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/menu/Menu.java @@ -17,14 +17,16 @@ void setMenuSource(AbstractMenuButton source) { @Override public void onMouseLeaveArea() { super.onMouseLeaveArea(); - checkClose(); + checkClose(true, true); } - protected void checkClose() { - if (this.menuSource != null && !this.menuSource.isBelowMouse() && !isSelfOrChildHovered()) { - this.menuSource.closeMenu(true); - this.menuSource.checkClose(); + public void checkClose(boolean soft, boolean requireNoHover) { + if (this.menuSource == null) return; + if (soft || requireNoHover) { + if (this.menuSource.isBelowMouse() || isSelfOrChildHovered()) return; } + this.menuSource.closeMenu(soft); + this.menuSource.checkClose(soft, requireNoHover); } @Override @@ -42,4 +44,8 @@ protected void onChildAdd(IWidget child) { protected WidgetThemeEntry getWidgetThemeInternal(ITheme theme) { return theme.getWidgetTheme(IThemeApi.PANEL); } + + public AbstractMenuButton getMenuSource() { + return menuSource; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/menu/MenuPanel.java b/src/main/java/com/cleanroommc/modularui/widgets/menu/MenuPanel.java index 6ce837a83..36e91bbd6 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/menu/MenuPanel.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/menu/MenuPanel.java @@ -2,9 +2,12 @@ import com.cleanroommc.modularui.api.widget.IWidget; import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.widget.WidgetTree; import org.jetbrains.annotations.ApiStatus; +import java.util.List; + @ApiStatus.Experimental public class MenuPanel extends ModularPanel { @@ -19,6 +22,13 @@ public void openSubMenu(IWidget menuList) { child(menuList); } + @Override + public void onClose() { + super.onClose(); + // close all menus that are related to this panel + closeAllMenus(false, false); + } + @Override protected void onChildAdd(IWidget child) { super.onChildAdd(child); @@ -34,4 +44,13 @@ public boolean isDraggable() { public boolean closeOnOutOfBoundsClick() { return true; } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public void closeAllMenus(boolean soft, boolean requireNoHover) { + // need to collect menus first instead of closing while iterating to avoid CME + List menus = WidgetTree.flatListByType(this, Menu.class); + for (Menu menu : menus) { + menu.checkClose(soft, requireNoHover); + } + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/slot/FluidSlot.java b/src/main/java/com/cleanroommc/modularui/widgets/slot/FluidSlot.java index 456021238..490b7351e 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/slot/FluidSlot.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/slot/FluidSlot.java @@ -263,4 +263,16 @@ public boolean handleDragAndDrop(@NotNull ItemStack draggedStack, int button) { protected void setPhantomValue(@NotNull ItemStack draggedStack) { this.syncHandler.setValue(FluidContainerRegistry.getFluidForFilledItem(draggedStack)); } + + @Override + public @NotNull FluidSlotSyncHandler getSyncHandler() { + if (this.syncHandler == null) { + throw new IllegalStateException("Widget is not initialised or not synced!"); + } + return syncHandler; + } + + public boolean isAlwaysShowFull() { + return alwaysShowFull; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/slot/ModularSlot.java b/src/main/java/com/cleanroommc/modularui/widgets/slot/ModularSlot.java index 18b8b8038..d7e567a12 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/slot/ModularSlot.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/slot/ModularSlot.java @@ -1,6 +1,8 @@ package com.cleanroommc.modularui.widgets.slot; import com.cleanroommc.modularui.utils.item.IItemHandler; +import com.cleanroommc.modularui.utils.item.IItemHandlerModifiable; +import com.cleanroommc.modularui.utils.item.PlayerArmorInvWrapper; import com.cleanroommc.modularui.utils.item.PlayerInvWrapper; import com.cleanroommc.modularui.utils.item.PlayerMainInvWrapper; import com.cleanroommc.modularui.utils.item.SlotItemHandler; @@ -57,14 +59,6 @@ public void initialize(ItemSlotSH syncManager, boolean phantom) { this.phantom = phantom; } - protected boolean canPut() { - return canPut; - } - - protected boolean canTake() { - return canTake; - } - @ApiStatus.Internal public void dispose() { this.syncHandler = null; @@ -276,4 +270,42 @@ public static boolean isPlayerSlot(SlotItemHandler slot) { return slot.getItemHandler() instanceof PlayerInvWrapper || slot.getItemHandler() instanceof PlayerMainInvWrapper || slot.getItemHandler() instanceof com.gtnewhorizons.modularui.api.forge.PlayerMainInvWrapper; } + + public static EntityPlayer getPlayerSlotPlayer(Slot slot) { + return slot.inventory instanceof InventoryPlayer inv ? inv.player : null; + } + + public static EntityPlayer getPlayerSlotPlayer(SlotItemHandler slot) { + if (slot.getItemHandler() instanceof PlayerInvWrapper inv) { + for (IItemHandlerModifiable ih : inv) { + if (ih instanceof PlayerMainInvWrapper mainInv) { + return mainInv.getInventoryPlayer().player; + } + } + return null; + } + if (slot.getItemHandler() instanceof PlayerMainInvWrapper wrapper) { + return wrapper.getInventoryPlayer().player; + } + if (slot.getItemHandler() instanceof PlayerArmorInvWrapper wrapper) { + return wrapper.getInventoryPlayer().player; + } + return null; + } + + public boolean isCanTake() { + return canTake; + } + + public boolean isCanPut() { + return canPut; + } + + public boolean isCanDragInto() { + return canDragInto; + } + + public Predicate getFilter() { + return filter; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/textfield/BaseTextFieldWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/textfield/BaseTextFieldWidget.java index 38fdbab99..0142e7819 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/textfield/BaseTextFieldWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/textfield/BaseTextFieldWidget.java @@ -376,4 +376,36 @@ public static char getDecimalSeparator() { public static char getGroupSeparator() { return format.getDecimalFormatSymbols().getGroupingSeparator(); } + + public Alignment getTextAlignment() { + return textAlignment; + } + + public int getScrollOffset() { + return scrollOffset; + } + + public float getScale() { + return scale; + } + + public boolean isFocusOnGuiOpen() { + return focusOnGuiOpen; + } + + public Integer getTextColor() { + return textColor; + } + + public Integer getMarkedColor() { + return markedColor; + } + + public String getHintText() { + return hintText; + } + + public Integer getHintTextColor() { + return hintTextColor; + } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java index b43020534..a718735e6 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java @@ -417,4 +417,24 @@ public TextFieldWidget addTooltipStringLines(Iterable lines) { tooltipOverride = true; return super.addTooltipStringLines(lines); } + + public Function getValidator() { + return validator; + } + + public boolean isNumbers() { + return numbers; + } + + public double getDefaultNumber() { + return defaultNumber; + } + + public boolean isTooltipOverride() { + return tooltipOverride; + } + + public boolean isAcceptsExpression() { + return acceptsExpression; + } }