From 394748b747b51ac0aad11770d2ec03c27826ea20 Mon Sep 17 00:00:00 2001 From: brachy84 <45517902+brachy84@users.noreply.github.com> Date: Fri, 29 Sep 2023 11:01:26 +0200 Subject: [PATCH] Bogosort compat (#32) * update bogosorter api * fix widgets using deprecated draw method * open client screens on next tick * fix sync value collecting * add sort buttons to sortable slot groups * enable test guis via config --- .../bogosorter/api/IBogoSortAPI.java | 139 ++++++++++++++++++ .../bogosorter/api/IButtonPos.java | 74 ++++++++++ .../bogosorter/api/ICustomInsertable.java | 11 ++ .../bogosorter/api/IPosSetter.java | 37 +++++ .../com/cleanroommc/bogosorter/api/ISlot.java | 41 ++++++ .../bogosorter/api/ISlotGroup.java | 59 ++++++++ .../bogosorter/api/ISortableContainer.java | 22 ++- .../api/ISortingContextBuilder.java | 40 ++++- .../cleanroommc/bogosorter/api/SortType.java | 11 ++ .../modularui/ClientEventHandler.java | 8 + .../com/cleanroommc/modularui/ModularUI.java | 10 +- .../modularui/ModularUIConfig.java | 6 +- .../modularui/api/widget/IWidget.java | 5 +- .../modularui/manager/GuiManager.java | 21 ++- .../modularui/screen/JeiSettings.java | 3 +- .../modularui/screen/ModularContainer.java | 34 ++++- .../cleanroommc/modularui/test/TestTile.java | 8 +- .../modularui/widget/WidgetTree.java | 21 ++- .../modularui/widgets/FluidSlot.java | 3 +- .../modularui/widgets/ItemSlot.java | 3 +- .../modularui/widgets/ProgressWidget.java | 3 +- .../widgets/ScrollingTextWidget.java | 3 +- .../modularui/widgets/SortButtons.java | 90 ++++++++++++ .../modularui/widgets/TextWidget.java | 2 +- 24 files changed, 618 insertions(+), 36 deletions(-) create mode 100644 src/api/java/com/cleanroommc/bogosorter/api/IBogoSortAPI.java create mode 100644 src/api/java/com/cleanroommc/bogosorter/api/IButtonPos.java create mode 100644 src/api/java/com/cleanroommc/bogosorter/api/ICustomInsertable.java create mode 100644 src/api/java/com/cleanroommc/bogosorter/api/IPosSetter.java create mode 100644 src/api/java/com/cleanroommc/bogosorter/api/ISlot.java create mode 100644 src/api/java/com/cleanroommc/bogosorter/api/ISlotGroup.java create mode 100644 src/api/java/com/cleanroommc/bogosorter/api/SortType.java create mode 100644 src/main/java/com/cleanroommc/modularui/widgets/SortButtons.java diff --git a/src/api/java/com/cleanroommc/bogosorter/api/IBogoSortAPI.java b/src/api/java/com/cleanroommc/bogosorter/api/IBogoSortAPI.java new file mode 100644 index 00000000..2c663d47 --- /dev/null +++ b/src/api/java/com/cleanroommc/bogosorter/api/IBogoSortAPI.java @@ -0,0 +1,139 @@ +package com.cleanroommc.bogosorter.api; + +import net.minecraft.inventory.Container; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import java.util.Comparator; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; + +public interface IBogoSortAPI { + + static IBogoSortAPI getInstance() { + throw new UnsupportedOperationException(); + } + + /** + * Register a function that converts a {@link Slot} to a {@link ISlot}. Useful if modders messed up. + * + * @param clazz slot class + * @param function converter function + * @param slot type + */ + void addSlotGetter(Class clazz, Function function); + + /** + * Registers a function which handles slot insertions in a custom way. + * + * @param clazz container class + * @param insertable custom insertion function + */ + void addCustomInsertable(Class clazz, ICustomInsertable insertable); + + /** + * Adds sorting compat for a container class + * + * @param clazz container class + * @param builder sorting compat builder + * @param container type + */ + void addCompat(Class clazz, BiConsumer builder); + + /** + * Adds sorting compat for a container class. + * Is useful when you don't have access to the super class of {@link T} + * + * @param clazz container class + * @param builder sorting compat builder + * @param container type + */ + void addCompatSimple(Class clazz, BiConsumer builder); + + /** + * Registers a function to figure out where to place player inventory sort buttons. + * + * @param clazz class of the container + * @param buttonPos pos function or null if no buttons are desired + * @throws IllegalArgumentException if the class is not of a container + */ + void addPlayerSortButtonPosition(Class clazz, IPosSetter buttonPos); + + /** + * Removes sorting compat for a container class + * + * @param clazz container class + * @param container type + */ + void removeCompat(Class clazz); + + /** + * Registers a sorting rule for items + * + * @param itemComparator comparator + */ + void registerItemSortingRule(String key, Comparator itemComparator); + + /** + * Registers a sorting rule for NBT tags + * + * @param tagPath path of the nbt tag. Separate sub tags with '/' + * @param comparator comparator sorting the tags based on tagPath + */ + void registerNbtSortingRule(String key, String tagPath, Comparator comparator); + + /** + * Registers a sorting rule for NBT tags + * + * @param tagPath path of the nbt tag. Separate sub tags with '/' + * @param expectedType the expected NBT tag id. Will be automatically compared + * @see net.minecraftforge.common.util.Constants.NBT for expectedType + */ + void registerNbtSortingRule(String key, String tagPath, int expectedType); + + /** + * Registers a sorting rule for NBT tags + * + * @param tagPath path of the nbt tag. Separate sub tags with '/' + * @param expectedType the expected NBT tag id + * @param comparator comparator of the type converted by converter + * @param converter converts the tag found at the tagPath for the comparator + * @see net.minecraftforge.common.util.Constants.NBT for expectedType + */ + void registerNbtSortingRule(String key, String tagPath, int expectedType, Comparator comparator, Function converter); + + /** + * Opens the bogosort config gui + */ + @SideOnly(Side.CLIENT) + void openConfigGui(); + + /** + * Tries to sort a slot group with the given slot. + * + * @param slot slot of slot group to sort + * @return if the slot group will be sorted (on server side) + */ + @SideOnly(Side.CLIENT) + boolean sortSlotGroup(Slot slot); + + /** + * Turns a slot into a generic interface slot + * + * @param slot slot + * @return generic slot + */ + ISlot getSlot(Slot slot); + + /** + * Transforms a list of slots using {@link #getSlot(Slot)} + * + * @param slots list of slots + * @return list of generic slots + */ + List getSlots(List slots); +} diff --git a/src/api/java/com/cleanroommc/bogosorter/api/IButtonPos.java b/src/api/java/com/cleanroommc/bogosorter/api/IButtonPos.java new file mode 100644 index 00000000..94fd0fcb --- /dev/null +++ b/src/api/java/com/cleanroommc/bogosorter/api/IButtonPos.java @@ -0,0 +1,74 @@ +package com.cleanroommc.bogosorter.api; + +/** + * Determines the true sort button pos for slot groups. + */ +public interface IButtonPos { + + void setEnabled(boolean enabled); + + /** + * Sets position where the buttons will be placed. + * + * @param x x pos + * @param y y pos + */ + void setPos(int x, int y); + + /** + * Sets the alignment of the buttons. Determines where the buttons are placed relative to the pos. + * For example if the alignment is bottom left, then the pos will be the bottom left corner of the buttons. + * + * @param alignment alignment + */ + void setAlignment(Alignment alignment); + + /** + * Sets the layout of the buttons. Horizontal is next to each other and vertical is on top of each other. + * + * @param layout layout + */ + void setLayout(Layout layout); + + default void setTopLeft() { + setAlignment(Alignment.TOP_LEFT); + } + + default void setTopRight() { + setAlignment(Alignment.TOP_RIGHT); + } + + default void setBottomLeft() { + setAlignment(Alignment.BOTTOM_LEFT); + } + + default void setBottomRight() { + setAlignment(Alignment.BOTTOM_RIGHT); + } + + default void setHorizontal() { + setLayout(Layout.HORIZONTAL); + } + + default void setVertical() { + setLayout(Layout.VERTICAL); + } + + boolean isEnabled(); + + int getX(); + + int getY(); + + Alignment getAlignment(); + + Layout getLayout(); + + enum Alignment { + TOP_RIGHT, TOP_LEFT, BOTTOM_RIGHT, BOTTOM_LEFT + } + + enum Layout { + HORIZONTAL, VERTICAL + } +} diff --git a/src/api/java/com/cleanroommc/bogosorter/api/ICustomInsertable.java b/src/api/java/com/cleanroommc/bogosorter/api/ICustomInsertable.java new file mode 100644 index 00000000..078eda7f --- /dev/null +++ b/src/api/java/com/cleanroommc/bogosorter/api/ICustomInsertable.java @@ -0,0 +1,11 @@ +package com.cleanroommc.bogosorter.api; + +import net.minecraft.inventory.Container; +import net.minecraft.item.ItemStack; + +import java.util.List; + +public interface ICustomInsertable { + + ItemStack insert(Container container, List slots, ItemStack itemStack, boolean emptyOnly); +} diff --git a/src/api/java/com/cleanroommc/bogosorter/api/IPosSetter.java b/src/api/java/com/cleanroommc/bogosorter/api/IPosSetter.java new file mode 100644 index 00000000..74c967fb --- /dev/null +++ b/src/api/java/com/cleanroommc/bogosorter/api/IPosSetter.java @@ -0,0 +1,37 @@ +package com.cleanroommc.bogosorter.api; + +/** + * A function to set the sort button pos of {@link ISlotGroup}'s. + */ +@FunctionalInterface +public interface IPosSetter { + + IPosSetter TOP_RIGHT_HORIZONTAL = (slotGroup, buttonPos) -> { + if (slotGroup.getSlots().size() < slotGroup.getRowSize()) { + buttonPos.setPos(-1000, -1000); + } else { + ISlot topRight = slotGroup.getSlots().get(slotGroup.getRowSize() - 1); + buttonPos.setPos(topRight.bogo$getX() + 17, topRight.bogo$getY() - 2); + } + }; + + IPosSetter TOP_RIGHT_VERTICAL = (slotGroup, buttonPos) -> { + if (slotGroup.getSlots().size() < slotGroup.getRowSize()) { + buttonPos.setPos(-1000, -1000); + } else { + ISlot topRight = slotGroup.getSlots().get(slotGroup.getRowSize() - 1); + buttonPos.setVertical(); + buttonPos.setTopLeft(); + buttonPos.setPos(topRight.bogo$getX() + 18, topRight.bogo$getY() - 1); + } + }; + + /** + * Called every frame, to make sure the buttons are always in the right position. + * Call setters on {@link IButtonPos} here. + * + * @param slotGroup slot group of the buttons + * @param buttonPos the mutable button pos + */ + void setButtonPos(ISlotGroup slotGroup, IButtonPos buttonPos); +} diff --git a/src/api/java/com/cleanroommc/bogosorter/api/ISlot.java b/src/api/java/com/cleanroommc/bogosorter/api/ISlot.java new file mode 100644 index 00000000..bee88c32 --- /dev/null +++ b/src/api/java/com/cleanroommc/bogosorter/api/ISlot.java @@ -0,0 +1,41 @@ +package com.cleanroommc.bogosorter.api; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +/** + * A custom slot interface. Useful if mods have a slot that does not implement the necessary methods. + * {@link Slot} implements this interface via mixin. + */ +public interface ISlot { + + Slot bogo$getRealSlot(); + + int bogo$getX(); + + int bogo$getY(); + + int bogo$getSlotNumber(); + + int bogo$getSlotIndex(); + + IInventory bogo$getInventory(); + + void bogo$putStack(ItemStack itemStack); + + ItemStack bogo$getStack(); + + int bogo$getMaxStackSize(ItemStack itemStack); + + int bogo$getItemStackLimit(ItemStack itemStack); + + boolean bogo$isEnabled(); + + boolean bogo$isItemValid(ItemStack stack); + + boolean bogo$canTakeStack(EntityPlayer player); + + void bogo$onSlotChanged(); +} diff --git a/src/api/java/com/cleanroommc/bogosorter/api/ISlotGroup.java b/src/api/java/com/cleanroommc/bogosorter/api/ISlotGroup.java new file mode 100644 index 00000000..12c64d11 --- /dev/null +++ b/src/api/java/com/cleanroommc/bogosorter/api/ISlotGroup.java @@ -0,0 +1,59 @@ +package com.cleanroommc.bogosorter.api; + +import java.util.List; + +/** + * A slot group is a list of slots which is organized in a rectangle. + * It doesn't necessarily need to be a rectangle, but you might run into issues if the shape is more complex. + */ +public interface ISlotGroup { + + /** + * An unmodifiable view of all the slots of this group. + * + * @return all slots + */ + List getSlots(); + + /** + * Returns how many slots are in row. This is mostly used to determine the button position with + * {@link IPosSetter#TOP_RIGHT_VERTICAL} and {@link IPosSetter#TOP_RIGHT_HORIZONTAL}. If the slot group shape is + * not rectangular, try to return the row size of the first row. + * + * @return slots per row + */ + int getRowSize(); + + /** + * Returns the priority that this group takes when items are transferred via shortcuts. + * + * @return priority + */ + int getPriority(); + + /** + * Returns if this slot groups only consists of player inventory slots. It does not need to be the full player + * inventory. The player hotbar is usually not part of this. + * + * @return if all slots are part of the player inventory + */ + boolean isPlayerInventory(); + + /** + * Sets the priority of this slot group. Can determine where items are transferred first with shortcuts. + * Returns itself to be used in a builder like manner. + * + * @param priority priority + * @return this + */ + ISlotGroup priority(int priority); + + /** + * Sets a custom function to determine the position of sort buttons. Default is top right corner. + * Returns itself to be used in a builder like manner. + * + * @param posSetter pos function or null if no buttons are desired + * @return this + */ + ISlotGroup buttonPosSetter(IPosSetter posSetter); +} diff --git a/src/api/java/com/cleanroommc/bogosorter/api/ISortableContainer.java b/src/api/java/com/cleanroommc/bogosorter/api/ISortableContainer.java index e210f686..e4efa3de 100644 --- a/src/api/java/com/cleanroommc/bogosorter/api/ISortableContainer.java +++ b/src/api/java/com/cleanroommc/bogosorter/api/ISortableContainer.java @@ -1,9 +1,29 @@ package com.cleanroommc.bogosorter.api; +import java.util.function.BiConsumer; + /** - * implement on {@link net.minecraft.inventory.Container} + * This interface marks a {@link net.minecraft.inventory.Container} as sortable. + * Implementing this interface takes priority over {@link IBogoSortAPI#addCompat(Class, BiConsumer)} and + * {@link IBogoSortAPI#addPlayerSortButtonPosition(Class, IPosSetter)}, but has the same effect. */ public interface ISortableContainer { + /** + * Is called when the container is opened. Add slot groups this container adds here. + * Do not add the player inventory here (except if Bogosorter doesn't do it automatically). + * + * @param builder builder to build slot groups + */ void buildSortingContext(ISortingContextBuilder builder); + + /** + * Determines where the buttons of the player inventory (if exists) should be placed. + * Returning null will result in no sort buttons. + * + * @return player inventory sort button position function + */ + default IPosSetter getPlayerButtonPosSetter() { + return IPosSetter.TOP_RIGHT_HORIZONTAL; + } } diff --git a/src/api/java/com/cleanroommc/bogosorter/api/ISortingContextBuilder.java b/src/api/java/com/cleanroommc/bogosorter/api/ISortingContextBuilder.java index 91bc4f2b..f59c5766 100644 --- a/src/api/java/com/cleanroommc/bogosorter/api/ISortingContextBuilder.java +++ b/src/api/java/com/cleanroommc/bogosorter/api/ISortingContextBuilder.java @@ -1,17 +1,49 @@ package com.cleanroommc.bogosorter.api; +import net.minecraft.inventory.Container; import net.minecraft.inventory.Slot; import java.util.List; /** - * Meant for use in {@link ISortableContainer#buildSortingContext(ISortingContextBuilder)} + * A helper interface to create {@link ISlotGroup} instances. + * Meant to be used in {@link ISortableContainer#buildSortingContext(ISortingContextBuilder)} */ public interface ISortingContextBuilder { - ISortingContextBuilder addSlotGroup(Slot[][] slotGroup); + /** + * Creates and registers a slot group with a list of slots. + * + * @param slots slot list + * @param rowSize This is mostly used to determine the button position with {@link IPosSetter#TOP_RIGHT_VERTICAL} + * and {@link IPosSetter#TOP_RIGHT_HORIZONTAL}. If the slot group shape is not rectangular, + * try to use the row size of the first row. + * @return the created slot group + */ + ISlotGroup addSlotGroupOf(List slots, int rowSize); - ISortingContextBuilder addSlotGroup(int rowSize, int startIndex, int endIndex); + /** + * Creates and registers a slot group with a list of slots. + * + * @param slots slot list + * @param rowSize This is mostly used to determine the button position with {@link IPosSetter#TOP_RIGHT_VERTICAL} + * and {@link IPosSetter#TOP_RIGHT_HORIZONTAL}. If the slot group shape is not rectangular, + * try to use the row size of the first row. + * @return the created slot group + */ + ISlotGroup addSlotGroup(List slots, int rowSize); - ISortingContextBuilder addSlotGroup(int rowSize, List slots); + /** + * Creates and registers a slot group based on a start and end index. + * + * @param startIndex index of the first slot (including) + * @param endIndex index of the end slot (excluding) + * @param rowSize This is mostly used to determine the button position with {@link IPosSetter#TOP_RIGHT_VERTICAL} + * and {@link IPosSetter#TOP_RIGHT_HORIZONTAL}. If the slot group shape is not rectangular, + * try to use the row size of the first row. + * @return the created slot group + */ + ISlotGroup addSlotGroup(int startIndex, int endIndex, int rowSize); + + Container getContainer(); } diff --git a/src/api/java/com/cleanroommc/bogosorter/api/SortType.java b/src/api/java/com/cleanroommc/bogosorter/api/SortType.java new file mode 100644 index 00000000..f30f7ffc --- /dev/null +++ b/src/api/java/com/cleanroommc/bogosorter/api/SortType.java @@ -0,0 +1,11 @@ +package com.cleanroommc.bogosorter.api; + +public enum SortType { + + MOD, + ID, + META, + COUNT, + NBT, + OREDICT +} diff --git a/src/main/java/com/cleanroommc/modularui/ClientEventHandler.java b/src/main/java/com/cleanroommc/modularui/ClientEventHandler.java index 4d0206b7..8ea95e1b 100644 --- a/src/main/java/com/cleanroommc/modularui/ClientEventHandler.java +++ b/src/main/java/com/cleanroommc/modularui/ClientEventHandler.java @@ -1,6 +1,7 @@ package com.cleanroommc.modularui; import com.cleanroommc.modularui.drawable.Stencil; +import com.cleanroommc.modularui.manager.GuiManager; import com.cleanroommc.modularui.screen.ModularScreen; import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; @@ -31,4 +32,11 @@ public static void preDraw(TickEvent.RenderTickEvent event) { } Stencil.reset(); } + + @SubscribeEvent + public static void onClientTick(TickEvent.ClientTickEvent event) { + if (event.phase == TickEvent.Phase.START) { + GuiManager.checkQueuedScreen(); + } + } } diff --git a/src/main/java/com/cleanroommc/modularui/ModularUI.java b/src/main/java/com/cleanroommc/modularui/ModularUI.java index 770955b0..7c0b9c53 100644 --- a/src/main/java/com/cleanroommc/modularui/ModularUI.java +++ b/src/main/java/com/cleanroommc/modularui/ModularUI.java @@ -42,7 +42,11 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -@Mod(modid = ModularUI.ID, name = ModularUI.NAME, version = ModularUI.VERSION, dependencies = "required-after:mixinbooter@[5.0,);") +@Mod(modid = ModularUI.ID, + name = ModularUI.NAME, + version = ModularUI.VERSION, + dependencies = "required-after:mixinbooter@[5.0,);" + + "after:bogorter@[1.3.4,);") public class ModularUI { public static final String ID = Tags.ID; @@ -74,7 +78,7 @@ public void preInit(FMLPreInitializationEvent event) { preInitClient(); } - if (FMLLaunchHandler.isDeobfuscatedEnvironment()) { + if (ModularUIConfig.enabledTestGuis) { MinecraftForge.EVENT_BUS.register(TestBlock.class); TestBlock.preInit(); } @@ -95,7 +99,7 @@ private void preInitClient() { MinecraftForge.EVENT_BUS.register(ClientEventHandler.class); MinecraftForge.EVENT_BUS.register(KeyBindHandler.class); - if (FMLLaunchHandler.isDeobfuscatedEnvironment()) { + if (ModularUIConfig.enabledTestGuis) { MinecraftForge.EVENT_BUS.register(EventHandler.class); } diff --git a/src/main/java/com/cleanroommc/modularui/ModularUIConfig.java b/src/main/java/com/cleanroommc/modularui/ModularUIConfig.java index 16a35b7b..920bf15c 100644 --- a/src/main/java/com/cleanroommc/modularui/ModularUIConfig.java +++ b/src/main/java/com/cleanroommc/modularui/ModularUIConfig.java @@ -20,7 +20,7 @@ public class ModularUIConfig { public static int panelOpenCloseAnimationTime = 8; // Tooltip - @Config.Comment("If panels should be placed next to a widgets panel or the widget by default.") + @Config.Comment("If tooltips should be placed next to a widgets panel or the widget by default.") public static boolean placeNextToPanelByDefault = true; // Default direction @Config.Comment("Default tooltip position around the widget or its panel.") @@ -32,6 +32,10 @@ public class ModularUIConfig { @Config.Comment("If true and not specified otherwise, screens will try to use the 'vanilla_dark' theme.") public static boolean useDarkThemeByDefault = false; + @Config.RequiresMcRestart + @Config.Comment("Enables a test block, test item with a test gui and opening a gui by right clicking a diamond.") + public static boolean enabledTestGuis = FMLLaunchHandler.isDeobfuscatedEnvironment(); + public static boolean placeTooltipNextToPanel() { return NetworkUtils.isDedicatedClient() && placeNextToPanelByDefault && Minecraft.getMinecraft().gameSettings.guiScale > 0; } 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 399fa558..20204575 100644 --- a/src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java +++ b/src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java @@ -50,11 +50,12 @@ public interface IWidget extends IGuiElement { /** * Draws additional stuff in this widget. - * x = 0 and y = 0 is now in the top left corner of this widget + * x = 0 and y = 0 is now in the top left corner of this widget. + * Do NOT override this method, it is never called. Use {@link #draw(GuiContext, WidgetTheme)} instead. * * @param context gui context */ - @ApiStatus.ScheduledForRemoval(inVersion = "2.3.0") + @ApiStatus.NonExtendable @Deprecated @Override default void draw(GuiContext context) { diff --git a/src/main/java/com/cleanroommc/modularui/manager/GuiManager.java b/src/main/java/com/cleanroommc/modularui/manager/GuiManager.java index 21f4c8f9..9afd30ba 100644 --- a/src/main/java/com/cleanroommc/modularui/manager/GuiManager.java +++ b/src/main/java/com/cleanroommc/modularui/manager/GuiManager.java @@ -12,6 +12,7 @@ import net.minecraftforge.fml.common.network.IGuiHandler; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; public final class GuiManager implements IGuiHandler { @@ -19,6 +20,8 @@ public final class GuiManager implements IGuiHandler { public static final GuiManager INSTANCE = new GuiManager(); private final Int2ObjectOpenHashMap guiInfos = new Int2ObjectOpenHashMap<>(); + private static ModularScreen queuedClientScreen; + private static JeiSettings queuedJeiSettings; private GuiManager() { } @@ -38,9 +41,21 @@ public static void openClientUI(EntityPlayer player, ModularScreen screen, JeiSe ModularUI.LOGGER.info("Tried opening client ui on server!"); return; } - screen.getContext().setJeiSettings(jeiSettings); - GuiScreenWrapper screenWrapper = new GuiScreenWrapper(new ModularContainer(), screen); - FMLCommonHandler.instance().showGuiScreen(screenWrapper); + // we need to queue the screen, because we might break the current gui + queuedClientScreen = screen; + queuedJeiSettings = jeiSettings; + } + + @SideOnly(Side.CLIENT) + @ApiStatus.Internal + public static void checkQueuedScreen() { + if (queuedClientScreen != null) { + queuedClientScreen.getContext().setJeiSettings(queuedJeiSettings); + GuiScreenWrapper screenWrapper = new GuiScreenWrapper(new ModularContainer(), queuedClientScreen); + FMLCommonHandler.instance().showGuiScreen(screenWrapper); + queuedClientScreen = null; + queuedJeiSettings = null; + } } @Nullable diff --git a/src/main/java/com/cleanroommc/modularui/screen/JeiSettings.java b/src/main/java/com/cleanroommc/modularui/screen/JeiSettings.java index 62419b57..cf972d52 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/JeiSettings.java +++ b/src/main/java/com/cleanroommc/modularui/screen/JeiSettings.java @@ -98,7 +98,8 @@ public List> getAllGhostIngredientTargets( continue; } if (widget.isEnabled() && slot.castGhostIngredientIfValid(ingredient) != null) { - ghostHandlerTargets.add((IGhostIngredientHandler.Target) new GhostIngredientTarget<>(widget, slot)); + JeiGhostIngredientSlot slotWithType = (JeiGhostIngredientSlot) slot; + ghostHandlerTargets.add(new GhostIngredientTarget<>(widget, slotWithType)); } } return ghostHandlerTargets; diff --git a/src/main/java/com/cleanroommc/modularui/screen/ModularContainer.java b/src/main/java/com/cleanroommc/modularui/screen/ModularContainer.java index bfe5c246..445df384 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/ModularContainer.java +++ b/src/main/java/com/cleanroommc/modularui/screen/ModularContainer.java @@ -1,5 +1,6 @@ package com.cleanroommc.modularui.screen; +import com.cleanroommc.bogosorter.api.IPosSetter; import com.cleanroommc.bogosorter.api.ISortableContainer; import com.cleanroommc.bogosorter.api.ISortingContextBuilder; import com.cleanroommc.modularui.ModularUI; @@ -21,6 +22,7 @@ import net.minecraftforge.items.wrapper.PlayerInvWrapper; import net.minecraftforge.items.wrapper.PlayerMainInvWrapper; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -111,6 +113,27 @@ public void registerSlot(ModularSlot slot) { } } + @Contract("null, null -> fail") + @NotNull + @ApiStatus.Internal + public SlotGroup validateSlotGroup(@Nullable String slotGroupName, @Nullable SlotGroup slotGroup) { + if (slotGroup != null) { + if (getSyncManager().getSlotGroup(slotGroup.getName()) == null) { + throw new IllegalArgumentException("Slot group is not registered in the GUI."); + } + return slotGroup; + } + if (slotGroupName != null) { + slotGroup = getSyncManager().getSlotGroup(slotGroupName); + if (slotGroup == null) { + throw new IllegalArgumentException("Can't find slot group for name " + slotGroupName); + } + return slotGroup; + } + throw new IllegalArgumentException("Either the slot group or the name must not be null!"); + } + + public GuiSyncManager getSyncManager() { if (this.guiSyncManager == null) { throw new IllegalStateException("GuiSyncManager is not available for client only GUI's."); @@ -210,9 +233,16 @@ private static boolean isPlayerSlot(Slot slot) { @Override public void buildSortingContext(ISortingContextBuilder builder) { for (SlotGroup slotGroup : this.getSyncManager().getSlotGroups()) { - if (slotGroup.isAllowSorting()) { - builder.addSlotGroup(slotGroup.getRowSize(), slotGroup.getSlots()); + if (slotGroup.isAllowSorting() && !isPlayerSlot(slotGroup.getSlots().get(0))) { + builder.addSlotGroupOf(slotGroup.getSlots(), slotGroup.getRowSize()) + .buttonPosSetter(null) + .priority(slotGroup.getShiftClickPriority()); } } } + + @Override + public IPosSetter getPlayerButtonPosSetter() { + return null; + } } diff --git a/src/main/java/com/cleanroommc/modularui/test/TestTile.java b/src/main/java/com/cleanroommc/modularui/test/TestTile.java index 735067c9..904be414 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestTile.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestTile.java @@ -8,6 +8,7 @@ import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.Tooltip; import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.value.BoolValue; @@ -182,7 +183,10 @@ public ModularPanel buildUI(GuiCreationContext guiCreationContext, GuiSyncManage return new ItemSlot().slot(SyncHandlers.itemSlot(this.bigInventory, index).slotGroup("item_inv")); }) .build() - .marginBottom(2)) + .marginBottom(2) + .child(new SortButtons() + .slotGroup("item_inv") + .right(0).top(-11))) .child(SlotGroupWidget.builder() .row("FII") .row("FII") @@ -349,7 +353,7 @@ private SpecialButton(AnimatedText animatedKey) { } @Override - public void draw(GuiContext context) { + public void draw(GuiContext context, WidgetTheme widgetTheme) { this.animatedKey.draw(context, 0, 0, getArea().w(), getArea().h()); } diff --git a/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java b/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java index b9ae6f75..4c6248df 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java +++ b/src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java @@ -318,19 +318,16 @@ public static T findParent(IWidget parent, Class type) { } public static void collectSyncValues(GuiSyncManager syncHandler, ModularPanel panel) { - collectSyncValues(syncHandler, panel.getName(), panel, new AtomicInteger(0)); - } - - private static > void collectSyncValues(GuiSyncManager syncHandler, String syncSuffix, T parent, AtomicInteger id) { - if (parent.isSynced()) { - syncHandler.syncValue(GuiSyncManager.AUTO_SYNC_PREFIX + syncSuffix, id.getAndIncrement(), parent.getSyncHandler()); - } - if (parent.hasChildren()) { - for (IWidget widget : parent.getChildren()) { - if (widget instanceof ISynced) { - collectSyncValues(syncHandler, syncSuffix, (T) widget, id); + AtomicInteger id = new AtomicInteger(0); + String syncKey = GuiSyncManager.AUTO_SYNC_PREFIX + panel.getName(); + foreachChildByLayer(panel, widget -> { + if (widget instanceof ISynced) { + ISynced synced = (ISynced) widget; + if (synced.isSynced()) { + syncHandler.syncValue(syncKey, id.getAndIncrement(), synced.getSyncHandler()); } } - } + return true; + }, true); } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/FluidSlot.java b/src/main/java/com/cleanroommc/modularui/widgets/FluidSlot.java index 5a9c6082..152f8c16 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/FluidSlot.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/FluidSlot.java @@ -12,6 +12,7 @@ import com.cleanroommc.modularui.screen.Tooltip; import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.theme.WidgetSlotTheme; +import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.utils.MouseData; @@ -109,7 +110,7 @@ public boolean isValidSyncHandler(SyncHandler syncHandler) { } @Override - public void draw(GuiContext context) { + public void draw(GuiContext context, WidgetTheme widgetTheme) { IFluidTank fluidTank = getFluidTank(); FluidStack content = this.syncHandler.getValue(); if (content != null) { diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ItemSlot.java b/src/main/java/com/cleanroommc/modularui/widgets/ItemSlot.java index e7bb5293..e50ca304 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ItemSlot.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ItemSlot.java @@ -13,6 +13,7 @@ import com.cleanroommc.modularui.screen.Tooltip; import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.theme.WidgetSlotTheme; +import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.utils.MouseData; @@ -75,7 +76,7 @@ public void onUpdate() { } @Override - public void draw(GuiContext context) { + public void draw(GuiContext context, WidgetTheme widgetTheme) { if (this.syncHandler == null) return; RenderHelper.enableGUIStandardItemLighting(); drawSlot(getSlot()); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java index fe0c2818..5b8d57d7 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ProgressWidget.java @@ -4,6 +4,7 @@ import com.cleanroommc.modularui.api.value.IDoubleValue; import com.cleanroommc.modularui.drawable.UITexture; import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.value.DoubleValue; import com.cleanroommc.modularui.value.sync.SyncHandler; import com.cleanroommc.modularui.widget.Widget; @@ -55,7 +56,7 @@ public float getCurrentProgress() { } @Override - public void draw(GuiContext context) { + public void draw(GuiContext context, WidgetTheme widgetTheme) { if (this.emptyTexture != null) { this.emptyTexture.draw(context, 0, 0, getArea().w(), getArea().h()); } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ScrollingTextWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/ScrollingTextWidget.java index 574b595b..983cbb9b 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ScrollingTextWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ScrollingTextWidget.java @@ -3,6 +3,7 @@ import com.cleanroommc.modularui.api.drawable.IKey; import com.cleanroommc.modularui.drawable.TextRenderer; import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.utils.Alignment; public class ScrollingTextWidget extends TextWidget { @@ -54,7 +55,7 @@ public void onUpdate() { } @Override - public void draw(GuiContext context) { + public void draw(GuiContext context, WidgetTheme widgetTheme) { checkString(); TextRenderer renderer = TextRenderer.SHARED; renderer.setColor(getColor()); diff --git a/src/main/java/com/cleanroommc/modularui/widgets/SortButtons.java b/src/main/java/com/cleanroommc/modularui/widgets/SortButtons.java new file mode 100644 index 00000000..383a8626 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/widgets/SortButtons.java @@ -0,0 +1,90 @@ +package com.cleanroommc.modularui.widgets; + +import com.cleanroommc.bogosorter.api.IBogoSortAPI; +import com.cleanroommc.modularui.ModularUI; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.widget.IWidget; +import com.cleanroommc.modularui.widget.Widget; +import com.cleanroommc.modularui.widgets.slot.SlotGroup; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.List; + +public class SortButtons extends Widget { + + private String slotGroupName; + private SlotGroup slotGroup; + + private boolean horizontal = true; + private final ButtonWidget sortButton = new ButtonWidget<>(); + private final ButtonWidget settingsButton = new ButtonWidget<>(); + private final List children = Arrays.asList(sortButton, settingsButton); + + @Override + public void onInit() { + super.onInit(); + this.slotGroup = getScreen().getContainer().validateSlotGroup(this.slotGroupName, this.slotGroup); + if (!this.slotGroup.isAllowSorting()) { + throw new IllegalStateException("Slot group can't be sorted!"); + } + this.sortButton.size(10).pos(0, 0) + .overlay(IKey.str("z")) + .onMousePressed(mouseButton -> { + IBogoSortAPI.getInstance().sortSlotGroup(this.slotGroup.getSlots().get(0)); + return true; + }); + this.settingsButton.size(10) + .overlay(IKey.str("...")) + .onMousePressed(mouseButton -> { + IBogoSortAPI.getInstance().openConfigGui(); + return true; + }); + if (this.horizontal) { + size(20, 10); + this.settingsButton.pos(10, 0); + } else { + size(10, 20); + this.settingsButton.pos(0, 10); + } + } + + @NotNull + @Override + public List getChildren() { + return this.children; + } + + @Override + public boolean isEnabled() { + return super.isEnabled() && ModularUI.isSortModLoaded(); + } + + public String getSlotGroupName() { + return slotGroupName; + } + + public SlotGroup getSlotGroup() { + return slotGroup; + } + + public SortButtons slotGroup(String slotGroupName) { + this.slotGroupName = slotGroupName; + return this; + } + + public SortButtons slotGroup(SlotGroup slotGroup) { + this.slotGroup = slotGroup; + return this; + } + + public SortButtons horizontal() { + this.horizontal = true; + return this; + } + + public SortButtons vertical() { + this.horizontal = false; + return this; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java index 2fa0d0d0..9e35dc20 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java @@ -26,7 +26,7 @@ public TextWidget(IKey key) { } @Override - public void draw(GuiContext context) { + public void draw(GuiContext context, WidgetTheme widgetTheme) { TextRenderer renderer = TextRenderer.SHARED; renderer.setColor(this.color); renderer.setAlignment(this.alignment, getArea().w() + 1, getArea().h());