From 8c3df228ff7df1e0bb27bec0439e8c314a8976cd Mon Sep 17 00:00:00 2001 From: brachy84 <45517902+brachy84@users.noreply.github.com> Date: Thu, 21 Sep 2023 20:36:03 +0200 Subject: [PATCH] Resizing fixes (#31) * show relative pos in debug screen * replace linked lists with fastutil * push everything * fixed padding & margin * clean up & move some classes * fix layout widget margin & padding --- .../{widget/sizer => api}/GuiAxis.java | 2 +- .../sizer => api/layout}/IResizeable.java | 24 ++- .../modularui/api/widget/IGuiElement.java | 3 +- .../modularui/api/widget/IWidget.java | 2 +- .../modularui/drawable/GuiTextures.java | 2 +- .../modularui/drawable/TabTexture.java | 2 +- .../modularui/screen/GuiScreenWrapper.java | 2 +- .../modularui/screen/ModularPanel.java | 12 +- .../modularui/screen/WindowManager.java | 8 +- .../modularui/screen/viewport/GuiContext.java | 2 +- .../modularui/test/EventHandler.java | 6 +- .../modularui/test/ResizerTest.java | 28 +++ .../cleanroommc/modularui/test/TestTile.java | 16 +- .../modularui/utils/MathUtils.java | 6 + .../modularui/utils/ObjectList.java | 159 ++++++++++++++++++ .../modularui/utils/ScrollArea.java | 2 +- .../modularui/utils/ScrollDirection.java | 2 +- .../modularui/widget/EmptyWidget.java | 2 +- .../cleanroommc/modularui/widget/Widget.java | 2 +- .../modularui/widget/WidgetTree.java | 18 +- .../modularui/widget/sizer/Area.java | 49 ++---- .../modularui/widget/sizer/Box.java | 11 +- .../widget/sizer/DimensionSizer.java | 60 +++++-- .../modularui/widget/sizer/Flex.java | 79 +++++++-- .../modularui/widget/sizer/IUnResizeable.java | 57 +++++++ .../modularui/widget/sizer/Unit.java | 2 + .../modularui/widgets/ListWidget.java | 19 +-- .../modularui/widgets/PagedWidget.java | 10 -- .../modularui/widgets/SliderWidget.java | 2 +- .../modularui/widgets/TextWidget.java | 4 +- .../modularui/widgets/layout/Column.java | 31 ++-- .../widgets/layout/OrganizedPanel.java | 5 + .../modularui/widgets/layout/Row.java | 24 ++- 33 files changed, 504 insertions(+), 149 deletions(-) rename src/main/java/com/cleanroommc/modularui/{widget/sizer => api}/GuiAxis.java (83%) rename src/main/java/com/cleanroommc/modularui/{widget/sizer => api/layout}/IResizeable.java (74%) create mode 100644 src/main/java/com/cleanroommc/modularui/test/ResizerTest.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/ObjectList.java create mode 100644 src/main/java/com/cleanroommc/modularui/widget/sizer/IUnResizeable.java diff --git a/src/main/java/com/cleanroommc/modularui/widget/sizer/GuiAxis.java b/src/main/java/com/cleanroommc/modularui/api/GuiAxis.java similarity index 83% rename from src/main/java/com/cleanroommc/modularui/widget/sizer/GuiAxis.java rename to src/main/java/com/cleanroommc/modularui/api/GuiAxis.java index 6823099c..782408fb 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/GuiAxis.java +++ b/src/main/java/com/cleanroommc/modularui/api/GuiAxis.java @@ -1,4 +1,4 @@ -package com.cleanroommc.modularui.widget.sizer; +package com.cleanroommc.modularui.api; public enum GuiAxis { diff --git a/src/main/java/com/cleanroommc/modularui/widget/sizer/IResizeable.java b/src/main/java/com/cleanroommc/modularui/api/layout/IResizeable.java similarity index 74% rename from src/main/java/com/cleanroommc/modularui/widget/sizer/IResizeable.java rename to src/main/java/com/cleanroommc/modularui/api/layout/IResizeable.java index c86407ce..d41c0a29 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/IResizeable.java +++ b/src/main/java/com/cleanroommc/modularui/api/layout/IResizeable.java @@ -1,7 +1,12 @@ -package com.cleanroommc.modularui.widget.sizer; +package com.cleanroommc.modularui.api.layout; +import com.cleanroommc.modularui.api.GuiAxis; import com.cleanroommc.modularui.api.widget.IGuiElement; +import com.cleanroommc.modularui.widget.sizer.Area; +/** + * An interface that handles resizing of widgets. + */ public interface IResizeable { void initResizing(); @@ -60,4 +65,21 @@ default void setWidthResized(boolean v) { default void setHeightResized(boolean v) { setResized(isXCalculated(), isYCalculated(), isWidthCalculated(), v); } + + default void setResized(boolean b) { + setResized(b, b, b, b); + } + + void setXMarginPaddingApplied(boolean b); + + void setYMarginPaddingApplied(boolean b); + + default void setMarginPaddingApplied(boolean b) { + setXMarginPaddingApplied(b); + setYMarginPaddingApplied(b); + } + + boolean isXMarginPaddingApplied(); + + boolean isYMarginPaddingApplied(); } diff --git a/src/main/java/com/cleanroommc/modularui/api/widget/IGuiElement.java b/src/main/java/com/cleanroommc/modularui/api/widget/IGuiElement.java index d1e8bcfe..db90ba40 100644 --- a/src/main/java/com/cleanroommc/modularui/api/widget/IGuiElement.java +++ b/src/main/java/com/cleanroommc/modularui/api/widget/IGuiElement.java @@ -1,12 +1,11 @@ package com.cleanroommc.modularui.api.widget; import com.cleanroommc.modularui.api.ITheme; +import com.cleanroommc.modularui.api.layout.IResizeable; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.ModularScreen; import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.widget.sizer.Area; -import com.cleanroommc.modularui.widget.sizer.IResizeable; -import org.jetbrains.annotations.ApiStatus; /** * Base interface for gui elements. For example widgets. diff --git a/src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java b/src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java index dd802151..98616067 100644 --- a/src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java +++ b/src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java @@ -1,6 +1,7 @@ package com.cleanroommc.modularui.api.widget; import com.cleanroommc.modularui.api.ITheme; +import com.cleanroommc.modularui.api.layout.IResizeable; import com.cleanroommc.modularui.api.layout.IViewportStack; import com.cleanroommc.modularui.drawable.Stencil; import com.cleanroommc.modularui.screen.ModularPanel; @@ -8,7 +9,6 @@ import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.widget.sizer.Area; import com.cleanroommc.modularui.widget.sizer.Flex; -import com.cleanroommc.modularui.widget.sizer.IResizeable; import org.jetbrains.annotations.NotNull; import java.util.Collections; diff --git a/src/main/java/com/cleanroommc/modularui/drawable/GuiTextures.java b/src/main/java/com/cleanroommc/modularui/drawable/GuiTextures.java index d863f5ae..cdbd98fd 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/GuiTextures.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/GuiTextures.java @@ -1,7 +1,7 @@ package com.cleanroommc.modularui.drawable; import com.cleanroommc.modularui.ModularUI; -import com.cleanroommc.modularui.widget.sizer.GuiAxis; +import com.cleanroommc.modularui.api.GuiAxis; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.util.ResourceLocation; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/com/cleanroommc/modularui/drawable/TabTexture.java b/src/main/java/com/cleanroommc/modularui/drawable/TabTexture.java index bfba9b05..e93ab1d3 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/TabTexture.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/TabTexture.java @@ -1,6 +1,6 @@ package com.cleanroommc.modularui.drawable; -import com.cleanroommc.modularui.widget.sizer.GuiAxis; +import com.cleanroommc.modularui.api.GuiAxis; import java.util.Objects; diff --git a/src/main/java/com/cleanroommc/modularui/screen/GuiScreenWrapper.java b/src/main/java/com/cleanroommc/modularui/screen/GuiScreenWrapper.java index 4ff6775e..37c11a52 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/GuiScreenWrapper.java +++ b/src/main/java/com/cleanroommc/modularui/screen/GuiScreenWrapper.java @@ -243,7 +243,7 @@ public void drawDebugScreen() { } GlStateManager.popMatrix(); locatedHovered.unapplyMatrix(context); - GuiDraw.drawText("Pos: " + area.x + ", " + area.y, 5, lineY, 1, color, false); + GuiDraw.drawText("Pos: " + area.x + ", " + area.y + " Rel: " + area.rx + ", " + area.ry, 5, lineY, 1, color, false); lineY -= 11; GuiDraw.drawText("Size: " + area.width + ", " + area.height, 5, lineY, 1, color, false); lineY -= 11; diff --git a/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java b/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java index 7c0989f0..58664e76 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java +++ b/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java @@ -16,6 +16,7 @@ import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.utils.Animator; import com.cleanroommc.modularui.utils.Interpolation; +import com.cleanroommc.modularui.utils.ObjectList; import com.cleanroommc.modularui.widget.ParentWidget; import com.cleanroommc.modularui.widget.sizer.Area; import com.cleanroommc.modularui.widgets.SlotGroupWidget; @@ -25,9 +26,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; import java.util.Objects; import java.util.function.Function; @@ -53,8 +51,8 @@ public static ModularPanel defaultPanel(@NotNull String name, int width, int hei @NotNull private final String name; private ModularScreen screen; - private final LinkedList hovering = new LinkedList<>(); - private final List acceptedInteractions = new ArrayList<>(); + private final ObjectList hovering = ObjectList.create(); + private final ObjectList acceptedInteractions = ObjectList.create(); private boolean isMouseButtonHeld = false, isKeyHeld = false; @Nullable private LocatedWidget lastPressed; @@ -157,7 +155,7 @@ public void add(IWidget widget, TransformationMatrix transformationMatrix) { @Override public IWidget peek() { - return isEmpty() ? null : ModularPanel.this.hovering.peekFirst().getElement(); + return isEmpty() ? null : ModularPanel.this.hovering.getFirst().getElement(); } @Override @@ -511,7 +509,7 @@ public ModularScreen getScreen() { } @NotNull - public LinkedList getHovering() { + public ObjectList getHovering() { return this.hovering; } diff --git a/src/main/java/com/cleanroommc/modularui/screen/WindowManager.java b/src/main/java/com/cleanroommc/modularui/screen/WindowManager.java index ac099279..61f7741c 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/WindowManager.java +++ b/src/main/java/com/cleanroommc/modularui/screen/WindowManager.java @@ -2,13 +2,17 @@ import com.cleanroommc.modularui.api.widget.IWidget; import com.cleanroommc.modularui.screen.viewport.LocatedWidget; +import com.cleanroommc.modularui.utils.ObjectList; import com.cleanroommc.modularui.utils.ReverseIterable; import com.cleanroommc.modularui.widget.WidgetTree; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnmodifiableView; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; public class WindowManager { @@ -21,7 +25,7 @@ public class WindowManager { /** * List of all open panels from top to bottom. */ - private final LinkedList panels = new LinkedList<>(); + private final ObjectList panels = ObjectList.create(); private final List panelsView = Collections.unmodifiableList(this.panels); private final ReverseIterable reversePanels = new ReverseIterable<>(this.panelsView); private final List queueOpenPanels = new ArrayList<>(); diff --git a/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiContext.java b/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiContext.java index c5ad60b1..dcd71f96 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiContext.java +++ b/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiContext.java @@ -1,12 +1,12 @@ package com.cleanroommc.modularui.screen.viewport; import com.cleanroommc.modularui.ModularUI; +import com.cleanroommc.modularui.api.GuiAxis; import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.layout.IViewport; import com.cleanroommc.modularui.api.widget.*; import com.cleanroommc.modularui.core.mixin.GuiContainerAccessor; import com.cleanroommc.modularui.screen.*; -import com.cleanroommc.modularui.widget.sizer.GuiAxis; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import org.jetbrains.annotations.ApiStatus; diff --git a/src/main/java/com/cleanroommc/modularui/test/EventHandler.java b/src/main/java/com/cleanroommc/modularui/test/EventHandler.java index 4d35c21a..71e6481d 100644 --- a/src/main/java/com/cleanroommc/modularui/test/EventHandler.java +++ b/src/main/java/com/cleanroommc/modularui/test/EventHandler.java @@ -2,6 +2,7 @@ import com.cleanroommc.modularui.holoui.HoloScreenEntity; import com.cleanroommc.modularui.holoui.HoloUI; +import com.cleanroommc.modularui.manager.GuiManager; import net.minecraft.client.Minecraft; import net.minecraft.init.Items; import net.minecraftforge.event.entity.player.PlayerInteractEvent; @@ -13,10 +14,11 @@ public class EventHandler { public static void onItemUse(PlayerInteractEvent.RightClickItem event) { if (event.getEntityPlayer().getEntityWorld().isRemote && event.getItemStack().getItem() == Items.DIAMOND) { //GuiManager.openClientUI(Minecraft.getMinecraft().player, new TestGui()); - HoloUI.builder() + /*HoloUI.builder() .inFrontOf(Minecraft.getMinecraft().player, 5, false) .screenScale(0.5f) - .open(new TestGui()); + .open(new TestGui());*/ + GuiManager.openClientUI(Minecraft.getMinecraft().player, new ResizerTest()); } } } diff --git a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java new file mode 100644 index 00000000..4f3fd3cc --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java @@ -0,0 +1,28 @@ +package com.cleanroommc.modularui.test; + +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.screen.ModularScreen; +import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.widget.ParentWidget; +import com.cleanroommc.modularui.widgets.ButtonWidget; +import org.jetbrains.annotations.NotNull; + +public class ResizerTest extends ModularScreen { + + @Override + public @NotNull ModularPanel buildUI(GuiContext context) { + return ModularPanel.defaultPanel("main") + .coverChildrenWidth() + .height(40) + .child(new ParentWidget<>() + .coverChildren() + .padding(5) + .child(IKey.str("A decently sized string!").asWidget().debugName("label")) + .child(new ButtonWidget<>() + .size(10) + .top(5) + .right(5) + .debugName("button"))); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/test/TestTile.java b/src/main/java/com/cleanroommc/modularui/test/TestTile.java index 49cd4281..8c3b72ce 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestTile.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestTile.java @@ -72,7 +72,7 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage panel.flex() // returns object which is responsible for sizing .size(176, 220) // set a static size for the main panel .align(Alignment.Center); // center the panel in the screen - panel.bindPlayerInventory() + panel .child(new Row() .debugName("Tab row") .coverChildren() @@ -90,12 +90,12 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage .addPage(new ParentWidget<>() .debugName("page 1 parent") .sizeRel(1f, 1f) - .child(SlotGroupWidget.playerInventory()) + .padding(7) .child(new Row() .debugName("buttons, slots and more tests") .height(137) .coverChildrenWidth() - .padding(7) + //.padding(7) .child(new Column() .debugName("buttons and slots test") .coverChildren() @@ -169,9 +169,10 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage ))) .addPage(new Column() .debugName("Slots test page") - //.coverChildren() + .coverChildren() .padding(7) - .child(SlotGroupWidget.playerInventory()) + .alignX(0.5f) + //.child(SlotGroupWidget.playerInventory().left(0)) .child(SlotGroupWidget.builder() .matrix("III", "III", "III") .key('I', index -> { @@ -209,7 +210,7 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage .debugName("page 3 parent") .sizeRel(1f, 1f) .padding(7) - .child(SlotGroupWidget.playerInventory()) + //.child(SlotGroupWidget.playerInventory()) .child(new SliderWidget() .widthRel(1f).height(16) .top(7) @@ -271,7 +272,8 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage .texture(GuiTextures.CHECK_BOX) .size(14, 14)) .child(IKey.lang("bogosort.gui.enabled").asWidget() - .height(14)))))); + .height(14)))))) + .bindPlayerInventory(); /*panel.child(new ButtonWidget<>() .flex(flex -> flex.size(60, 20) .top(7) diff --git a/src/main/java/com/cleanroommc/modularui/utils/MathUtils.java b/src/main/java/com/cleanroommc/modularui/utils/MathUtils.java index 8d302c78..95be617f 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/MathUtils.java +++ b/src/main/java/com/cleanroommc/modularui/utils/MathUtils.java @@ -44,6 +44,9 @@ public static int gridRows(int count, int size, int width) { } public static int min(int... values) { + if (values == null || values.length == 0) throw new IllegalArgumentException(); + if (values.length == 1) return values[0]; + if (values.length == 2) return Math.min(values[0], values[1]); int min = Integer.MAX_VALUE; for (int i : values) { if (i < min) { @@ -54,6 +57,9 @@ public static int min(int... values) { } public static int max(int... values) { + if (values == null || values.length == 0) throw new IllegalArgumentException(); + if (values.length == 1) return values[0]; + if (values.length == 2) return Math.max(values[0], values[1]); int max = Integer.MIN_VALUE; for (int i : values) { if (i > max) { diff --git a/src/main/java/com/cleanroommc/modularui/utils/ObjectList.java b/src/main/java/com/cleanroommc/modularui/utils/ObjectList.java new file mode 100644 index 00000000..62e7a4f2 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/ObjectList.java @@ -0,0 +1,159 @@ +package com.cleanroommc.modularui.utils; + +import it.unimi.dsi.fastutil.objects.ObjectCollection; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Iterator; + +public interface ObjectList extends it.unimi.dsi.fastutil.objects.ObjectList { + + static ObjectArrayList create() { + return new ObjectArrayList<>(); + } + + static ObjectArrayList create(int size) { + return new ObjectArrayList<>(size); + } + + static ObjectArrayList of(Collection c) { + return new ObjectArrayList<>(c); + } + + static ObjectArrayList of(ObjectCollection c) { + return new ObjectArrayList<>(c); + } + + static ObjectArrayList of(it.unimi.dsi.fastutil.objects.ObjectList l) { + return new ObjectArrayList<>(l); + } + + static ObjectArrayList of(V[] a) { + return new ObjectArrayList<>(a); + } + + static ObjectArrayList of(V[] a, int offset, int length) { + return new ObjectArrayList<>(a, offset, length); + } + + static ObjectArrayList of(Iterator i) { + return new ObjectArrayList<>(i); + } + + static ObjectArrayList of(ObjectIterator i) { + return new ObjectArrayList<>(i); + } + + void addFirst(V v); + + void addLast(V v); + + V getFirst(); + + V getLast(); + + V removeFirst(); + + V removeLast(); + + @Nullable + V peekFirst(); + + @Nullable + V pollFirst(); + + @Nullable + V peekLast(); + + @Nullable + V pollLast(); + + class ObjectArrayList extends it.unimi.dsi.fastutil.objects.ObjectArrayList implements ObjectList { + + public ObjectArrayList(int capacity) { + super(capacity); + } + + public ObjectArrayList() { + } + + public ObjectArrayList(Collection c) { + super(c); + } + + public ObjectArrayList(ObjectCollection c) { + super(c); + } + + public ObjectArrayList(it.unimi.dsi.fastutil.objects.ObjectList l) { + super(l); + } + + public ObjectArrayList(V[] a) { + super(a); + } + + public ObjectArrayList(V[] a, int offset, int length) { + super(a, offset, length); + } + + public ObjectArrayList(Iterator i) { + super(i); + } + + public ObjectArrayList(ObjectIterator i) { + super(i); + } + + @Override + public void addFirst(V v) { + add(0, v); + } + + @Override + public void addLast(V v) { + add(v); + } + + @Override + public V getFirst() { + return get(0); + } + + @Override + public V getLast() { + return get(size() - 1); + } + + @Override + public V removeFirst() { + return remove(0); + } + + @Override + public V removeLast() { + return remove(size() - 1); + } + + @Override + public V peekFirst() { + return isEmpty() ? null : getFirst(); + } + + @Override + public V pollFirst() { + return isEmpty() ? null : removeFirst(); + } + + @Override + public V peekLast() { + return isEmpty() ? null : getLast(); + } + + @Override + public V pollLast() { + return isEmpty() ? null : removeLast(); + } + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/ScrollArea.java b/src/main/java/com/cleanroommc/modularui/utils/ScrollArea.java index 229a2502..77ae069b 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/ScrollArea.java +++ b/src/main/java/com/cleanroommc/modularui/utils/ScrollArea.java @@ -1,8 +1,8 @@ package com.cleanroommc.modularui.utils; +import com.cleanroommc.modularui.api.GuiAxis; import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.widget.sizer.Area; -import com.cleanroommc.modularui.widget.sizer.GuiAxis; import net.minecraft.client.gui.GuiScreen; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; diff --git a/src/main/java/com/cleanroommc/modularui/utils/ScrollDirection.java b/src/main/java/com/cleanroommc/modularui/utils/ScrollDirection.java index 776f0411..b795c72e 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/ScrollDirection.java +++ b/src/main/java/com/cleanroommc/modularui/utils/ScrollDirection.java @@ -1,7 +1,7 @@ package com.cleanroommc.modularui.utils; +import com.cleanroommc.modularui.api.GuiAxis; import com.cleanroommc.modularui.widget.sizer.Area; -import com.cleanroommc.modularui.widget.sizer.GuiAxis; /** * Scroll direction diff --git a/src/main/java/com/cleanroommc/modularui/widget/EmptyWidget.java b/src/main/java/com/cleanroommc/modularui/widget/EmptyWidget.java index 8acd4d61..f7215432 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/EmptyWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widget/EmptyWidget.java @@ -1,5 +1,6 @@ package com.cleanroommc.modularui.widget; +import com.cleanroommc.modularui.api.layout.IResizeable; import com.cleanroommc.modularui.api.layout.IViewportStack; import com.cleanroommc.modularui.api.widget.IWidget; import com.cleanroommc.modularui.screen.ModularPanel; @@ -7,7 +8,6 @@ import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.widget.sizer.Area; import com.cleanroommc.modularui.widget.sizer.Flex; -import com.cleanroommc.modularui.widget.sizer.IResizeable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/com/cleanroommc/modularui/widget/Widget.java b/src/main/java/com/cleanroommc/modularui/widget/Widget.java index 0cb43f14..80326bda 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/Widget.java +++ b/src/main/java/com/cleanroommc/modularui/widget/Widget.java @@ -3,6 +3,7 @@ import com.cleanroommc.modularui.ModularUIConfig; import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.api.layout.IResizeable; import com.cleanroommc.modularui.api.value.IValue; import com.cleanroommc.modularui.api.widget.*; import com.cleanroommc.modularui.drawable.DrawableArray; @@ -18,7 +19,6 @@ import com.cleanroommc.modularui.widget.sizer.Area; import com.cleanroommc.modularui.widget.sizer.Box; import com.cleanroommc.modularui.widget.sizer.Flex; -import com.cleanroommc.modularui.widget.sizer.IResizeable; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java b/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java index 20d0ac23..65f93d64 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java +++ b/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java @@ -1,19 +1,19 @@ package com.cleanroommc.modularui.widget; import com.cleanroommc.modularui.api.layout.ILayoutWidget; +import com.cleanroommc.modularui.api.layout.IResizeable; import com.cleanroommc.modularui.api.layout.IViewport; import com.cleanroommc.modularui.api.widget.IGuiElement; import com.cleanroommc.modularui.api.widget.ISynced; import com.cleanroommc.modularui.api.widget.IWidget; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.utils.ObjectList; import com.cleanroommc.modularui.value.sync.GuiSyncManager; -import com.cleanroommc.modularui.widget.sizer.IResizeable; import net.minecraft.client.renderer.GlStateManager; import org.jetbrains.annotations.ApiStatus; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; @@ -33,10 +33,10 @@ public static List getAllChildrenByLayer(IWidget parent) { public static List getAllChildrenByLayer(IWidget parent, boolean includeSelf) { List children = new ArrayList<>(); if (includeSelf) children.add(parent); - LinkedList parents = new LinkedList<>(); + ObjectList parents = ObjectList.create(); parents.add(parent); while (!parents.isEmpty()) { - for (IWidget child : parents.pollFirst().getChildren()) { + for (IWidget child : parents.removeFirst().getChildren()) { if (!child.getChildren().isEmpty()) { parents.add(child); } @@ -52,10 +52,10 @@ public static boolean foreachChildByLayer(IWidget parent, Predicate con public static boolean foreachChildByLayer(IWidget parent, Predicate consumer, boolean includeSelf) { if (includeSelf && !consumer.test(parent)) return false; - LinkedList parents = new LinkedList<>(); + ObjectList parents = ObjectList.create(); parents.add(parent); while (!parents.isEmpty()) { - for (IWidget child : parents.pollFirst().getChildren()) { + for (IWidget child : parents.removeFirst().getChildren()) { if (child.hasChildren()) { parents.addLast(child); } @@ -67,10 +67,10 @@ public static boolean foreachChildByLayer(IWidget parent, Predicate con public static boolean foreachChildByLayer2(IWidget parent, Predicate consumer, boolean includeSelf) { if (includeSelf && !consumer.test(parent)) return false; - LinkedList parents = new LinkedList<>(); + ObjectList parents = ObjectList.create(); parents.add(parent); while (!parents.isEmpty()) { - for (IWidget child : parents.pollFirst().getChildren()) { + for (IWidget child : parents.removeFirst().getChildren()) { if (!consumer.test(child)) return false; if (child.hasChildren()) { @@ -209,6 +209,7 @@ public static void onFrameUpdate(IWidget parent) { } public static void resize(IWidget parent) { + // TODO check if widget has a parent which depends on its children // resize each widget and calculate their relative pos if (!resizeWidget(parent, true) && !resizeWidget(parent, false)) { throw new IllegalStateException("Failed to resize widgets"); @@ -217,7 +218,6 @@ public static void resize(IWidget parent) { applyPos(parent); WidgetTree.foreachChildByLayer(parent, child -> { child.postResize(); - //ModularUI.LOGGER.info("{} at {}", child, child.getArea()); return true; }, true); } diff --git a/src/main/java/com/cleanroommc/modularui/widget/sizer/Area.java b/src/main/java/com/cleanroommc/modularui/widget/sizer/Area.java index 3b5dafd6..4b88b18e 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/Area.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/Area.java @@ -1,5 +1,6 @@ package com.cleanroommc.modularui.widget.sizer; +import com.cleanroommc.modularui.api.GuiAxis; import com.cleanroommc.modularui.api.layout.IViewportStack; import com.cleanroommc.modularui.api.widget.IGuiElement; import com.cleanroommc.modularui.utils.MathUtils; @@ -7,12 +8,25 @@ import java.awt.*; import java.awt.geom.Rectangle2D; -public class Area extends Rectangle implements IResizeable { +/** + * A rectangular widget area, composed of a position and a size. + * Also has fields for a relative position, a layer and margin & padding. + */ +public class Area extends Rectangle implements IUnResizeable { public static final Area SHARED = new Area(); + /** + * relative position (in most cases the direct parent) + */ public int rx, ry; + /** + * each panel has its own layer + */ private byte panelLayer = 0; + /** + * the widget layer within this panel + */ private int z; private final Box margin = new Box(); private final Box padding = new Box(); @@ -327,50 +341,17 @@ public Box getPadding() { return this.padding; } - @Override - public void initResizing() { - } - @Override public boolean resize(IGuiElement guiElement) { guiElement.getArea().set(this); return true; } - @Override - public boolean postResize(IGuiElement guiElement) { - return true; - } - @Override public Area getArea() { return this; } - @Override - public boolean isXCalculated() { - return true; - } - - @Override - public boolean isYCalculated() { - return true; - } - - @Override - public boolean isWidthCalculated() { - return true; - } - - @Override - public boolean isHeightCalculated() { - return true; - } - - @Override - public void setResized(boolean x, boolean y, boolean w, boolean h) { - } - public Area createCopy() { return new Area(this); } diff --git a/src/main/java/com/cleanroommc/modularui/widget/sizer/Box.java b/src/main/java/com/cleanroommc/modularui/widget/sizer/Box.java index bc58ce72..6f615b6a 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/Box.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/Box.java @@ -1,5 +1,11 @@ package com.cleanroommc.modularui.widget.sizer; +import com.cleanroommc.modularui.api.GuiAxis; + +/** + * A box with four edges. + * Used for margins and paddings. + */ public class Box { public static final Box SHARED = new Box(); @@ -22,31 +28,26 @@ public Box all(int left, int right, int top, int bottom) { this.top = top; this.right = right; this.bottom = bottom; - return this; } public Box left(int left) { this.left = left; - return this; } public Box top(int top) { this.top = top; - return this; } public Box right(int right) { this.right = right; - return this; } public Box bottom(int bottom) { this.bottom = bottom; - return this; } 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 4a9ca491..49978021 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/DimensionSizer.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/DimensionSizer.java @@ -1,7 +1,16 @@ package com.cleanroommc.modularui.widget.sizer; +import com.cleanroommc.modularui.api.GuiAxis; +import com.cleanroommc.modularui.api.layout.IResizeable; +import org.jetbrains.annotations.ApiStatus; + import java.util.function.IntSupplier; +/** + * Handles calculating size and position in one dimension (x or y). + * Two of these can fully calculate a widget size and pos. + */ +@ApiStatus.Internal public class DimensionSizer { private final GuiAxis axis; @@ -14,6 +23,7 @@ public class DimensionSizer { private boolean defaultMode = false; private boolean posCalculated = false, sizeCalculated = false; + private boolean marginPaddingApplied = false; public DimensionSizer(GuiAxis axis) { this.axis = axis; @@ -105,17 +115,27 @@ public void setResized(boolean pos, boolean size) { this.sizeCalculated = size; } + public boolean isMarginPaddingApplied() { + return marginPaddingApplied; + } + + public void setMarginPaddingApplied(boolean marginPaddingApplied) { + this.marginPaddingApplied = marginPaddingApplied; + } + private boolean needsSize(Unit unit) { return unit.isRelative() && unit.getAnchor() != 0; } public void apply(Area area, IResizeable relativeTo, IntSupplier defaultSize) { + // is already calculated if (this.sizeCalculated && this.posCalculated) return; int p, s; int parentSize = relativeTo.getArea().getSize(this.axis); boolean calcParent = relativeTo.isSizeCalculated(this.axis); if (this.sizeCalculated && !this.posCalculated) { + // size was calculated before s = area.getSize(this.axis); if (this.start != null) { p = calcPoint(this.start, s, parentSize, calcParent); @@ -126,6 +146,7 @@ public void apply(Area area, IResizeable relativeTo, IntSupplier defaultSize) { throw new IllegalStateException(); } } else if (!this.sizeCalculated && this.posCalculated) { + // pos was calculated before p = area.getRelativePoint(this.axis); if (this.size != null) { s = this.coverChildren ? 18 : calcSize(this.size, parentSize, calcParent); @@ -179,28 +200,20 @@ public void apply(Area area, IResizeable relativeTo, IntSupplier defaultSize) { } } - // apply padding and margin - if (this.posCalculated && this.sizeCalculated) { - Box.SHARED.all(0); + // apply padding and margin to size + if (this.sizeCalculated && calcParent && ((this.size != null && this.size.isRelative()) || + (this.start != null && this.end != null && (this.start.isRelative() || this.end.isRelative())))) { Box padding = relativeTo.getArea().getPadding(); Box margin = area.getMargin(); - - if (!calcParent || (this.size != null && !this.size.isRelative())) { - area.setRelativePoint(this.axis, p); - } else { - area.setRelativePoint(this.axis, Math.max(p, padding.getStart(this.axis) + margin.getStart(this.axis))); - s = Math.min(s, parentSize - padding.getTotal(this.axis) - margin.getTotal(this.axis)); - } - - } else { - area.setRelativePoint(this.axis, p); + s = Math.min(s, parentSize - padding.getTotal(this.axis) - margin.getTotal(this.axis)); } - p += relativeTo.getArea().x; - area.setPoint(this.axis, p); // temporary + area.setRelativePoint(this.axis, p); + area.setPoint(this.axis, p + relativeTo.getArea().x); // temporary area.setSize(this.axis, s); } public int postApply(Area area, Area 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; @@ -213,7 +226,7 @@ public int postApply(Area area, Area relativeTo, int p0, int p1) { p = calcPoint(this.end, s, relativeTo.getSize(this.axis), true); p = relativeTo.getSize(this.axis) - p - s; } else { - p = area.getRelativePoint(this.axis) + p0 + area.getMargin().getStart(this.axis); + p = area.getRelativePoint(this.axis) + p0/* + area.getMargin().getStart(this.axis)*/; if (!this.cancelAutoMovement) { moveAmount = -p0; } @@ -224,6 +237,17 @@ public int postApply(Area area, Area relativeTo, int p0, int p1) { return moveAmount; } + public void applyMarginAndPaddingToPos(Area area, Area relativeTo) { + // apply self margin and parent padding if not done yet + if (isMarginPaddingApplied()) return; + setMarginPaddingApplied(true); + int o = area.getMargin().getStart(this.axis) + relativeTo.getPadding().getStart(this.axis); + if (o == 0) return; + if (this.start != null && !this.start.isRelative()) return; + if (this.end != null && !this.end.isRelative() && (this.size == null || !this.size.isRelative())) return; + area.setRelativePoint(this.axis, area.getRelativePoint(this.axis) + o); + } + private int calcSize(Unit s, int parentSize, boolean parentSizeCalculated) { if (this.coverChildren) return 18; float val = s.getValue(); @@ -252,6 +276,10 @@ public int calcPoint(Unit p, int width, int parentSize, boolean parentSizeCalcul return (int) val; } + // the following methods try to find a unit from p1 and p2 and apply it to start, end or size + // if one of the units was applied in default mode, they can be overwritten + // if p1 and p2 are already in use and none is in default mode, an exception is thrown + protected Unit getStart() { if (this.start == null) { Unit u = null; diff --git a/src/main/java/com/cleanroommc/modularui/widget/sizer/Flex.java b/src/main/java/com/cleanroommc/modularui/widget/sizer/Flex.java index 1fa0dda3..01d8dbb8 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/Flex.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/Flex.java @@ -1,7 +1,9 @@ package com.cleanroommc.modularui.widget.sizer; import com.cleanroommc.modularui.GuiError; +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.IGuiElement; import com.cleanroommc.modularui.api.widget.IPositioned; import com.cleanroommc.modularui.api.widget.IVanillaSlot; @@ -13,6 +15,9 @@ import java.util.List; import java.util.function.DoubleSupplier; +/** + * This class handles resizing and positioning of widgets. + */ public class Flex implements IResizeable, IPositioned { private final DimensionSizer x = new DimensionSizer(GuiAxis.X); @@ -286,8 +291,8 @@ public boolean hasFixedSize() { @Override public void initResizing() { - this.x.setResized(false); - this.y.setResized(false); + setMarginPaddingApplied(false); + setResized(false); } @Override @@ -296,6 +301,26 @@ public void setResized(boolean x, boolean y, boolean w, boolean h) { this.y.setResized(y, h); } + @Override + public void setXMarginPaddingApplied(boolean b) { + this.x.setMarginPaddingApplied(b); + } + + @Override + public void setYMarginPaddingApplied(boolean b) { + this.y.setMarginPaddingApplied(b); + } + + @Override + public boolean isXMarginPaddingApplied() { + return this.x.isMarginPaddingApplied(); + } + + @Override + public boolean isYMarginPaddingApplied() { + return this.y.isMarginPaddingApplied(); + } + @Override public boolean resize(IGuiElement guiElement) { IResizeable relativeTo = getRelativeTo(); @@ -317,9 +342,13 @@ public boolean resize(IGuiElement guiElement) { @Override public boolean postResize(IGuiElement guiElement) { if (this.parent instanceof ILayoutWidget) { + // layout widgets handle widget layout's themselves, so we only need to fit the right and bottom border coverChildrenForLayout(); return true; } + // 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) // not skipped children are now calculated and now this area can be calculated if it requires childrens area if (this.x.dependsOnChildren() || this.y.dependsOnChildren()) { @@ -336,8 +365,10 @@ public boolean postResize(IGuiElement guiElement) { IResizeable resizeable = child.resizer(); Area area = child.getArea(); if (this.x.dependsOnChildren() && resizeable.isWidthCalculated()) { + // minimum width this widget requests w = Math.max(w, area.requestedWidth() + padding.horizontal()); if (resizeable.isXCalculated()) { + // if pos is calculated use that x0 = Math.min(x0, area.rx - padding.left - margin.left); x1 = Math.max(x1, area.rx + area.width + padding.right + margin.right); } @@ -352,24 +383,30 @@ public boolean postResize(IGuiElement guiElement) { } if (x1 == Integer.MIN_VALUE) x1 = 0; if (y1 == Integer.MIN_VALUE) y1 = 0; - if (x0 == Integer.MAX_VALUE) y0 = 0; + if (x0 == Integer.MAX_VALUE) x0 = 0; if (y0 == Integer.MAX_VALUE) y0 = 0; - if (w > x1 - x0) x1 = x0 + w; + if (w > x1 - x0) + x1 = x0 + w; // we found at least one widget which was wider than what was calculated by start and end pos if (h > y1 - y0) y1 = y0 + h; // now calculate new x, y, width and height based on the childrens area Area relativeTo = getRelativeTo().getArea(); if (this.x.dependsOnChildren()) { + // apply the size to this widget + // the return value is the amount of pixels we need to move the children moveChildrenX = this.x.postApply(this.parent.getArea(), relativeTo, x0, x1); } if (this.y.dependsOnChildren()) { moveChildrenY = this.y.postApply(this.parent.getArea(), relativeTo, y0, y1); } - for (IWidget widget : children) { - Area area = widget.getArea(); - IResizeable resizeable = widget.resizer(); - if (resizeable.isXCalculated()) area.rx += moveChildrenX; - if (resizeable.isYCalculated()) area.ry += moveChildrenY; + // 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 widget : children) { + Area area = widget.getArea(); + IResizeable resizeable = widget.resizer(); + if (resizeable.isXCalculated()) area.rx += moveChildrenX; + if (resizeable.isYCalculated()) area.ry += moveChildrenY; + } } } } @@ -382,18 +419,30 @@ private void coverChildrenForLayout() { if (!children.isEmpty()) { Box padding = this.parent.getArea().getPadding(); // first calculate the area the children span - int x0 = Integer.MAX_VALUE, x1 = Integer.MIN_VALUE, y0 = Integer.MAX_VALUE, y1 = Integer.MIN_VALUE; + int x1 = Integer.MIN_VALUE, y1 = Integer.MIN_VALUE; + int w = 0, h = 0; for (IWidget child : children) { Box margin = child.getArea().getMargin(); IResizeable resizeable = child.resizer(); Area area = child.getArea(); if (this.x.dependsOnChildren() && resizeable.isWidthCalculated()) { - x1 = Math.max(x1, area.rx + area.width + padding.right + margin.right); + w = Math.max(w, area.requestedWidth() + padding.horizontal()); + if (resizeable.isXCalculated()) { + x1 = Math.max(x1, area.rx + area.width + padding.right + margin.right); + } } if (this.y.dependsOnChildren() && resizeable.isHeightCalculated()) { - y1 = Math.max(y1, area.ry + area.height + padding.bottom + margin.bottom); + h = Math.max(h, area.requestedHeight() + padding.vertical()); + if (resizeable.isXCalculated()) { + y1 = Math.max(y1, area.ry + area.height + padding.bottom + margin.bottom); + } } } + if (x1 == Integer.MIN_VALUE) x1 = 0; + if (y1 == Integer.MIN_VALUE) y1 = 0; + if (w > x1) x1 = w; + if (h > y1) y1 = h; + Area relativeTo = getRelativeTo().getArea(); if (this.x.dependsOnChildren()) { this.x.postApply(getArea(), relativeTo, 0, x1); @@ -407,14 +456,18 @@ private void coverChildrenForLayout() { @Override public void applyPos(IGuiElement parent) { - // after all widgets x, y, width and height have been calculated we can now calculate the absolute position Area relativeTo = getRelativeTo().getArea(); Area area = parent.getArea(); + // apply margin and padding if not done yet + this.x.applyMarginAndPaddingToPos(area, relativeTo); + this.y.applyMarginAndPaddingToPos(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); Area parentArea = parent.getParentArea(); area.rx = area.x - parentArea.x; area.ry = area.y - parentArea.y; if (parent instanceof IVanillaSlot) { + // special treatment for minecraft slots Slot slot = ((IVanillaSlot) parent).getVanillaSlot(); slot.xPos = parent.getArea().x; slot.yPos = parent.getArea().y; diff --git a/src/main/java/com/cleanroommc/modularui/widget/sizer/IUnResizeable.java b/src/main/java/com/cleanroommc/modularui/widget/sizer/IUnResizeable.java new file mode 100644 index 00000000..0b66662a --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/IUnResizeable.java @@ -0,0 +1,57 @@ +package com.cleanroommc.modularui.widget.sizer; + +import com.cleanroommc.modularui.api.layout.IResizeable; +import com.cleanroommc.modularui.api.widget.IGuiElement; + +/** + * A variation of {@link IResizeable} with default implementations which don't do anything + */ +public interface IUnResizeable extends IResizeable { + + @Override + default void initResizing() {} + + @Override + default boolean postResize(IGuiElement guiElement) { + return true; + } + + @Override + default boolean isXCalculated() { + return true; + } + + @Override + default boolean isYCalculated() { + return true; + } + + @Override + default boolean isWidthCalculated() { + return true; + } + + @Override + default boolean isHeightCalculated() { + return true; + } + + @Override + default void setResized(boolean x, boolean y, boolean w, boolean h) {} + + @Override + default void setXMarginPaddingApplied(boolean b) {} + + @Override + default void setYMarginPaddingApplied(boolean b) {} + + @Override + default boolean isXMarginPaddingApplied() { + return true; + } + + @Override + default boolean isYMarginPaddingApplied() { + return true; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/widget/sizer/Unit.java b/src/main/java/com/cleanroommc/modularui/widget/sizer/Unit.java index 4571c79c..ede78a64 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/Unit.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/Unit.java @@ -1,9 +1,11 @@ package com.cleanroommc.modularui.widget.sizer; import org.intellij.lang.annotations.MagicConstant; +import org.jetbrains.annotations.ApiStatus; import java.util.function.DoubleSupplier; +@ApiStatus.Internal public class Unit { public static final byte UNUSED = -2; diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java index 27098a19..f1243361 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java @@ -1,5 +1,6 @@ package com.cleanroommc.modularui.widgets; +import com.cleanroommc.modularui.api.GuiAxis; import com.cleanroommc.modularui.api.layout.ILayoutWidget; import com.cleanroommc.modularui.api.widget.IValueWidget; import com.cleanroommc.modularui.api.widget.IWidget; @@ -7,7 +8,6 @@ import com.cleanroommc.modularui.utils.ScrollDirection; import com.cleanroommc.modularui.widget.ScrollWidget; import com.cleanroommc.modularui.widget.WidgetTree; -import com.cleanroommc.modularui.widget.sizer.GuiAxis; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.jetbrains.annotations.Nullable; @@ -99,26 +99,23 @@ public List getValues() { @Override public void layoutWidgets() { GuiAxis axis = this.scrollData.direction.axis; - int p = 0; - int lastMargin = getArea().getPadding().getStart(axis); + int p = getArea().getPadding().getStart(axis); for (IWidget widget : getChildren()) { - if (widget.getFlex() != null && (axis.isVertical() ? widget.getFlex().hasYPos() : widget.getFlex().hasXPos())) { + if (axis.isVertical() ? + widget.getFlex().hasYPos() || !widget.resizer().isHeightCalculated() : + widget.getFlex().hasXPos() || !widget.resizer().isWidthCalculated()) { continue; } - if (axis.isHorizontal() ? !widget.resizer().isWidthCalculated() : !widget.resizer().isHeightCalculated()) { - continue; - } - p += Math.max(lastMargin, widget.getArea().getMargin().getStart(axis)); + p += widget.getArea().getMargin().getStart(axis); widget.getArea().setRelativePoint(axis, p); - p += widget.getArea().getSize(axis); - lastMargin = widget.getArea().getMargin().getEnd(axis); + p += widget.getArea().getSize(axis) + widget.getArea().getMargin().getEnd(axis); if (axis.isHorizontal()) { widget.resizer().setXResized(true); } else { widget.resizer().setYResized(true); } } - getScrollData().scrollSize = p + Math.max(lastMargin, getArea().getPadding().getEnd(axis)); + getScrollData().scrollSize = p + getArea().getPadding().getEnd(axis); } public ScrollData getScrollData() { diff --git a/src/main/java/com/cleanroommc/modularui/widgets/PagedWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/PagedWidget.java index 35aa719d..6bac9c52 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/PagedWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/PagedWidget.java @@ -62,16 +62,6 @@ public int getCurrentPageIndex() { return this.pages; } - // TODO why is this needed? - /*@Override - public boolean resize(boolean init) { - int page = this.currentPageIndex; - this.currentPageIndex = -1; - boolean b = super.resize(init); - this.currentPageIndex = page; - return b; - }*/ - public W addPage(IWidget widget) { this.pages.add(widget); widget.setEnabled(false); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java index 5bbc4ef8..9a230685 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java @@ -1,5 +1,6 @@ package com.cleanroommc.modularui.widgets; +import com.cleanroommc.modularui.api.GuiAxis; import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.api.value.IDoubleValue; @@ -14,7 +15,6 @@ import com.cleanroommc.modularui.value.sync.SyncHandler; import com.cleanroommc.modularui.widget.Widget; import com.cleanroommc.modularui.widget.sizer.Area; -import com.cleanroommc.modularui.widget.sizer.GuiAxis; import com.cleanroommc.modularui.widget.sizer.Unit; import it.unimi.dsi.fastutil.doubles.DoubleArrayList; import it.unimi.dsi.fastutil.doubles.DoubleList; diff --git a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java index 344ab215..2fa0d0d0 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java @@ -67,9 +67,9 @@ private TextRenderer simulate(float maxWidth) { public int getDefaultHeight() { float maxWidth = getScreen().getScreenArea().width; if (resizer() != null && resizer().isWidthCalculated()) { - maxWidth = getArea().width; + maxWidth = getArea().width + 1; } else if (getParent().resizer() != null && getParent().resizer().isWidthCalculated()) { - maxWidth = getParent().getArea().width; + maxWidth = getParent().getArea().width + 1; } TextRenderer renderer = simulate(maxWidth); Box padding = getArea().getPadding(); 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 c783bff7..31006e11 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/layout/Column.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/layout/Column.java @@ -23,16 +23,23 @@ public void layoutWidgets() { boolean hasHeight = resizer().isHeightCalculated(); int height = getArea().height; Box padding = getArea().getPadding(); + MainAxisAlignment maa = this.maa; + if (!hasHeight && maa != MainAxisAlignment.START) { + if (flex().yAxisDependsOnChildren()) { + maa = MainAxisAlignment.START; + } else { + throw new IllegalStateException("MainAxisAlignment other than start need the height to be calculated!"); + } + } - int maxWidth = 0; int totalHeight = 0; int expandedAmount = 0; + // TODO children needs width calculated // calculate total height and maximum width for (IWidget widget : getChildren()) { // exclude self positioned (Y) children if (widget.flex().hasYPos()) continue; - maxWidth = Math.max(maxWidth, widget.getArea().requestedWidth()); if (widget.flex().isExpanded()) { expandedAmount++; totalHeight += widget.getArea().getMargin().vertical(); @@ -51,18 +58,20 @@ public void layoutWidgets() { widget.resizer().setHeightResized(true); } } + if (maa == MainAxisAlignment.SPACE_BETWEEN || maa == MainAxisAlignment.SPACE_AROUND) { + maa = MainAxisAlignment.START; + } } // calculate start y - int lastY = 0; + int lastY = padding.top; if (hasHeight) { - if (this.maa == MainAxisAlignment.CENTER) { + if (maa == MainAxisAlignment.CENTER) { lastY = (int) (height / 2f - totalHeight / 2f); - } else if (this.maa == MainAxisAlignment.END) { + } else if (maa == MainAxisAlignment.END) { lastY = height - totalHeight; } } - lastY = Math.max(lastY, padding.top) - getArea().getMargin().top; for (IWidget widget : getChildren()) { // exclude self positioned (Y) children @@ -71,12 +80,13 @@ public void layoutWidgets() { // set calculated relative Y pos and set bottom margin for next widget widget.getArea().ry = lastY + margin.top; + widget.resizer().setYResized(true); + widget.resizer().setYMarginPaddingApplied(true); lastY += widget.getArea().requestedHeight(); - if (hasHeight && this.maa == MainAxisAlignment.SPACE_BETWEEN) { + if (hasHeight && maa == MainAxisAlignment.SPACE_BETWEEN) { lastY += (height - totalHeight) / (getChildren().size() - 1); } - widget.resizer().setYResized(true); } } @@ -91,16 +101,17 @@ public void postLayoutWidgets() { Box margin = widget.getArea().getMargin(); // don't align auto positioned (X) children in X if (!widget.flex().hasXPos() && widget.resizer().isWidthCalculated()) { - int x = padding.left + margin.left; + int x = margin.left + padding.left; if (hasWidth) { if (this.caa == CrossAxisAlignment.CENTER) { x = (int) (width / 2f - widget.getArea().width / 2f); } else if (this.caa == CrossAxisAlignment.END) { - x = width - widget.getArea().width - padding.right - margin.left; + x = width - widget.getArea().width - margin.right - padding.left; } } widget.getArea().rx = x; widget.resizer().setXResized(true); + widget.resizer().setXMarginPaddingApplied(true); } } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/layout/OrganizedPanel.java b/src/main/java/com/cleanroommc/modularui/widgets/layout/OrganizedPanel.java index 5924f576..2504f6e8 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/layout/OrganizedPanel.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/layout/OrganizedPanel.java @@ -3,7 +3,12 @@ import com.cleanroommc.modularui.api.widget.IWidget; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.widget.ParentWidget; +import org.jetbrains.annotations.ApiStatus; +/** + * A panel with a header, left sidebar, right sidebar and a footer + */ +@ApiStatus.Experimental public class OrganizedPanel extends ModularPanel { private ParentWidget header; 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 f7d06417..d3246eeb 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/layout/Row.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/layout/Row.java @@ -23,15 +23,21 @@ public void layoutWidgets() { boolean hasWidth = resizer().isWidthCalculated(); int width = getArea().width; Box padding = getArea().getPadding(); + MainAxisAlignment maa = this.maa; + if (!hasWidth && maa != MainAxisAlignment.START) { + if (flex().yAxisDependsOnChildren()) { + maa = MainAxisAlignment.START; + } else { + throw new IllegalStateException("MainAxisAlignment other than start need the width to be calculated!"); + } + } - int maxHeight = 0; int totalWidth = 0; int expandedAmount = 0; for (IWidget widget : getChildren()) { // exclude self positioned (X) children if (widget.flex().hasXPos()) continue; - maxHeight = Math.max(maxHeight, widget.getArea().requestedHeight()); if (widget.flex().isExpanded()) { expandedAmount++; totalWidth += widget.getArea().getMargin().horizontal(); @@ -50,18 +56,20 @@ public void layoutWidgets() { widget.resizer().setWidthResized(true); } } + if (maa == MainAxisAlignment.SPACE_BETWEEN || maa == MainAxisAlignment.SPACE_AROUND) { + maa = MainAxisAlignment.START; + } } // calculate start y - int lastX = 0; + int lastX = padding.left; if (hasWidth) { - if (this.maa == MainAxisAlignment.CENTER) { + if (maa == MainAxisAlignment.CENTER) { lastX = (int) (width / 2f - totalWidth / 2f); - } else if (this.maa == MainAxisAlignment.END) { + } else if (maa == MainAxisAlignment.END) { lastX = width - totalWidth; } } - lastX = Math.max(lastX, padding.left) - getArea().getMargin().left; for (IWidget widget : getChildren()) { // exclude self positioned (X) children @@ -71,9 +79,10 @@ public void layoutWidgets() { // set calculated relative Y pos and set bottom margin for next widget widget.getArea().rx = lastX + margin.left; widget.resizer().setXResized(true); + widget.resizer().setXMarginPaddingApplied(true); lastX += widget.getArea().requestedWidth(); - if (hasWidth && this.maa == MainAxisAlignment.SPACE_BETWEEN) { + if (hasWidth && maa == MainAxisAlignment.SPACE_BETWEEN) { lastX += (width - totalWidth) / (getChildren().size() - 1); } } @@ -100,6 +109,7 @@ public void postLayoutWidgets() { } widget.getArea().ry = y; widget.resizer().setYResized(true); + widget.resizer().setYMarginPaddingApplied(true); } } }