diff --git a/TODO.md b/TODO.md index a5d2b3dc..95d341c0 100644 --- a/TODO.md +++ b/TODO.md @@ -48,4 +48,13 @@ - [ ] properly add modularui elements to any gui screen - [ ] a gui to create guis - [ ] sounds ? -- [ ] change color of themes to IDrawable +- [x] change color of themes to IDrawable + +## Internal changes +- [ ] Clean up IInterpolation +- [ ] Fix shift clicking to slots which can not be seen +- [ ] Put axis alignments as inner class into Alignment + +## HoloUI +- [ ] Fix/Remove current rotations +- [ ] Implement interactions diff --git a/build.gradle b/build.gradle index 64673d88..f4a15fa2 100644 --- a/build.gradle +++ b/build.gradle @@ -109,6 +109,7 @@ dependencies { annotationProcessor('com.google.guava:guava:24.1.1-jre') annotationProcessor('com.google.code.gson:gson:2.8.6') annotationProcessor(mixin) { transitive = false } + // Example deobf dependency // compileOnly rfg.deobf("curse.maven:endercore-231868:2972849:") diff --git a/src/main/java/com/cleanroommc/modularui/api/layout/ILayoutWidget.java b/src/main/java/com/cleanroommc/modularui/api/layout/ILayoutWidget.java index 3d7c69af..883f2c4e 100644 --- a/src/main/java/com/cleanroommc/modularui/api/layout/ILayoutWidget.java +++ b/src/main/java/com/cleanroommc/modularui/api/layout/ILayoutWidget.java @@ -6,13 +6,15 @@ public interface ILayoutWidget { /** - * Called when this should re-layout it's children. + * Called after the children tried to calculate their size. + * Might be called multiple times. */ void layoutWidgets(); /** - * Can be used for some extra calculations - * TODO: Really needed? + * Called after post calculation of this widget. + * Might be called multiple times. + * The last call guarantees, that this widget is fully calculated. */ default void postLayoutWidgets() { } 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 faf21e37..d1e8bcfe 100644 --- a/src/main/java/com/cleanroommc/modularui/api/widget/IGuiElement.java +++ b/src/main/java/com/cleanroommc/modularui/api/widget/IGuiElement.java @@ -5,6 +5,8 @@ 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. @@ -21,6 +23,8 @@ public interface IGuiElement { */ IGuiElement getParent(); + IResizeable resizer(); + /** * @return the area this element occupies */ @@ -97,11 +101,6 @@ default boolean isBelowMouse() { */ boolean isEnabled(); - /** - * Called when the screen resizes. Handles the positioning and sizing of this element. - */ - void resize(); - /** * @return default width if it can't be calculated */ 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 d5744612..dd802151 100644 --- a/src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java +++ b/src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java @@ -1,7 +1,6 @@ package com.cleanroommc.modularui.api.widget; import com.cleanroommc.modularui.api.ITheme; -import com.cleanroommc.modularui.api.layout.ILayoutWidget; import com.cleanroommc.modularui.api.layout.IViewportStack; import com.cleanroommc.modularui.drawable.Stencil; import com.cleanroommc.modularui.screen.ModularPanel; @@ -11,7 +10,6 @@ import com.cleanroommc.modularui.widget.sizer.Flex; import com.cleanroommc.modularui.widget.sizer.IResizeable; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; @@ -130,6 +128,15 @@ default boolean hasChildren() { void setEnabled(boolean enabled); + default boolean areAncestorsEnabled() { + IWidget parent = this; + while (parent != null) { + if (!parent.isEnabled()) return false; + parent = parent.getParent(); + } + return true; + } + default boolean canBeSeen(IViewportStack stack) { return Stencil.isInsideScissorArea(getArea(), stack); } @@ -162,7 +169,6 @@ default boolean canHover() { /** * @return resizer of this widget */ - @Nullable IResizeable resizer(); /** @@ -173,33 +179,20 @@ default boolean canHover() { void resizer(IResizeable resizer); /** - * Called when the screen resizes. Handles the positioning and sizing of this element. + * Called before a widget is resized. */ - @Override - default void resize() { - IResizeable resizer = resizer(); - if (resizer != null) { - if (resizer.isSkip()) return; - resizer.apply(this); - } - - if (hasChildren()) { - getChildren().forEach(IWidget::resize); - } - - if (this instanceof ILayoutWidget) { - ((ILayoutWidget) this).layoutWidgets(); - } - - if (resizer != null) { - resizer.postApply(this); - } + default void beforeResize() { + } - if (this instanceof ILayoutWidget) { - ((ILayoutWidget) this).postLayoutWidgets(); - } + /** + * Called after a widget is fully resized. + */ + default void onResized() { } + /** + * Called after the full widget tree is resized and the absolute positions are calculated. + */ default void postResize() { } diff --git a/src/main/java/com/cleanroommc/modularui/holoui/HoloUI.java b/src/main/java/com/cleanroommc/modularui/holoui/HoloUI.java index 33b4a6a8..3b7ec26b 100644 --- a/src/main/java/com/cleanroommc/modularui/holoui/HoloUI.java +++ b/src/main/java/com/cleanroommc/modularui/holoui/HoloUI.java @@ -1,5 +1,6 @@ package com.cleanroommc.modularui.holoui; +import com.cleanroommc.modularui.screen.JeiSettings; import com.cleanroommc.modularui.screen.ModularScreen; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.client.Minecraft; @@ -78,6 +79,9 @@ public Builder plane(Plane3D plane) { } public void open(ModularScreen screen) { + JeiSettings jeiSettings = new JeiSettings(); + jeiSettings.disableJei(); + screen.getContext().setJeiSettings(jeiSettings); HoloScreenEntity holoScreenEntity = new HoloScreenEntity(Minecraft.getMinecraft().world, this.plane3D); holoScreenEntity.setPosition(this.x, this.y, this.z); holoScreenEntity.setScreen(screen); diff --git a/src/main/java/com/cleanroommc/modularui/manager/GuiCreationContext.java b/src/main/java/com/cleanroommc/modularui/manager/GuiCreationContext.java index c37d4fd4..050d5de7 100644 --- a/src/main/java/com/cleanroommc/modularui/manager/GuiCreationContext.java +++ b/src/main/java/com/cleanroommc/modularui/manager/GuiCreationContext.java @@ -1,5 +1,6 @@ package com.cleanroommc.modularui.manager; +import com.cleanroommc.modularui.screen.JeiSettings; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; @@ -15,15 +16,17 @@ public class GuiCreationContext { private final World world; private final int x, y, z; private final EnumHand hand; + private final JeiSettings jeiSettings; @ApiStatus.Internal - public GuiCreationContext(EntityPlayer player, World world, int x, int y, int z, EnumHand hand) { + public GuiCreationContext(EntityPlayer player, World world, int x, int y, int z, EnumHand hand, JeiSettings jeiSettings) { this.player = player; this.world = world; this.x = x; this.y = y; this.z = z; this.hand = hand; + this.jeiSettings = jeiSettings; } public EntityPlayer getPlayer() { @@ -78,6 +81,10 @@ public BlockPos getBlockPos() { return new BlockPos(this.x, this.y, this.z); } + public JeiSettings getJeiSettings() { + return this.jeiSettings; + } + public IBlockState getBlockState() { BlockPos.PooledMutableBlockPos pos = BlockPos.PooledMutableBlockPos.retain(this.x, this.y, this.z); IBlockState blockState = this.world.getBlockState(pos); @@ -94,7 +101,7 @@ public TileEntity getTileEntity() { public GuiCreationContext with(EnumHand hand) { if (this.hand != hand) { - return new GuiCreationContext(this.player, this.world, this.x, this.y, this.z, hand); + return new GuiCreationContext(this.player, this.world, this.x, this.y, this.z, hand, this.jeiSettings); } return this; } diff --git a/src/main/java/com/cleanroommc/modularui/manager/GuiInfo.java b/src/main/java/com/cleanroommc/modularui/manager/GuiInfo.java index 0caa81c2..e113f137 100644 --- a/src/main/java/com/cleanroommc/modularui/manager/GuiInfo.java +++ b/src/main/java/com/cleanroommc/modularui/manager/GuiInfo.java @@ -12,6 +12,7 @@ import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import java.util.Objects; import java.util.function.BiFunction; public class GuiInfo { @@ -49,17 +50,15 @@ public void open(EntityPlayer player, World world, int x, int y, int z) { } public ModularPanel createCommonGui(GuiCreationContext context, GuiSyncManager guiSyncManager) { - ModularPanel panel = this.mainPanelCreator.apply(context, guiSyncManager); + ModularPanel panel = Objects.requireNonNull(this.mainPanelCreator.apply(context, guiSyncManager), "The main panel must not be null!"); WidgetTree.collectSyncValues(guiSyncManager, panel); return panel; } @SideOnly(Side.CLIENT) public ModularScreen createClientGui(GuiCreationContext context, ModularPanel panel) { - Object screen = this.clientGuiCreator.apply(context, panel); - if (!(screen instanceof ModularScreen)) { - throw new IllegalStateException("Client screen must be an instance of ModularScreen"); - } + Object screen = Objects.requireNonNull(this.clientGuiCreator.apply(context, panel), "The modular screen must not be null!"); + if (!(screen instanceof ModularScreen)) throw new IllegalStateException("Client screen must be an instance of ModularScreen"); return (ModularScreen) screen; } diff --git a/src/main/java/com/cleanroommc/modularui/manager/GuiManager.java b/src/main/java/com/cleanroommc/modularui/manager/GuiManager.java index 966dc62f..11fac4c3 100644 --- a/src/main/java/com/cleanroommc/modularui/manager/GuiManager.java +++ b/src/main/java/com/cleanroommc/modularui/manager/GuiManager.java @@ -2,10 +2,7 @@ import com.cleanroommc.modularui.ModularUI; import com.cleanroommc.modularui.network.NetworkUtils; -import com.cleanroommc.modularui.screen.GuiScreenWrapper; -import com.cleanroommc.modularui.screen.ModularContainer; -import com.cleanroommc.modularui.screen.ModularPanel; -import com.cleanroommc.modularui.screen.ModularScreen; +import com.cleanroommc.modularui.screen.*; import com.cleanroommc.modularui.value.sync.GuiSyncManager; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.entity.player.EntityPlayer; @@ -46,7 +43,7 @@ public Object getServerGuiElement(int ID, EntityPlayer player, World world, int GuiInfo info = this.guiInfos.get(ID); if (info == null) return null; GuiSyncManager guiSyncManager = new GuiSyncManager(player); - info.createCommonGui(new GuiCreationContext(player, world, x, y, z, EnumHand.MAIN_HAND), guiSyncManager); + info.createCommonGui(new GuiCreationContext(player, world, x, y, z, EnumHand.MAIN_HAND, new JeiSettings()), guiSyncManager); return new ModularContainer(guiSyncManager); } @@ -56,8 +53,10 @@ public Object getClientGuiElement(int ID, EntityPlayer player, World world, int GuiInfo info = this.guiInfos.get(ID); if (info == null) return null; GuiSyncManager guiSyncManager = new GuiSyncManager(player); - GuiCreationContext context = new GuiCreationContext(player, world, x, y, z, EnumHand.MAIN_HAND); + GuiCreationContext context = new GuiCreationContext(player, world, x, y, z, EnumHand.MAIN_HAND, new JeiSettings()); ModularPanel panel = info.createCommonGui(context, guiSyncManager); - return new GuiScreenWrapper(new ModularContainer(guiSyncManager), info.createClientGui(context, panel)); + ModularScreen modularScreen = info.createClientGui(context, panel); + modularScreen.getContext().setJeiSettings(context.getJeiSettings()); + return new GuiScreenWrapper(new ModularContainer(guiSyncManager), modularScreen); } } diff --git a/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java b/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java index 75f84a61..dca42225 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java +++ b/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java @@ -405,6 +405,10 @@ public ResourceLocation getResourceLocation() { return new ResourceLocation(this.owner, this.name); } + public GuiContext getContext() { + return this.context; + } + public WindowManager getWindowManager() { return this.windowManager; } @@ -467,11 +471,6 @@ public ModularScreen useTheme(String theme) { return this; } - public ModularScreen useJeiSettings(JeiSettings jeiSettings) { - this.context.setJeiSettings(jeiSettings); - return this; - } - public enum UpOrDown { UP(1), DOWN(-1); 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 3e9c486a..7b432f3e 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiContext.java +++ b/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiContext.java @@ -54,7 +54,7 @@ public class GuiContext extends GuiViewportStack { public List> postRenderCallbacks = new ArrayList<>(); - private JeiSettings jeiSettings = new JeiSettings(); + private JeiSettings jeiSettings; public GuiContext(ModularScreen screen) { this.screen = screen; @@ -432,10 +432,17 @@ public ITheme getTheme() { } public JeiSettings getJeiSettings() { + if (this.jeiSettings == null) { + throw new IllegalStateException("The screen is not yet initialised!"); + } return this.jeiSettings; } + @ApiStatus.Internal public void setJeiSettings(JeiSettings jeiSettings) { + if (this.jeiSettings != null) { + throw new IllegalStateException("Tried to set jei settings twice"); + } this.jeiSettings = jeiSettings; } diff --git a/src/main/java/com/cleanroommc/modularui/test/EventHandler.java b/src/main/java/com/cleanroommc/modularui/test/EventHandler.java index c6fbb845..4d35c21a 100644 --- a/src/main/java/com/cleanroommc/modularui/test/EventHandler.java +++ b/src/main/java/com/cleanroommc/modularui/test/EventHandler.java @@ -15,6 +15,7 @@ public static void onItemUse(PlayerInteractEvent.RightClickItem event) { //GuiManager.openClientUI(Minecraft.getMinecraft().player, new TestGui()); HoloUI.builder() .inFrontOf(Minecraft.getMinecraft().player, 5, false) + .screenScale(0.5f) .open(new TestGui()); } } diff --git a/src/main/java/com/cleanroommc/modularui/test/TestTile.java b/src/main/java/com/cleanroommc/modularui/test/TestTile.java index 3cd35e37..49cd4281 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestTile.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestTile.java @@ -74,6 +74,7 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage .align(Alignment.Center); // center the panel in the screen panel.bindPlayerInventory() .child(new Row() + .debugName("Tab row") .coverChildren() .topRel(0f, 4, 1f) .child(new PageButton(0, tabController) @@ -83,18 +84,24 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage .child(new PageButton(2, tabController) .tab(GuiTextures.TAB_TOP, 0))) .child(new PagedWidget<>() + .debugName("root parent") .sizeRel(1f) .controller(tabController) .addPage(new ParentWidget<>() + .debugName("page 1 parent") .sizeRel(1f, 1f) .child(SlotGroupWidget.playerInventory()) .child(new Row() + .debugName("buttons, slots and more tests") .height(137) + .coverChildrenWidth() .padding(7) .child(new Column() + .debugName("buttons and slots test") .coverChildren() + .marginRight(8) //.flex(flex -> flex.height(0.5f)) - .widthRel(0.5f) + //.widthRel(0.5f) .crossAxisAlignment(CrossAxisAlignment.CENTER) .child(new ButtonWidget<>() .size(60, 18) @@ -132,10 +139,11 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage .size(60, 20) .value(SyncHandlers.doubleNumber(() -> this.doubleValue, val -> this.doubleValue = val)) .setNumbersDouble(Function.identity())) - .child(IKey.str("Test string").asWidget().padding(2))) + .child(IKey.str("Test string").asWidget().padding(2).debugName("test string"))) .child(new Column() + .debugName("button and slots test 2") .coverChildren() - .widthRel(0.5f) + //.widthRel(0.5f) .crossAxisAlignment(CrossAxisAlignment.CENTER) .child(new ProgressWidget() .progress(() -> this.progress / (double) this.duration) @@ -160,6 +168,7 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage .syncHandler(SyncHandlers.fluidSlot(this.fluidTankPhantom).phantom(true))) ))) .addPage(new Column() + .debugName("Slots test page") //.coverChildren() .padding(7) .child(SlotGroupWidget.playerInventory()) @@ -197,6 +206,7 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage .asWidget() .flex(flex -> flex.width(1f).height(1f))*/) .addPage(new ParentWidget<>() + .debugName("page 3 parent") .sizeRel(1f, 1f) .padding(7) .child(SlotGroupWidget.playerInventory()) @@ -206,6 +216,7 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage .stopper(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100) .background(GuiTextures.SLOT_DARK)) .child(new ButtonWidget<>() + .debugName("color picker button") .top(25) .background(colorPickerBackground) .onMousePressed(mouseButton -> { @@ -223,7 +234,9 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage .left(32) .size(1, 40))*/ .child(new Row() - .widthRel(1f).height(14) + .debugName("bogo test config 1") + .widthRel(1f).coverChildrenHeight() + .crossAxisAlignment(CrossAxisAlignment.CENTER) .child(new CycleButtonWidget() .value(new BoolValue(false)) .texture(GuiTextures.CHECK_BOX) @@ -233,6 +246,7 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage .height(14) .marginLeft(10))) .child(new Row() + .debugName("bogo test config 2") .widthRel(1f).height(14) .child(new TextFieldWidget() .value(new IntValue.Dynamic(() -> this.num, val -> this.num = val)) @@ -250,6 +264,7 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage .tooltip(tooltip -> tooltip.showUpTimer(10) .addLine(IKey.lang("bogosort.gui.hotbar_scrolling.tooltip")))) .child(new Row() + .debugName("bogo test config 3") .widthRel(1f).height(14) .child(new CycleButtonWidget() .value(new BoolValue(false)) diff --git a/src/main/java/com/cleanroommc/modularui/value/sync/GuiSyncManager.java b/src/main/java/com/cleanroommc/modularui/value/sync/GuiSyncManager.java index 22b14e38..9fabff2e 100644 --- a/src/main/java/com/cleanroommc/modularui/value/sync/GuiSyncManager.java +++ b/src/main/java/com/cleanroommc/modularui/value/sync/GuiSyncManager.java @@ -179,6 +179,10 @@ public PlayerMainInvWrapper getPlayerInventory() { return this.playerInventory; } + public boolean isClient() { + return NetworkUtils.isClient(this.player); + } + public static String makeSyncKey(String name, int id) { return name + ":" + id; } diff --git a/src/main/java/com/cleanroommc/modularui/value/sync/PanelSyncHandler.java b/src/main/java/com/cleanroommc/modularui/value/sync/PanelSyncHandler.java index 17369f47..3f963627 100644 --- a/src/main/java/com/cleanroommc/modularui/value/sync/PanelSyncHandler.java +++ b/src/main/java/com/cleanroommc/modularui/value/sync/PanelSyncHandler.java @@ -1,6 +1,5 @@ package com.cleanroommc.modularui.value.sync; -import com.cleanroommc.modularui.network.NetworkUtils; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.widget.WidgetTree; import net.minecraft.network.PacketBuffer; @@ -11,6 +10,7 @@ public abstract class PanelSyncHandler extends SyncHandler { private final ModularPanel mainPanel; + private ModularPanel openedPanel; protected PanelSyncHandler(ModularPanel mainPanel) { this.mainPanel = mainPanel; @@ -23,9 +23,9 @@ public void openPanel() { } private void openPanel(boolean syncToServer) { - boolean client = NetworkUtils.isClient(getSyncManager().getPlayer()); + boolean client = getSyncManager().isClient(); if (syncToServer && client) { - syncToServer(0, buffer -> {}); + syncToServer(0); return; } ModularPanel panel = Objects.requireNonNull(createUI(this.mainPanel, getSyncManager())); @@ -35,13 +35,29 @@ private void openPanel(boolean syncToServer) { WidgetTree.collectSyncValues(getSyncManager(), panel); if (client && !this.mainPanel.getScreen().isPanelOpen(panel.getName())) { this.mainPanel.getScreen().openPanel(panel); + this.openedPanel = panel; } } + public void closePanel() { + if (getSyncManager().isClient()) { + if (this.openedPanel != null) this.openedPanel.animateClose(); + } else { + syncToClient(2); + } + this.openedPanel = null; + } + + public boolean isPanelOpen() { + return this.openedPanel != null && (!getSyncManager().isClient() || this.openedPanel.isOpen()); + } + @Override public void readOnClient(int i, PacketBuffer packetBuffer) throws IOException { if (i == 1) { openPanel(false); + } else if (i == 2) { + closePanel(); } } @@ -49,7 +65,7 @@ public void readOnClient(int i, PacketBuffer packetBuffer) throws IOException { public void readOnServer(int i, PacketBuffer packetBuffer) throws IOException { if (i == 0) { openPanel(false); - syncToClient(1, buf -> {}); + syncToClient(1); } } } 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 a2f9d012..f4f9789b 100644 --- a/src/main/java/com/cleanroommc/modularui/value/sync/SyncHandler.java +++ b/src/main/java/com/cleanroommc/modularui/value/sync/SyncHandler.java @@ -1,7 +1,6 @@ package com.cleanroommc.modularui.value.sync; import com.cleanroommc.modularui.network.NetworkHandler; -import com.cleanroommc.modularui.network.NetworkUtils; import com.cleanroommc.modularui.network.packets.PacketSyncHandler; import io.netty.buffer.Unpooled; import net.minecraft.entity.player.EntityPlayerMP; @@ -10,6 +9,7 @@ import net.minecraftforge.fml.relauncher.SideOnly; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.MustBeInvokedByOverriders; +import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.util.Objects; @@ -38,7 +38,7 @@ public void init(String key, GuiSyncManager syncManager) { * @param id an internal denominator to identify this package * @param bufferConsumer the package builder */ - public final void syncToClient(int id, Consumer bufferConsumer) { + public final void syncToClient(int id, @NotNull Consumer bufferConsumer) { PacketBuffer buffer = new PacketBuffer(Unpooled.buffer()); buffer.writeVarInt(id); bufferConsumer.accept(buffer); @@ -52,7 +52,7 @@ public final void syncToClient(int id, Consumer bufferConsumer) { * @param bufferConsumer the package builder */ @SideOnly(Side.CLIENT) - public final void syncToServer(int id, Consumer bufferConsumer) { + public final void syncToServer(int id, @NotNull Consumer bufferConsumer) { PacketBuffer buffer = new PacketBuffer(Unpooled.buffer()); buffer.writeVarInt(id); bufferConsumer.accept(buffer); @@ -65,17 +65,44 @@ public final void syncToServer(int id, Consumer bufferConsumer) { * @param id an internal denominator to identify this package * @param bufferConsumer the package builder */ - public final void sync(int id, Consumer bufferConsumer) { + public final void sync(int id, @NotNull Consumer bufferConsumer) { PacketBuffer buffer = new PacketBuffer(Unpooled.buffer()); buffer.writeVarInt(id); bufferConsumer.accept(buffer); - if (NetworkUtils.isClient(getSyncManager().getPlayer())) { + if (getSyncManager().isClient()) { sendToServer(buffer, this); } else { sendToClient(buffer, this); } } + /** + * Sends an empty packte to the client with an id. + * + * @param id identifier + */ + public final void syncToClient(int id) { + syncToClient(id, buf -> {}); + } + + /** + * Sends an empty packte to the server with an id. + * + * @param id identifier + */ + public final void syncToServer(int id) { + syncToServer(id, buf -> {}); + } + + /** + * Sends an empty packte to the other side with an id. + * + * @param id identifier + */ + public final void sync(int id) { + sync(id, buf -> {}); + } + /** * Called when this sync handler receives a packet on client. * diff --git a/src/main/java/com/cleanroommc/modularui/widget/ScrollWidget.java b/src/main/java/com/cleanroommc/modularui/widget/ScrollWidget.java index b00f629f..53cd5a47 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/ScrollWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widget/ScrollWidget.java @@ -65,8 +65,7 @@ public void getWidgetsAt(IViewportStack stack, IWidgetList widgets, int x, int y } @Override - public void resize() { - super.resize(); + public void onResized() { if (this.scroll.getScrollX() != null) { this.scroll.getScrollX().clamp(this.scroll); } diff --git a/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java b/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java index 6f43302f..20d0ac23 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java +++ b/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java @@ -1,5 +1,6 @@ package com.cleanroommc.modularui.widget; +import com.cleanroommc.modularui.api.layout.ILayoutWidget; import com.cleanroommc.modularui.api.layout.IViewport; import com.cleanroommc.modularui.api.widget.IGuiElement; import com.cleanroommc.modularui.api.widget.ISynced; @@ -22,8 +23,6 @@ */ public class WidgetTree { - private static final String AUTO_SYNC_KEY = "auto$sync"; - private WidgetTree() { } @@ -211,15 +210,67 @@ public static void onFrameUpdate(IWidget parent) { public static void resize(IWidget parent) { // resize each widget and calculate their relative pos - parent.resize(); + if (!resizeWidget(parent, true) && !resizeWidget(parent, false)) { + throw new IllegalStateException("Failed to resize widgets"); + } // now apply the calculated pos applyPos(parent); WidgetTree.foreachChildByLayer(parent, child -> { child.postResize(); + //ModularUI.LOGGER.info("{} at {}", child, child.getArea()); return true; }, true); } + private static boolean resizeWidget(IWidget widget, boolean init) { + boolean result = false; + // first try to resize this widget + IResizeable resizer = widget.resizer(); + if (resizer != null) { + if (init) { + widget.beforeResize(); + resizer.initResizing(); + } + result = resizer.resize(widget); + } + + // now resize all children and collect children which could not be fully calculated + List anotherResize = new ArrayList<>(); + if (widget.hasChildren()) { + widget.getChildren().forEach(iWidget -> { + if (!resizeWidget(iWidget, init)) { + anotherResize.add(iWidget); + } + }); + } + + if (widget instanceof ILayoutWidget) { + ((ILayoutWidget) widget).layoutWidgets(); + } + + // post resize this widget if possible + if (resizer != null && !result) { + result = resizer.postResize(widget); + } + + if (widget instanceof ILayoutWidget) { + ((ILayoutWidget) widget).postLayoutWidgets(); + } + + // now fully resize all children which needs it + if (!anotherResize.isEmpty()) { + anotherResize.forEach(iWidget -> { + if (!iWidget.resizer().isFullyCalculated()) { + resizeWidget(iWidget, false); + } + }); + } + + if (result) widget.onResized(); + + return result; + } + public static void applyPos(IWidget parent) { WidgetTree.foreachChildByLayer(parent, child -> { IResizeable resizer = child.resizer(); 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 2f41e9b5..3b5dafd6 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/Area.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/Area.java @@ -328,15 +328,60 @@ public Box getPadding() { } @Override - public void apply(IGuiElement guiElement) { + 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 void postApply(IGuiElement guiElement) { + 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); } + + @Override + public String toString() { + return "Area{" + + "x=" + this.x + + ", y=" + this.y + + ", width=" + this.width + + ", height=" + this.height + + '}'; + } } 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 328d0102..19947498 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/DimensionSizer.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/DimensionSizer.java @@ -13,6 +13,8 @@ public class DimensionSizer { private boolean cancelAutoMovement = false; private boolean defaultMode = false; + private boolean posCalculated = false, sizeCalculated = false; + public DimensionSizer(GuiAxis axis) { this.axis = axis; } @@ -76,6 +78,14 @@ public boolean hasSize() { return this.size != null; } + public boolean isSizeCalculated() { + return this.sizeCalculated; + } + + public boolean isPosCalculated() { + return this.posCalculated; + } + public boolean dependsOnChildren() { return this.coverChildren; } @@ -86,54 +96,107 @@ public boolean dependsOnParent() { (this.size != null && this.size.isRelative()); } - public void apply(Area area, Area relativeTo, IntSupplier defaultSize) { + public void setResized(boolean all) { + setResized(all, all); + } + + public void setResized(boolean pos, boolean size) { + this.posCalculated = pos; + this.sizeCalculated = size; + } + + private boolean needsSize(Unit unit) { + return unit.isRelative() && unit.getAnchor() != 0; + } + + public void apply(Area area, IResizeable relativeTo, IntSupplier defaultSize) { + if (this.sizeCalculated && this.posCalculated) return; int p, s; - int parentSize = relativeTo.getSize(this.axis); + int parentSize = relativeTo.getArea().getSize(this.axis); + boolean calcParent = relativeTo.isSizeCalculated(this.axis); - // calc start, end and size - if (this.start == null && this.end == null) { - p = 0; - s = this.size == null ? defaultSize.getAsInt() : calcSize(this.size, parentSize); + if (this.sizeCalculated && !this.posCalculated) { + s = area.getSize(this.axis); + if (this.start != null) { + p = calcPoint(this.start, s, parentSize, calcParent); + } else if (this.end != null) { + p = calcPoint(this.end, s, parentSize, calcParent); + } else { + throw new IllegalStateException(); + } + } else if (!this.sizeCalculated && this.posCalculated) { + p = area.getRelativePoint(this.axis); + if (this.size != null) { + s = this.coverChildren ? 18 : calcSize(this.size, parentSize, calcParent); + } else { + s = defaultSize.getAsInt(); + this.sizeCalculated = s > 0; + } } else { - if (this.size == null) { - if (this.start != null && this.end != null) { - p = calcPoint(this.start, -1, parentSize); - int x2 = calcPoint(this.end, -1, parentSize); - s = Math.abs(parentSize - x2 - p); - } else { + // calc start, end and size + if (this.start == null && this.end == null) { + p = 0; + if (this.size == null) { s = defaultSize.getAsInt(); - if (this.start == null) { - p = calcPoint(this.end, s, parentSize); - p -= s; + this.sizeCalculated = s > 0; + } else { + s = calcSize(this.size, parentSize, calcParent); + } + this.posCalculated = true; + } else { + if (this.size == null) { + if (this.start != null && this.end != null) { + p = calcPoint(this.start, -1, parentSize, calcParent); + boolean b = this.posCalculated; + this.posCalculated = false; + int x2 = calcPoint(this.end, -1, parentSize, calcParent); + s = Math.abs(parentSize - x2 - p); + this.posCalculated &= b; + this.sizeCalculated |= this.posCalculated; } else { - p = calcPoint(this.start, s, parentSize); + s = defaultSize.getAsInt(); + this.sizeCalculated = s > 0; + if (this.start == null) { + p = calcPoint(this.end, s, parentSize, calcParent); + p -= s; + this.posCalculated &= this.sizeCalculated; + } else { + p = calcPoint(this.start, s, parentSize, calcParent); + this.posCalculated &= (this.sizeCalculated || !needsSize(this.start)); + } } + } else if (this.start != null) { + s = calcSize(this.size, parentSize, calcParent); + p = calcPoint(this.start, s, parentSize, calcParent); + this.posCalculated &= (this.sizeCalculated || !needsSize(this.start)); + } else { + s = calcSize(this.size, parentSize, calcParent); + p = calcPoint(this.end, s, parentSize, calcParent); + p = parentSize - p - s; + this.posCalculated &= this.sizeCalculated; } - } else if (this.end == null) { - s = calcSize(this.size, parentSize); - p = calcPoint(this.start, s, parentSize); - } else { - s = calcSize(this.size, parentSize); - p = calcPoint(this.end, s, parentSize); - p = parentSize - p - s; } } // apply padding and margin - Box.SHARED.all(0); - Box padding = relativeTo.getPadding(); - Box margin = area.getMargin(); + if (this.posCalculated && this.sizeCalculated) { + Box.SHARED.all(0); + Box padding = relativeTo.getArea().getPadding(); + Box margin = area.getMargin(); + + if (parentSize < 1 || (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)); + } - if (parentSize < 1 || (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)); + area.setRelativePoint(this.axis, p); } - - p += relativeTo.x; + p += relativeTo.getArea().x; + area.setPoint(this.axis, p); // temporary area.setSize(this.axis, s); - area.setPoint(this.axis, p); } public int postApply(Area area, Area relativeTo, int p0, int p1) { @@ -141,41 +204,50 @@ public int postApply(Area area, Area relativeTo, int p0, int p1) { // calculate width and recalculate x based on the new width int s = p1 - p0, p; area.setSize(this.axis, s); - if (this.start != null) { - p = calcPoint(this.start, s, relativeTo.getSize(this.axis)); - } else if (this.end != null) { - p = calcPoint(this.end, s, relativeTo.getSize(this.axis)); - p = relativeTo.getSize(this.axis) - p - s; - } else { - p = area.getRelativePoint(this.axis) + p0 + area.getMargin().getStart(this.axis); - if (!this.cancelAutoMovement) { - moveAmount = -p0; + this.sizeCalculated = true; + if (!isPosCalculated()) { + if (this.start != null) { + p = calcPoint(this.start, s, relativeTo.getSize(this.axis), true); + } else if (this.end != null) { + 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); + if (!this.cancelAutoMovement) { + moveAmount = -p0; + } } + area.setRelativePoint(this.axis, p); + this.posCalculated = true; } - area.setRelativePoint(this.axis, p); return moveAmount; } - private int calcSize(Unit s, int parentSize) { + private int calcSize(Unit s, int parentSize, boolean parentSizeCalculated) { + if (this.coverChildren) return 18; float val = s.getValue(); if (s.isRelative()) { - return (int) (val * parentSize); + if (!parentSizeCalculated) return (int) val; + val *= parentSize; } + this.sizeCalculated = true; return (int) val; } - public int calcPoint(Unit p, int width, int parentSize) { + public int calcPoint(Unit p, int width, int parentSize, boolean parentSizeCalculated) { float val = p.getValue(); + if (!parentSizeCalculated && (p == this.end || p.isRelative())) return (int) val; if (p.isRelative()) { val = parentSize * val; + float anchor = p.getAnchor(); + if (width > 0 && anchor != 0) { + val -= width * anchor; + } + if (p.getOffset() != 0) { + val += p.getOffset(); + } } - float anchor = p.getAnchor(); - if (width > 0 && anchor != 0) { - val -= width * anchor; - } - if (p.getOffset() != 0) { - val += p.getOffset(); - } + this.posCalculated = true; return (int) val; } 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 df2f26da..3627f2fa 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/Flex.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/Flex.java @@ -1,6 +1,7 @@ package com.cleanroommc.modularui.widget.sizer; import com.cleanroommc.modularui.GuiError; +import com.cleanroommc.modularui.api.layout.ILayoutWidget; import com.cleanroommc.modularui.api.widget.IGuiElement; import com.cleanroommc.modularui.api.widget.IPositioned; import com.cleanroommc.modularui.api.widget.IVanillaSlot; @@ -20,7 +21,6 @@ public class Flex implements IResizeable, IPositioned { private final IGuiElement parent; private Area relativeTo; private boolean relativeToParent = true; - private boolean skip = false; public Flex(IGuiElement parent) { this.parent = parent; @@ -58,6 +58,26 @@ public Area getArea() { return this.parent.getArea(); } + @Override + public boolean isXCalculated() { + return this.x.isPosCalculated(); + } + + @Override + public boolean isYCalculated() { + return this.y.isPosCalculated(); + } + + @Override + public boolean isWidthCalculated() { + return this.x.isSizeCalculated(); + } + + @Override + public boolean isHeightCalculated() { + return this.y.isSizeCalculated(); + } + public Flex coverChildrenWidth() { this.x.setCoverChildren(true); return this; @@ -226,8 +246,9 @@ public Flex anchor(Alignment alignment) { return this; } - private Area getRelativeTo() { - Area relativeTo = this.relativeToParent ? this.parent.getParentArea() : this.relativeTo; + private IResizeable getRelativeTo() { + IGuiElement parent = this.parent.getParent(); + IResizeable relativeTo = this.relativeToParent && parent != null ? parent.resizer() : this.relativeTo; return relativeTo != null ? relativeTo : this.parent.getScreen().getScreenArea(); } @@ -263,54 +284,43 @@ public boolean hasFixedSize() { return this.x.hasFixedSize() && this.y.hasFixedSize(); } - @ApiStatus.Internal - public void skip() { - this.skip = true; + @Override + public void initResizing() { + this.x.setResized(false); + this.y.setResized(false); } @Override - public boolean isSkip() { - return this.skip; + public void setResized(boolean x, boolean y, boolean w, boolean h) { + this.x.setResized(x, w); + this.y.setResized(y, h); } @Override - public void apply(IGuiElement guiElement) { - if (isSkip()) return; - Area relativeTo = getRelativeTo(); + public boolean resize(IGuiElement guiElement) { + IResizeable relativeTo = getRelativeTo(); + Area relativeArea = relativeTo.getArea(); byte panelLayer = this.parent.getArea().getPanelLayer(); - if (relativeTo.getPanelLayer() > panelLayer || - (relativeTo.getPanelLayer() == panelLayer && relativeTo.z() >= this.parent.getArea().z())) { + if (relativeArea.getPanelLayer() > panelLayer || + (relativeArea.getPanelLayer() == panelLayer && relativeArea.z() >= this.parent.getArea().z())) { GuiError.throwNew(this.parent, GuiError.Type.SIZING, "Widget can't be relative to a widget at the same level or above"); - return; - } - - // check if children area is required for this widget - if (this.x.dependsOnChildren() || this.y.dependsOnChildren()) { - if (!(this.parent instanceof IWidget)) { - throw new IllegalStateException("Can only cover children if instance of IWidget"); - } - - IWidget widget = (IWidget) this.parent; - - List children = widget.getChildren(); - if (!children.isEmpty()) { - for (IWidget child : children) { - if (dependsOnThis(child, null)) { - // children area requires this area to be calculated and will be skipped - child.flex().skip(); - } - } - } + return true; } // calculate x, y, width and height if possible this.x.apply(guiElement.getArea(), relativeTo, guiElement::getDefaultWidth); this.y.apply(guiElement.getArea(), relativeTo, guiElement::getDefaultHeight); + return isFullyCalculated(); } @Override - public void postApply(IGuiElement guiElement) { + public boolean postResize(IGuiElement guiElement) { + if (this.parent instanceof ILayoutWidget) { + coverChildrenForLayout(); + return true; + } + // 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()) { List children = ((IWidget) this.parent).getChildren(); @@ -322,20 +332,20 @@ public void postApply(IGuiElement guiElement) { int x0 = Integer.MAX_VALUE, x1 = Integer.MIN_VALUE, y0 = Integer.MAX_VALUE, y1 = Integer.MIN_VALUE; for (IWidget child : children) { Box margin = child.getArea().getMargin(); - Flex flex = child.flex(); + IResizeable resizeable = child.resizer(); Area area = child.getArea(); - if (this.x.dependsOnChildren() && !dependsOnThis(child, GuiAxis.X)) { + if (this.x.dependsOnChildren() && resizeable.isWidthCalculated()) { x0 = Math.min(x0, area.rx - padding.left - margin.left); x1 = Math.max(x1, area.rx + area.width + padding.right + margin.right); } - if (this.y.dependsOnChildren() && !dependsOnThis(child, GuiAxis.Y)) { + if (this.y.dependsOnChildren() && resizeable.isHeightCalculated()) { y0 = Math.min(y0, area.ry - padding.top - margin.top); y1 = Math.max(y1, area.ry + area.height + padding.bottom + margin.bottom); } } // now calculate new x, y, width and height based on the childrens area - Area relativeTo = getRelativeTo(); + Area relativeTo = getRelativeTo().getArea(); if (this.x.dependsOnChildren()) { moveChildrenX = this.x.postApply(this.parent.getArea(), relativeTo, x0, x1); } @@ -343,26 +353,49 @@ public void postApply(IGuiElement guiElement) { moveChildrenY = this.y.postApply(this.parent.getArea(), relativeTo, y0, y1); } for (IWidget widget : children) { - if (widget.flex().isSkip()) { - // child was skipped before - // this area is now calculated and child can now be calculated - widget.flex().skip = false; - widget.resize(); - } else { - // child was already calculated and now needs to be moved, so that child stays in the same spot and doesn't get moved with the whole parent - Area area = widget.getArea(); - area.rx += moveChildrenX; - area.ry += moveChildrenY; + Area area = widget.getArea(); + IResizeable resizeable = widget.resizer(); + if (resizeable.isXCalculated()) area.rx += moveChildrenX; + if (resizeable.isYCalculated()) area.ry += moveChildrenY; + } + } + } + return isFullyCalculated(); + } + + private void coverChildrenForLayout() { + if (this.x.dependsOnChildren() || this.y.dependsOnChildren()) { + List children = ((IWidget) this.parent).getChildren(); + 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; + 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); + } + if (this.y.dependsOnChildren() && resizeable.isHeightCalculated()) { + y1 = Math.max(y1, area.ry + area.height + padding.bottom + margin.bottom); } } + Area relativeTo = getRelativeTo().getArea(); + if (this.x.dependsOnChildren()) { + this.x.postApply(getArea(), relativeTo, 0, x1); + } + if (this.y.dependsOnChildren()) { + this.y.postApply(getArea(), relativeTo, 0, y1); + } } } } @Override public void applyPos(IGuiElement parent) { - // after all widgets x, y, width and height have been calculated when can now calculate the absolute position - Area relativeTo = getRelativeTo(); + // 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(); area.applyPos(relativeTo.x, relativeTo.y); Area parentArea = parent.getParentArea(); @@ -375,12 +408,6 @@ public void applyPos(IGuiElement parent) { } } - private boolean dependsOnThis(IWidget child, GuiAxis axis) { - Flex flex = child.getFlex(); - if (flex == null || flex.getRelativeTo() != this.parent.getArea()) return false; - return axis == null ? flex.x.dependsOnParent() || flex.y.dependsOnParent() : axis == GuiAxis.X ? flex.x.dependsOnParent() : flex.y.dependsOnParent(); - } - private Unit getLeft() { return this.x.getStart(); } diff --git a/src/main/java/com/cleanroommc/modularui/widget/sizer/IResizeable.java b/src/main/java/com/cleanroommc/modularui/widget/sizer/IResizeable.java index f58e8a1b..c86407ce 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/IResizeable.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/IResizeable.java @@ -4,14 +4,60 @@ public interface IResizeable { - void apply(IGuiElement guiElement); + void initResizing(); - void postApply(IGuiElement guiElement); + boolean resize(IGuiElement guiElement); + + boolean postResize(IGuiElement guiElement); default void applyPos(IGuiElement guiElement) { } - default boolean isSkip() { - return false; + Area getArea(); + + boolean isXCalculated(); + + boolean isYCalculated(); + + boolean isWidthCalculated(); + + boolean isHeightCalculated(); + + default boolean isSizeCalculated(GuiAxis axis) { + return axis.isHorizontal() ? isWidthCalculated() : isHeightCalculated(); + } + + default boolean isPosCalculated(GuiAxis axis) { + return axis.isHorizontal() ? isXCalculated() : isYCalculated(); + } + + default boolean isFullyCalculated() { + return isXCalculated() && isYCalculated() && isWidthCalculated() && isHeightCalculated(); + } + + void setResized(boolean x, boolean y, boolean w, boolean h); + + default void setPosResized(boolean x, boolean y) { + setResized(x, y, isWidthCalculated(), isHeightCalculated()); + } + + default void setSizeResized(boolean w, boolean h) { + setResized(isXCalculated(), isYCalculated(), w, h); + } + + default void setXResized(boolean v) { + setResized(v, isYCalculated(), isWidthCalculated(), isHeightCalculated()); + } + + default void setYResized(boolean v) { + setResized(isXCalculated(), v, isWidthCalculated(), isHeightCalculated()); + } + + default void setWidthResized(boolean v) { + setResized(isXCalculated(), isYCalculated(), v, isHeightCalculated()); + } + + default void setHeightResized(boolean v) { + setResized(isXCalculated(), isYCalculated(), isWidthCalculated(), v); } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/CategoryList.java b/src/main/java/com/cleanroommc/modularui/widgets/CategoryList.java index 946c7c38..6c72f384 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/CategoryList.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/CategoryList.java @@ -86,6 +86,7 @@ public void calculateHeightAndLayout(boolean calculateParents) { int y = getArea().height; for (IWidget widget : getChildren()) { widget.getArea().ry = y; + widget.resizer().setYResized(true); y += widget instanceof CategoryList && ((CategoryList) widget).expanded ? ((CategoryList) widget).totalHeight : widget.getArea().height; } @@ -141,6 +142,7 @@ public void layoutWidgets() { int y = 0; for (IWidget widget : getChildren()) { widget.getArea().ry = y; + widget.resizer().setYResized(true); y += widget instanceof CategoryList && ((CategoryList) widget).expanded ? ((CategoryList) widget).totalHeight : widget.getArea().height; } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java index 1fcb4b84..388528b0 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java @@ -2,6 +2,7 @@ import com.cleanroommc.modularui.ModularUI; import com.cleanroommc.modularui.ModularUIConfig; +import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.api.drawable.IKey; import com.cleanroommc.modularui.api.value.IBoolValue; @@ -109,6 +110,11 @@ public void setState(int state, boolean setSource) { return Result.IGNORE; } + @Override + public WidgetTheme getWidgetTheme(ITheme theme) { + return theme.getButtonTheme(); + } + @Override public void drawBackground(GuiContext context) { WidgetTheme widgetTheme = getWidgetTheme(context.getTheme()); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java index 69bcd833..27098a19 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ListWidget.java @@ -62,8 +62,7 @@ public static > ListWidget> extends Widget { public PopupMenu(IWidget child) { this.menu = new MenuWrapper(child); - child.flex().relative(this); + child.flex().relative(this.getArea()); this.menu.setEnabled(false); this.children = Collections.singletonList(this.menu); } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java index 10328943..fe0c2818 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java @@ -44,8 +44,7 @@ public boolean isValidSyncHandler(SyncHandler syncHandler) { } @Override - public void resize() { - super.resize(); + public void onResized() { if (this.imageSize < 0) { this.imageSize = getArea().width; } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java index a2ddd68a..5bbc4ef8 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/SliderWidget.java @@ -99,8 +99,7 @@ public void draw(GuiContext context) { } @Override - public void resize() { - super.resize(); + public void onResized() { float sw = this.sliderWidth.getValue(); if (this.sliderWidth.isRelative()) sw *= getArea().width; float sh = this.sliderHeight.getValue(); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java index 9ba8f0a8..5b071ae5 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java @@ -9,7 +9,6 @@ import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.widget.Widget; import com.cleanroommc.modularui.widget.sizer.Box; -import com.cleanroommc.modularui.widget.sizer.Flex; public class TextWidget extends Widget { @@ -53,10 +52,8 @@ public WidgetTheme getWidgetTheme(ITheme theme) { return theme.getWidgetTheme(this.widgetTheme); } - @Override - public int getDefaultHeight() { - Flex parentFlex = getParent().getFlex(); - float maxWidth = parentFlex != null && !parentFlex.xAxisDependsOnChildren() ? getParent().getArea().width : Float.MAX_VALUE; + private TextRenderer simulate() { + float maxWidth = getParent().resizer() != null ? getParent().getArea().width : Float.MAX_VALUE; Box padding = getArea().getPadding(); TextRenderer renderer = TextRenderer.SHARED; renderer.setAlignment(Alignment.TopLeft, maxWidth); @@ -64,20 +61,26 @@ public int getDefaultHeight() { renderer.setScale(this.scale); renderer.setSimulate(true); renderer.draw(this.key.get()); + return renderer; + } + + @Override + public int getDefaultHeight() { + if (getParent().resizer() != null && !getParent().resizer().isWidthCalculated()) { + return -1; + } + TextRenderer renderer = simulate(); + Box padding = getArea().getPadding(); return (int) (renderer.getLastHeight() + padding.vertical() + 0.5f); } @Override public int getDefaultWidth() { - Flex parentFlex = getParent().getFlex(); - float maxWidth = parentFlex != null && !parentFlex.xAxisDependsOnChildren() ? getParent().getArea().width : Float.MAX_VALUE; + if (getParent().resizer() != null && !getParent().resizer().isWidthCalculated()) { + return -1; + } + TextRenderer renderer = simulate(); Box padding = getArea().getPadding(); - TextRenderer renderer = TextRenderer.SHARED; - renderer.setAlignment(Alignment.TopLeft, maxWidth); - renderer.setPos(padding.left, padding.top); - renderer.setScale(this.scale); - renderer.setSimulate(true); - renderer.draw(this.key.get()); return (int) (renderer.getLastWidth() + padding.horizontal() + 0.5f); } 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 8d38bfd0..c783bff7 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/layout/Column.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/layout/Column.java @@ -20,8 +20,8 @@ public Column() { @Override public void layoutWidgets() { + boolean hasHeight = resizer().isHeightCalculated(); int height = getArea().height; - int width = getArea().width; Box padding = getArea().getPadding(); int maxWidth = 0; @@ -41,23 +41,26 @@ public void layoutWidgets() { totalHeight += widget.getArea().requestedHeight(); } - if (expandedAmount > 0) { + if (expandedAmount > 0 && hasHeight) { int newHeight = (height - totalHeight - padding.vertical()) / expandedAmount; for (IWidget widget : getChildren()) { // exclude self positioned (Y) children if (widget.flex().hasYPos()) continue; if (widget.flex().isExpanded()) { - widget.getArea().h(newHeight); + widget.getArea().height = newHeight; + widget.resizer().setHeightResized(true); } } } // calculate start y int lastY = 0; - if (this.maa == MainAxisAlignment.CENTER) { - lastY = (int) (height / 2f - totalHeight / 2f); - } else if (this.maa == MainAxisAlignment.END) { - lastY = height - totalHeight; + if (hasHeight) { + if (this.maa == MainAxisAlignment.CENTER) { + lastY = (int) (height / 2f - totalHeight / 2f); + } else if (this.maa == MainAxisAlignment.END) { + lastY = height - totalHeight; + } } lastY = Math.max(lastY, padding.top) - getArea().getMargin().top; @@ -65,25 +68,40 @@ public void layoutWidgets() { // exclude self positioned (Y) children if (widget.flex().hasYPos()) continue; Box margin = widget.getArea().getMargin(); - // don't align auto positioned (X) children in X - if (!widget.flex().hasXPos()) { - int x = 0; - if (this.caa == CrossAxisAlignment.CENTER) { - x = (int) (width / 2f - widget.getArea().requestedWidth() / 2f); - } else if (this.caa == CrossAxisAlignment.END) { - x = width - widget.getArea().requestedWidth(); - } - x = Math.max(x, padding.left + margin.left); - widget.getArea().rx = x; - } // set calculated relative Y pos and set bottom margin for next widget widget.getArea().ry = lastY + margin.top; lastY += widget.getArea().requestedHeight(); - if (this.maa == MainAxisAlignment.SPACE_BETWEEN) { + if (hasHeight && this.maa == MainAxisAlignment.SPACE_BETWEEN) { lastY += (height - totalHeight) / (getChildren().size() - 1); } + widget.resizer().setYResized(true); + } + } + + @Override + public void postLayoutWidgets() { + int width = getArea().width; + Box padding = getArea().getPadding(); + boolean hasWidth = resizer().isWidthCalculated(); + for (IWidget widget : getChildren()) { + // exclude self positioned (Y) children + if (widget.flex().hasYPos()) continue; + 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; + 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; + } + } + widget.getArea().rx = x; + widget.resizer().setXResized(true); + } } } 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 212247e3..08e82986 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/layout/Grid.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/layout/Grid.java @@ -83,6 +83,7 @@ public void layoutWidgets() { if (child != null) { child.getArea().rx = (int) (x + width * 0.5 - child.getArea().width * 0.5); child.getArea().ry = (int) (y + height * 0.5 - child.getArea().height * 0.5); + child.resizer().setPosResized(true, true); } x += width; } 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 ed3bab95..5924f576 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/layout/OrganizedPanel.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/layout/OrganizedPanel.java @@ -64,7 +64,7 @@ public ParentWidget getBody() { } @Override - public void resize() { + public void beforeResize() { int top = 0; int bot = 0; this.body.flex().reset(); @@ -91,7 +91,6 @@ public void resize() { this.rightSide.right(0).width(this.rightSideWidth).top(top).bottom(bot); this.body.right(this.rightSideWidth); } - super.resize(); } public OrganizedPanel header(IWidget widget) { 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 b8766b56..f7d06417 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/layout/Row.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/layout/Row.java @@ -20,8 +20,8 @@ public Row() { @Override public void layoutWidgets() { + boolean hasWidth = resizer().isWidthCalculated(); int width = getArea().width; - int height = getArea().height; Box padding = getArea().getPadding(); int maxHeight = 0; @@ -40,23 +40,26 @@ public void layoutWidgets() { totalWidth += widget.getArea().requestedWidth(); } - if (expandedAmount > 0) { + if (expandedAmount > 0 && hasWidth) { int newWidth = (width - totalWidth - padding.horizontal()) / expandedAmount; for (IWidget widget : getChildren()) { // exclude self positioned (X) children if (widget.flex().hasXPos()) continue; if (widget.flex().isExpanded()) { - widget.getArea().w(newWidth); + widget.getArea().width = newWidth; + widget.resizer().setWidthResized(true); } } } // calculate start y int lastX = 0; - if (this.maa == MainAxisAlignment.CENTER) { - lastX = (int) (width / 2f - totalWidth / 2f); - } else if (this.maa == MainAxisAlignment.END) { - lastX = width - totalWidth; + if (hasWidth) { + if (this.maa == MainAxisAlignment.CENTER) { + lastX = (int) (width / 2f - totalWidth / 2f); + } else if (this.maa == MainAxisAlignment.END) { + lastX = width - totalWidth; + } } lastX = Math.max(lastX, padding.left) - getArea().getMargin().left; @@ -64,28 +67,43 @@ public void layoutWidgets() { // exclude self positioned (X) children if (widget.flex().hasXPos()) continue; Box margin = widget.getArea().getMargin(); - // don't align auto positioned (Y) children in Y - if (!widget.flex().hasYPos()) { - int y = 0; - if (this.caa == CrossAxisAlignment.CENTER) { - y = (int) (height / 2f - widget.getArea().requestedHeight() / 2f); - } else if (this.caa == CrossAxisAlignment.END) { - y = height - widget.getArea().requestedHeight(); - } - y = Math.max(y, padding.top); - widget.getArea().ry = y; - } // set calculated relative Y pos and set bottom margin for next widget widget.getArea().rx = lastX + margin.left; + widget.resizer().setXResized(true); lastX += widget.getArea().requestedWidth(); - if (this.maa == MainAxisAlignment.SPACE_BETWEEN) { + if (hasWidth && this.maa == MainAxisAlignment.SPACE_BETWEEN) { lastX += (width - totalWidth) / (getChildren().size() - 1); } } } + @Override + public void postLayoutWidgets() { + int height = getArea().height; + Box padding = getArea().getPadding(); + boolean hasHeight = resizer().isWidthCalculated(); + for (IWidget widget : getChildren()) { + // exclude self positioned (X) children + if (widget.flex().hasXPos()) continue; + Box margin = widget.getArea().getMargin(); + // don't align auto positioned (Y) children in Y + if (!widget.flex().hasYPos()) { + int y = margin.top + padding.top; + if (hasHeight) { + if (this.caa == CrossAxisAlignment.CENTER) { + y = (int) (height / 2f - widget.getArea().height / 2f); + } else if (this.caa == CrossAxisAlignment.END) { + y = height - widget.getArea().height - margin.bottom - padding.bottom; + } + } + widget.getArea().ry = y; + widget.resizer().setYResized(true); + } + } + } + public Row crossAxisAlignment(CrossAxisAlignment caa) { this.caa = caa; return this; 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 b1a55b94..c1d4aa40 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/slot/ModularSlot.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/slot/ModularSlot.java @@ -67,6 +67,12 @@ public void onSlotChanged() { this.changeListener.accept(getStack()); } + @Override + public void putStack(@NotNull ItemStack stack) { + if (ItemStack.areItemStacksEqual(stack, getStack())) return; + super.putStack(stack); + } + @Nullable @SideOnly(Side.CLIENT) @Override