From 5fcf2698503b1eecae6b8f5abf79afbb11bc6f6e Mon Sep 17 00:00:00 2001 From: shedaniel Date: Wed, 7 Jun 2023 02:17:16 +0800 Subject: Initial Work on Config UI Rework --- .../rei/api/client/gui/widgets/Widgets.java | 5 + .../me/shedaniel/rei/impl/ClientInternals.java | 2 + .../rei/impl/client/config/ConfigManagerImpl.java | 2 + .../rei/impl/client/config/ConfigObjectImpl.java | 160 ++++++------ .../impl/client/gui/config/REIConfigScreen.java | 138 +++++++++++ .../components/ConfigCategoriesListWidget.java | 45 ++++ .../components/ConfigCategoryEntryWidget.java | 64 +++++ .../impl/client/gui/config/options/AllOptions.java | 53 ++++ .../client/gui/config/options/CompositeOption.java | 59 +++++ .../gui/config/options/ConfigCategories.java | 63 +++++ .../client/gui/config/options/ConfigUtils.java | 42 ++++ .../client/gui/config/options/OptionCategory.java | 58 +++++ .../client/gui/config/options/OptionGroup.java | 43 ++++ .../gui/config/options/OptionValueEntry.java | 67 +++++ .../rei/impl/client/gui/widget/HoleWidget.java | 101 ++++++++ .../impl/client/gui/widget/InternalWidgets.java | 5 + .../rei/impl/client/gui/widget/ListWidget.java | 272 +++++++++++++++++++++ .../impl/client/gui/widget/ScissoredWidget.java | 67 +++++ .../client/gui/widget/ScrollableViewWidget.java | 108 ++++++++ .../assets/roughlyenoughitems/lang/en_us.json | 220 ++--------------- .../textures/gui/config/accessibility.png | Bin 0 -> 223 bytes .../textures/gui/config/appearance.png | Bin 0 -> 251 bytes .../textures/gui/config/cheats.png | Bin 0 -> 211 bytes .../textures/gui/config/debug.png | Bin 0 -> 217 bytes .../textures/gui/config/favorites.png | Bin 0 -> 218 bytes .../textures/gui/config/filtering.png | Bin 0 -> 186 bytes .../textures/gui/config/keybinds.png | Bin 0 -> 238 bytes .../textures/gui/config/layout.png | Bin 0 -> 240 bytes .../textures/gui/config/list.png | Bin 0 -> 159 bytes .../textures/gui/config/performance.png | Bin 0 -> 202 bytes .../textures/gui/config/search.png | Bin 0 -> 199 bytes 31 files changed, 1301 insertions(+), 273 deletions(-) create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/REIConfigScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoriesListWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoryEntryWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllOptions.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/CompositeOption.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/ConfigCategories.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/ConfigUtils.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionCategory.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionGroup.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionValueEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/HoleWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ListWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ScissoredWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ScrollableViewWidget.java create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/config/accessibility.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/config/appearance.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/config/cheats.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/config/debug.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/config/favorites.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/config/filtering.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/config/keybinds.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/config/layout.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/config/list.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/config/performance.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/config/search.png diff --git a/api/src/main/java/me/shedaniel/rei/api/client/gui/widgets/Widgets.java b/api/src/main/java/me/shedaniel/rei/api/client/gui/widgets/Widgets.java index 954a83b8a..44e18b32e 100644 --- a/api/src/main/java/me/shedaniel/rei/api/client/gui/widgets/Widgets.java +++ b/api/src/main/java/me/shedaniel/rei/api/client/gui/widgets/Widgets.java @@ -313,6 +313,11 @@ public final class Widgets { return ClientInternals.getWidgetsProvider().wrapOverflow(bounds, widget); } + @ApiStatus.Experimental + public static WidgetWithBounds scissored(Rectangle bounds, Widget widget) { + return ClientInternals.getWidgetsProvider().wrapScissored(bounds, widget); + } + @ApiStatus.Experimental public static WidgetWithBounds padded(int padding, WidgetWithBounds widget) { return padded(padding, padding, padding, padding, widget); diff --git a/api/src/main/java/me/shedaniel/rei/impl/ClientInternals.java b/api/src/main/java/me/shedaniel/rei/impl/ClientInternals.java index e7e165b97..7fc29b400 100644 --- a/api/src/main/java/me/shedaniel/rei/impl/ClientInternals.java +++ b/api/src/main/java/me/shedaniel/rei/impl/ClientInternals.java @@ -232,6 +232,8 @@ public final class ClientInternals { WidgetWithBounds wrapOverflow(Rectangle bounds, WidgetWithBounds widget); + WidgetWithBounds wrapScissored(Rectangle bounds, Widget widget); + WidgetWithBounds wrapPadded(int padLeft, int padRight, int padTop, int padBottom, WidgetWithBounds widget); } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigManagerImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigManagerImpl.java index 6e871212b..16dcfcd3d 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigManagerImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigManagerImpl.java @@ -72,6 +72,7 @@ import me.shedaniel.rei.impl.client.config.addon.ConfigAddonRegistryImpl; import me.shedaniel.rei.impl.client.config.collapsible.CollapsibleConfigManager; import me.shedaniel.rei.impl.client.config.entries.*; import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; +import me.shedaniel.rei.impl.client.gui.config.REIConfigScreen; import me.shedaniel.rei.impl.client.gui.credits.CreditsScreen; import me.shedaniel.rei.impl.client.gui.performance.PerformanceScreen; import me.shedaniel.rei.impl.client.gui.screen.ConfigReloadingScreen; @@ -357,6 +358,7 @@ public class ConfigManagerImpl implements ConfigManager { @SuppressWarnings("deprecation") @Override public Screen getConfigScreen(Screen parent) { + if (true) return new REIConfigScreen(parent); class EmptyEntry extends AbstractConfigListEntry { private final int height; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java index 896ca434e..d4be3dfe0 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java @@ -61,9 +61,9 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { @ConfigEntry.Category("basics") @ConfigEntry.Gui.TransitiveObject @DontApplyFieldName public Basics basics = new Basics(); @ConfigEntry.Category("appearance") @ConfigEntry.Gui.TransitiveObject @DontApplyFieldName - private Appearance appearance = new Appearance(); + public Appearance appearance = new Appearance(); @ConfigEntry.Category("functionality") @ConfigEntry.Gui.TransitiveObject @DontApplyFieldName - private Functionality functionality = new Functionality(); + public Functionality functionality = new Functionality(); @ConfigEntry.Category("advanced") @ConfigEntry.Gui.TransitiveObject @DontApplyFieldName public Advanced advanced = new Advanced(); @@ -617,144 +617,144 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { @ConfigEntry.Gui.Excluded public List hiddenFavorites = new ArrayList<>(); @ConfigEntry.Gui.Excluded public List displayHistory = new ArrayList<>(); @Comment("Declares whether cheating mode is on.") @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private CheatingMode cheating = CheatingMode.OFF; - private boolean favoritesEnabled = true; - private boolean reduceMotion = false; + public CheatingMode cheating = CheatingMode.OFF; + public boolean favoritesEnabled = true; + public boolean reduceMotion = false; @ConfigEntry.Gui.CollapsibleObject(startExpanded = true) - private KeyBindings keyBindings = new KeyBindings(); - @Comment("Declares whether REI is visible.") @ConfigEntry.Gui.Excluded private boolean overlayVisible = true; + public KeyBindings keyBindings = new KeyBindings(); + @Comment("Declares whether REI is visible.") @ConfigEntry.Gui.Excluded public boolean overlayVisible = true; @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private ItemCheatingStyle cheatingStyle = ItemCheatingStyle.GRAB; + public ItemCheatingStyle cheatingStyle = ItemCheatingStyle.GRAB; } public static class KeyBindings { - private ModifierKeyCode recipeKeybind = ModifierKeyCode.of(InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_R), Modifier.none()); - private ModifierKeyCode usageKeybind = ModifierKeyCode.of(InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_U), Modifier.none()); - private ModifierKeyCode hideKeybind = ModifierKeyCode.of(InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_O), Modifier.of(false, true, false)); - private ModifierKeyCode previousPageKeybind = ModifierKeyCode.unknown(); - private ModifierKeyCode nextPageKeybind = ModifierKeyCode.unknown(); - private ModifierKeyCode focusSearchFieldKeybind = ModifierKeyCode.unknown(); - private ModifierKeyCode copyRecipeIdentifierKeybind = ModifierKeyCode.of(InputConstants.Type.MOUSE.getOrCreate(InputConstants.MOUSE_BUTTON_MIDDLE), Modifier.none()); - private ModifierKeyCode favoriteKeybind = ModifierKeyCode.of(InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_A), Modifier.none()); - private ModifierKeyCode exportImageKeybind = ModifierKeyCode.of(InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_F8), Modifier.none()); - private ModifierKeyCode previousScreenKeybind = ModifierKeyCode.of(InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_BACKSPACE), Modifier.none()); + public ModifierKeyCode recipeKeybind = ModifierKeyCode.of(InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_R), Modifier.none()); + public ModifierKeyCode usageKeybind = ModifierKeyCode.of(InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_U), Modifier.none()); + public ModifierKeyCode hideKeybind = ModifierKeyCode.of(InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_O), Modifier.of(false, true, false)); + public ModifierKeyCode previousPageKeybind = ModifierKeyCode.unknown(); + public ModifierKeyCode nextPageKeybind = ModifierKeyCode.unknown(); + public ModifierKeyCode focusSearchFieldKeybind = ModifierKeyCode.unknown(); + public ModifierKeyCode copyRecipeIdentifierKeybind = ModifierKeyCode.of(InputConstants.Type.MOUSE.getOrCreate(InputConstants.MOUSE_BUTTON_MIDDLE), Modifier.none()); + public ModifierKeyCode favoriteKeybind = ModifierKeyCode.of(InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_A), Modifier.none()); + public ModifierKeyCode exportImageKeybind = ModifierKeyCode.of(InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_F8), Modifier.none()); + public ModifierKeyCode previousScreenKeybind = ModifierKeyCode.of(InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_BACKSPACE), Modifier.none()); } public static class Appearance { - @UseSpecialRecipeTypeScreen private DisplayScreenType recipeScreenType = DisplayScreenType.UNSET; + @UseSpecialRecipeTypeScreen public DisplayScreenType recipeScreenType = DisplayScreenType.UNSET; @Comment("Declares the appearance of REI windows.") @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private AppearanceTheme theme = AppearanceTheme.LIGHT; + public AppearanceTheme theme = AppearanceTheme.LIGHT; @ConfigEntry.Gui.CollapsibleObject(startExpanded = true) - private Layout layout = new Layout(); + public Layout layout = new Layout(); @Comment("Declares the appearance of recipe's border.") @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private RecipeBorderType recipeBorder = RecipeBorderType.DEFAULT; - @Comment("Declares whether entry panel is scrolled.") private boolean scrollingEntryListWidget = false; - @Comment("Declares whether entry panel should be invisible when not searching") private boolean hideEntryPanelIfIdle = false; + public RecipeBorderType recipeBorder = RecipeBorderType.DEFAULT; + @Comment("Declares whether entry panel is scrolled.") public boolean scrollingEntryListWidget = false; + @Comment("Declares whether entry panel should be invisible when not searching") public boolean hideEntryPanelIfIdle = false; public static class Layout { @Comment("Declares the position of the search field.") @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private SearchFieldLocation searchFieldLocation = SearchFieldLocation.CENTER; + public SearchFieldLocation searchFieldLocation = SearchFieldLocation.CENTER; @Comment("Declares the position of the config button.") @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private ConfigButtonPosition configButtonLocation = ConfigButtonPosition.LOWER; - @Comment("Declares whether the craftable filter button is enabled.") private boolean showCraftableOnlyButton = true; + public ConfigButtonPosition configButtonLocation = ConfigButtonPosition.LOWER; + @Comment("Declares whether the craftable filter button is enabled.") public boolean showCraftableOnlyButton = true; } - @UsePercentage(min = 0.1, max = 1.0, prefix = "Limit: ") private double horizontalEntriesBoundaries = 1.0; - @UsePercentage(min = 0.1, max = 1.0, prefix = "Limit: ") private double verticalEntriesBoundaries = 1.0; - private int horizontalEntriesBoundariesColumns = 50; - private int verticalEntriesBoundariesRows = 1000; - @UsePercentage(min = 0.1, max = 1.0, prefix = "Limit: ") private double favoritesHorizontalEntriesBoundaries = 1.0; - private int favoritesHorizontalEntriesBoundariesColumns = 50; - @UseSpecialSearchFilterSyntaxHighlightingScreen private SyntaxHighlightingMode syntaxHighlightingMode = SyntaxHighlightingMode.COLORFUL; - private boolean isFocusModeZoomed = false; + @UsePercentage(min = 0.1, max = 1.0, prefix = "Limit: ") public double horizontalEntriesBoundaries = 1.0; + @UsePercentage(min = 0.1, max = 1.0, prefix = "Limit: ") public double verticalEntriesBoundaries = 1.0; + public int horizontalEntriesBoundariesColumns = 50; + public int verticalEntriesBoundariesRows = 1000; + @UsePercentage(min = 0.1, max = 1.0, prefix = "Limit: ") public double favoritesHorizontalEntriesBoundaries = 1.0; + public int favoritesHorizontalEntriesBoundariesColumns = 50; + @UseSpecialSearchFilterSyntaxHighlightingScreen public SyntaxHighlightingMode syntaxHighlightingMode = SyntaxHighlightingMode.COLORFUL; + public boolean isFocusModeZoomed = false; } public static class Functionality { - @ConfigEntry.Gui.Excluded @Nullable private ResourceLocation inputMethod = null; - @Comment("Declares whether REI should remove the recipe book.") private boolean disableRecipeBook = false; - @Comment("Declares whether mob effects should be on the left side instead of the right side.") private boolean leftSideMobEffects = false; - @Comment("Declares whether subsets is enabled.") private boolean isSubsetsEnabled = false; - private boolean allowInventoryHighlighting = true; + @ConfigEntry.Gui.Excluded @Nullable public ResourceLocation inputMethod = null; + @Comment("Declares whether REI should remove the recipe book.") public boolean disableRecipeBook = false; + @Comment("Declares whether mob effects should be on the left side instead of the right side.") public boolean leftSideMobEffects = false; + @Comment("Declares whether subsets is enabled.") public boolean isSubsetsEnabled = false; + public boolean allowInventoryHighlighting = true; @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private ItemCheatingMode itemCheatingMode = ItemCheatingMode.REI_LIKE; + public ItemCheatingMode itemCheatingMode = ItemCheatingMode.REI_LIKE; } public static class Advanced { @ConfigEntry.Gui.CollapsibleObject - private Tooltips tooltips = new Tooltips(); + public Tooltips tooltips = new Tooltips(); @ConfigEntry.Gui.CollapsibleObject - private Layout layout = new Layout(); + public Layout layout = new Layout(); @ConfigEntry.Gui.CollapsibleObject - private Accessibility accessibility = new Accessibility(); + public Accessibility accessibility = new Accessibility(); @ConfigEntry.Gui.CollapsibleObject - private Search search = new Search(); + public Search search = new Search(); @ConfigEntry.Gui.CollapsibleObject - private Commands commands = new Commands(); + public Commands commands = new Commands(); @ConfigEntry.Gui.CollapsibleObject - private Miscellaneous miscellaneous = new Miscellaneous(); + public Miscellaneous miscellaneous = new Miscellaneous(); @ConfigEntry.Gui.CollapsibleObject(startExpanded = true) public Filtering filtering = new Filtering(); public static class Tooltips { - @Comment("Declares whether REI should append mod names to entries.") private boolean appendModNames = true; - @Comment("Declares whether favorites tooltip should be displayed.") private boolean displayFavoritesTooltip = false; - @ConfigEntry.Gui.Excluded private boolean displayIMEHints = true; + @Comment("Declares whether REI should append mod names to entries.") public boolean appendModNames = true; + @Comment("Declares whether favorites tooltip should be displayed.") public boolean displayFavoritesTooltip = false; + @ConfigEntry.Gui.Excluded public boolean displayIMEHints = true; } public static class Layout { @Comment("The ordering of the items on the entry panel.") @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private EntryPanelOrderingConfig entryPanelOrdering = EntryPanelOrderingConfig.REGISTRY_ASCENDING; + public EntryPanelOrderingConfig entryPanelOrdering = EntryPanelOrderingConfig.REGISTRY_ASCENDING; @Comment("Declares the maximum amount of recipes displayed in a page if possible.") @ConfigEntry.BoundedDiscrete(min = 2, max = 99) - private int maxRecipesPerPage = 8; + public int maxRecipesPerPage = 8; @Comment("Declares the maximum amount of recipes displayed in a page if possible.") @ConfigEntry.BoundedDiscrete(min = 100, max = 1000) - private int maxRecipesPageHeight = 300; - @Comment("Declares whether entry rendering time should be debugged.") private boolean debugRenderTimeRequired = false; - @Comment("Merges displays with equal contents under 1 display.") private boolean mergeDisplayUnderOne = true; + public int maxRecipesPageHeight = 300; + @Comment("Declares whether entry rendering time should be debugged.") public boolean debugRenderTimeRequired = false; + @Comment("Merges displays with equal contents under 1 display.") public boolean mergeDisplayUnderOne = true; @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private FavoriteAddWidgetMode favoriteAddWidgetMode = FavoriteAddWidgetMode.ALWAYS_VISIBLE; + public FavoriteAddWidgetMode favoriteAddWidgetMode = FavoriteAddWidgetMode.ALWAYS_VISIBLE; } public static class Accessibility { - @UsePercentage(min = 0.25, max = 4.0) private double entrySize = 1.0; + @UsePercentage(min = 0.25, max = 4.0) public double entrySize = 1.0; @Comment("Declares the position of the entry panel.") @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private DisplayPanelLocation displayPanelLocation = DisplayPanelLocation.RIGHT; - @Comment("Declares how the scrollbar in composite screen should act.") private boolean compositeScrollBarPermanent = false; - private boolean toastDisplayedOnCopyIdentifier = true; - @Comment("Declares whether REI should use compact tabs for categories.") private boolean useCompactTabs = true; - @Comment("Declares whether REI should use compact tab buttons for categories.") private boolean useCompactTabButtons = false; + public DisplayPanelLocation displayPanelLocation = DisplayPanelLocation.RIGHT; + @Comment("Declares how the scrollbar in composite screen should act.") public boolean compositeScrollBarPermanent = false; + public boolean toastDisplayedOnCopyIdentifier = true; + @Comment("Declares whether REI should use compact tabs for categories.") public boolean useCompactTabs = true; + @Comment("Declares whether REI should use compact tab buttons for categories.") public boolean useCompactTabButtons = false; } public static class Search { - @Comment("Declares whether search time should be debugged.") private boolean debugSearchTimeRequired = false; - @Comment("Declares whether REI should search async.") private boolean asyncSearch = true; + @Comment("Declares whether search time should be debugged.") public boolean debugSearchTimeRequired = false; + @Comment("Declares whether REI should search async.") public boolean asyncSearch = true; @Comment("Declares how many entries should be grouped one async search.") @ConfigEntry.BoundedDiscrete(min = 25, max = 400) - private int asyncSearchPartitionSize = 100; - private boolean patchAsyncThreadCrash = true; + public int asyncSearchPartitionSize = 100; + public boolean patchAsyncThreadCrash = true; @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private SearchMode tooltipSearch = SearchMode.ALWAYS; + public SearchMode tooltipSearch = SearchMode.ALWAYS; @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private SearchMode tagSearch = SearchMode.PREFIX; + public SearchMode tagSearch = SearchMode.PREFIX; @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private SearchMode identifierSearch = SearchMode.ALWAYS; + public SearchMode identifierSearch = SearchMode.ALWAYS; @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) - private SearchMode modSearch = SearchMode.PREFIX; + public SearchMode modSearch = SearchMode.PREFIX; } public static class Commands { - @Comment("Declares the command used to change gamemode.") private String gamemodeCommand = "/gamemode {gamemode}"; - @Comment("Declares the command used in servers to cheat items.") private String giveCommand = "/give {player_name} {item_identifier}{nbt} {count}"; - @Comment("Declares the command used to change weather.") private String weatherCommand = "/weather {weather}"; - @Comment("Declares the command used to change time.") private String timeCommand = "/time set {time}"; + @Comment("Declares the command used to change gamemode.") public String gamemodeCommand = "/gamemode {gamemode}"; + @Comment("Declares the command used in servers to cheat items.") public String giveCommand = "/give {player_name} {item_identifier}{nbt} {count}"; + @Comment("Declares the command used to change weather.") public String weatherCommand = "/weather {weather}"; + @Comment("Declares the command used to change time.") public String timeCommand = "/time set {time}"; } public static class Miscellaneous { - @Comment("Declares whether arrows in containers should be clickable.") private boolean clickableRecipeArrows = true; - private boolean registerRecipesInAnotherThread = true; - private boolean newFastEntryRendering = true; + @Comment("Declares whether arrows in containers should be clickable.") public boolean clickableRecipeArrows = true; + public boolean registerRecipesInAnotherThread = true; + public boolean newFastEntryRendering = true; @ConfigEntry.Gui.PrefixText - private boolean cachingFastEntryRendering = false; - private boolean cachingDisplayLookup = true; + public boolean cachingFastEntryRendering = false; + public boolean cachingDisplayLookup = true; @ConfigEntry.Gui.Excluded public CategorySettings categorySettings = new CategorySettings(); public static class CategorySettings { @@ -765,7 +765,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { } public static class Filtering { - @UseFilteringScreen private List> filteredStacks = new ArrayList<>(); + @UseFilteringScreen public List> filteredStacks = new ArrayList<>(); public boolean shouldFilterDisplays = true; @ConfigEntry.Gui.Excluded public List> filteringRules = new ArrayList<>(); } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/REIConfigScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/REIConfigScreen.java new file mode 100644 index 000000000..35a1e7d20 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/REIConfigScreen.java @@ -0,0 +1,138 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.config; + +import com.google.common.base.Preconditions; +import com.mojang.blaze3d.vertex.PoseStack; +import dev.architectury.utils.value.IntValue; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.widgets.Widgets; +import me.shedaniel.rei.impl.client.gui.config.components.ConfigCategoriesListWidget; +import me.shedaniel.rei.impl.client.gui.config.options.ConfigCategories; +import me.shedaniel.rei.impl.client.gui.config.options.OptionCategory; +import me.shedaniel.rei.impl.client.gui.credits.CreditsScreen; +import me.shedaniel.rei.impl.client.gui.widget.HoleWidget; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Widget; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.TranslatableComponent; + +import java.util.ArrayList; +import java.util.List; + +public class REIConfigScreen extends Screen { + private final Screen parent; + private final List categories; + private final List widgets = new ArrayList<>(); + private OptionCategory activeCategory; + + public REIConfigScreen(Screen parent) { + this(parent, ConfigCategories.CATEGORIES); + } + + public REIConfigScreen(Screen parent, List categories) { + super(new TranslatableComponent("config.roughlyenoughitems.title")); + this.parent = parent; + this.categories = categories; + Preconditions.checkArgument(!categories.isEmpty(), "Categories cannot be empty!"); + this.activeCategory = categories.get(0); + } + + @Override + public void init() { + super.init(); + this.widgets.clear(); + this.widgets.add(Widgets.createButton(new Rectangle(4, 4, 100, 20), new TranslatableComponent("text.rei.credits")) + .onClick(button -> { + Minecraft.getInstance().setScreen(new CreditsScreen(this)); + })); + this.widgets.add(Widgets.createLabel(new Point(width / 2, 12), this.title)); + int sideWidth = (int) (width / 3.8); + this.widgets.add(ConfigCategoriesListWidget.create(new Rectangle(8, 32, sideWidth, height - 32 - 32), categories, new IntValue() { + @Override + public void accept(int i) { + REIConfigScreen.this.activeCategory = categories.get(i); + } + + @Override + public int getAsInt() { + return categories.indexOf(activeCategory); + } + })); + this.widgets.add(HoleWidget.create(new Rectangle(12 + sideWidth, 32, width - 20 - sideWidth, height - 32 - 32), () -> 0, 32)); + } + + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + this.renderDirtBackground(0); + super.render(poses, mouseX, mouseY, delta); + for (Widget widget : widgets) { + widget.render(poses, mouseX, mouseY, delta); + } + } + + @Override + public void onClose() { + this.minecraft.setScreen(this.parent); + } + + @Override + public List children() { + return (List) (List) this.widgets; + } + + @Override + public boolean charTyped(char character, int modifiers) { + for (GuiEventListener listener : children()) + if (listener.charTyped(character, modifiers)) + return true; + return super.charTyped(character, modifiers); + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + for (GuiEventListener entry : children()) + if (entry.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)) + return true; + return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } + + @Override + public boolean mouseReleased(double mouseX, double mouseY, int button) { + for (GuiEventListener entry : children()) + if (entry.mouseReleased(mouseX, mouseY, button)) + return true; + return super.mouseReleased(mouseX, mouseY, button); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + for (GuiEventListener listener : children()) + if (listener.mouseScrolled(mouseX, mouseY, amount)) + return true; + return super.mouseScrolled(mouseX, mouseY, amount); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoriesListWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoriesListWidget.java new file mode 100644 index 000000000..46e48b290 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoriesListWidget.java @@ -0,0 +1,45 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.config.components; + +import dev.architectury.utils.value.IntValue; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.impl.client.gui.config.options.OptionCategory; +import me.shedaniel.rei.impl.client.gui.widget.ListWidget; + +import java.util.List; + +public class ConfigCategoriesListWidget { + public static Widget create(Rectangle bounds, List categories, IntValue selected) { + return ListWidget.builderOf(bounds, categories, + (index, entry) -> ConfigCategoryEntryWidget.create(entry)) + .paddingHorizontal(3) + .paddingVertical(5) + .gap(3) + .isSelectable((index, entry) -> true) + .selected(selected) + .build(); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoryEntryWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoryEntryWidget.java new file mode 100644 index 000000000..e986d3395 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigCategoryEntryWidget.java @@ -0,0 +1,64 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.config.components; + +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.widgets.Label; +import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds; +import me.shedaniel.rei.api.client.gui.widgets.Widgets; +import me.shedaniel.rei.impl.client.gui.config.options.OptionCategory; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; + +public class ConfigCategoryEntryWidget { + public static WidgetWithBounds create(OptionCategory category) { + Label label = Widgets.createLabel(new Point(21, 7), category.getName().copy().withStyle(style -> style.withColor(0xFFD0D0D0))) + .leftAligned(); + Font font = Minecraft.getInstance().font; + Rectangle bounds = new Rectangle(0, 0, label.getBounds().getMaxX(), 7 * 3); + return Widgets.withBounds(Widgets.concat( + label, + Widgets.createTexturedWidget(category.getIcon(), new Rectangle(3, 3, 16, 16), 0, 0, 1, 1, 1, 1) + ), bounds); + } +} + + + + + + + + + + + + + + + + + + diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllOptions.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllOptions.java new file mode 100644 index 000000000..f992d5a68 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllOptions.java @@ -0,0 +1,53 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.config.options; + +import me.shedaniel.rei.api.client.gui.config.*; +import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +import static me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils.translatable; + +interface AllOptions { + static CompositeOption make(String id, Function bind, + BiConsumer save) { + return new CompositeOption<>(translatable("config.rei.options." + id), + translatable("config.rei.options." + id + ".desc"), bind); + } + + CompositeOption CHEATING_MODE = make("cheating_mode", i -> i.basics.cheating, (i, v) -> i.basics.cheating = v); + CompositeOption FAVORITES = make("favorites", i -> i.basics.favoritesEnabled, (i, v) -> i.basics.favoritesEnabled = v) + .enabledDisabled(); + CompositeOption REDUCED_MOTION = make("reduced_motion", i -> i.basics.reduceMotion, (i, v) -> i.basics.reduceMotion = v) + .trueFalse(); + CompositeOption CHEATING_STYLE = make("cheating_style", i -> i.basics.cheatingStyle, (i, v) -> i.basics.cheatingStyle = v); + CompositeOption DISPLAY_SCREEN_TYPE = make("display_screen_type", i -> i.appearance.recipeScreenType, (i, v) -> i.appearance.recipeScreenType = v); + CompositeOption THEME = make("theme", i -> i.appearance.theme, (i, v) -> i.appearance.theme = v); + CompositeOption SEARCH_FIELD_LOCATION = make("search_field_location", i -> i.appearance.layout.searchFieldLocation, (i, v) -> i.appearance.layout.searchFieldLocation = v); + CompositeOption CONFIG_BUTTON_LOCATION = make("config_button_location", i -> i.appearance.layout.configButtonLocation, (i, v) -> i.appearance.layout.configButtonLocation = v); + CompositeOption CRAFTABLE_FILTER = make("craftable_filter", i -> i.appearance.layout.showCraftableOnlyButton, (i, v) -> i.appearance.layout.showCraftableOnlyButton = v) + .enabledDisabled(); +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/CompositeOption.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/CompositeOption.java new file mode 100644 index 000000000..5b77fad0c --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/CompositeOption.java @@ -0,0 +1,59 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.config.options; + +import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; +import net.minecraft.network.chat.Component; + +import java.util.function.Function; + +public class CompositeOption { + private final Component name; + private final Component description; + private final Function bind; + private OptionValueEntry entry; + + public CompositeOption(Component name, Component description, Function bind) { + this.name = name; + this.description = description; + this.bind = bind; + } + + public CompositeOption entry(OptionValueEntry entry) { + this.entry = entry; + return this; + } + + public CompositeOption ofBoolean(Component falseText, Component trueText) { + return ((CompositeOption) this).entry(OptionValueEntry.ofBoolean(falseText, trueText)); + } + + public CompositeOption trueFalse() { + return ((CompositeOption) this).entry(OptionValueEntry.trueFalse()); + } + + public CompositeOption enabledDisabled() { + return ((CompositeOption) this).entry(OptionValueEntry.enabledDisabled()); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/ConfigCategories.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/ConfigCategories.java new file mode 100644 index 000000000..1fc11c535 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/ConfigCategories.java @@ -0,0 +1,63 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.config.options; + +import com.google.common.collect.ImmutableList; +import net.minecraft.resources.ResourceLocation; + +import java.util.List; + +import static me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils.translatable; + +public interface ConfigCategories { + static OptionCategory make(String key) { + return OptionCategory.of(new ResourceLocation("roughlyenoughitems:textures/gui/config/" + key + ".png"), + translatable("config.rei.categories." + key)); + } + + OptionCategory APPEARANCE = make("appearance"); + OptionCategory KEYBINDS = make("keybinds"); + OptionCategory CHEATS = make("cheats"); + OptionCategory LAYOUT = make("layout"); + OptionCategory ACCESSIBILITY = make("accessibility"); + OptionCategory FAVORITES = make("favorites"); + OptionCategory PERFORMANCE = make("performance"); + OptionCategory SEARCH = make("search"); + OptionCategory FILTERING = make("filtering"); + OptionCategory LIST = make("list"); + OptionCategory DEBUG = make("debug"); + List CATEGORIES = ImmutableList.of( + APPEARANCE, + KEYBINDS, + CHEATS, + LAYOUT, + ACCESSIBILITY, + FAVORITES, + PERFORMANCE, + SEARCH, + FILTERING, + LIST, + DEBUG + ); +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/ConfigUtils.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/ConfigUtils.java new file mode 100644 index 000000000..3bf883e05 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/ConfigUtils.java @@ -0,0 +1,42 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.config.options; + +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; + +interface ConfigUtils { + static MutableComponent literal(String text) { + return new TextComponent(text); + } + + static MutableComponent translatable(String key) { + return new TranslatableComponent(key); + } + + static MutableComponent translatable(String key, Object... args) { + return new TranslatableComponent(key, args); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionCategory.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionCategory.java new file mode 100644 index 000000000..ad8fdaa28 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionCategory.java @@ -0,0 +1,58 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.config.options; + +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; + +import java.util.ArrayList; +import java.util.List; + +public class OptionCategory { + private final ResourceLocation icon; + private final Component name; + private final List groups = new ArrayList<>(); + + private OptionCategory(ResourceLocation icon, Component name) { + this.icon = icon; + this.name = name; + } + + public static OptionCategory of(ResourceLocation icon, Component name) { + return new OptionCategory(icon, name); + } + + public OptionCategory add(OptionGroup group) { + this.groups.add(group); + return this; + } + + public ResourceLocation getIcon() { + return icon; + } + + public Component getName() { + return name; + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionGroup.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionGroup.java new file mode 100644 index 000000000..75eb304fa --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionGroup.java @@ -0,0 +1,43 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.config.options; + +import net.minecraft.network.chat.Component; + +import java.util.ArrayList; +import java.util.List; + +public class OptionGroup { + private final Component groupName; + private final List options = new ArrayList<>(); + + public OptionGroup(Component groupName) { + this.groupName = groupName; + } + + public OptionGroup add(CompositeOption option) { + this.options.add(option); + return this; + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionValueEntry.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionValueEntry.java new file mode 100644 index 000000000..8edf740bc --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/OptionValueEntry.java @@ -0,0 +1,67 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.config.options; + +import net.minecraft.network.chat.Component; + +import java.util.List; + +import static me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils.translatable; + +public interface OptionValueEntry { + static OptionValueEntry noOp() { + return new OptionValueEntry() { + }; + } + + static OptionValueEntry ofBoolean(Component falseText, Component trueText) { + return new Selection() { + @Override + public List getOptions() { + return List.of(falseText, trueText); + } + + @Override + public Component getOption(Boolean value) { + return value ? trueText : falseText; + } + }; + } + + static OptionValueEntry trueFalse() { + return ofBoolean(translatable("config.rei.value.trueFalse.false"), + translatable("config.rei.value.trueFalse.true")); + } + + static OptionValueEntry enabledDisabled() { + return ofBoolean(translatable("config.rei.value.enabledDisabled.false"), + translatable("config.rei.value.enabledDisabled.true")); + } + + interface Selection extends OptionValueEntry { + List getOptions(); + + Component getOption(T value); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/HoleWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/HoleWidget.java new file mode 100644 index 000000000..8e3440e39 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/HoleWidget.java @@ -0,0 +1,101 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.widget; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.math.Matrix4f; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.gui.widgets.Widgets; +import me.shedaniel.rei.impl.client.gui.widget.DynamicErrorFreeEntryListWidget; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.resources.ResourceLocation; + +import java.util.function.IntSupplier; + +public class HoleWidget { + // 32 for list background, 64 for header and footer + public static Widget create(Rectangle bounds, IntSupplier yOffset, int colorIntensity) { + return Widgets.withBounds( + Widgets.concat( + createBackground(bounds, yOffset, colorIntensity), + createInnerShadow(bounds) + ), + bounds + ); + } + + public static Widget create(Rectangle bounds, ResourceLocation backgroundLocation, IntSupplier yOffset, int colorIntensity) { + return Widgets.withBounds( + Widgets.concat( + createBackground(bounds, backgroundLocation, yOffset, colorIntensity), + createInnerShadow(bounds) + ), + bounds + ); + } + + public static Widget createBackground(Rectangle bounds, IntSupplier yOffset, int colorIntensity) { + return createBackground(bounds, Screen.BACKGROUND_LOCATION, yOffset, colorIntensity); + } + + public static Widget createBackground(Rectangle bounds, ResourceLocation backgroundLocation, IntSupplier yOffset, int colorIntensity) { + return Widgets.withBounds(Widgets.createDrawableWidget((helper, matrices, mouseX, mouseY, delta) -> { + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder buffer = tesselator.getBuilder(); + DynamicErrorFreeEntryListWidget.renderBackBackground(matrices, buffer, tesselator, backgroundLocation, bounds.x, bounds.y, bounds.getMaxX(), bounds.getMaxY(), yOffset.getAsInt(), colorIntensity); + }), bounds); + } + + public static Widget createInnerShadow(Rectangle bounds) { + return Widgets.withBounds(Widgets.createDrawableWidget((helper, matrices, mouseX, mouseY, delta) -> { + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder buffer = tesselator.getBuilder(); + RenderSystem.disableDepthTest(); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.enableBlend(); + RenderSystem.blendFuncSeparate(770, 771, 0, 1); + RenderSystem.disableTexture(); + RenderSystem.setShader(GameRenderer::getPositionTexColorShader); + Matrix4f matrix = matrices.last().pose(); + buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); + buffer.vertex(matrix, bounds.x, bounds.y + 4, 0.0F).uv(0, 1).color(0, 0, 0, 0).endVertex(); + buffer.vertex(matrix, bounds.getMaxX(), bounds.y + 4, 0.0F).uv(1, 1).color(0, 0, 0, 0).endVertex(); + buffer.vertex(matrix, bounds.getMaxX(), bounds.y, 0.0F).uv(1, 0).color(0, 0, 0, 255).endVertex(); + buffer.vertex(matrix, bounds.x, bounds.y, 0.0F).uv(0, 0).color(0, 0, 0, 255).endVertex(); + buffer.vertex(matrix, bounds.x, bounds.getMaxY(), 0.0F).uv(0, 1).color(0, 0, 0, 255).endVertex(); + buffer.vertex(matrix, bounds.getMaxX(), bounds.getMaxY(), 0.0F).uv(1, 1).color(0, 0, 0, 255).endVertex(); + buffer.vertex(matrix, bounds.getMaxX(), bounds.getMaxY() - 4, 0.0F).uv(1, 0).color(0, 0, 0, 0).endVertex(); + buffer.vertex(matrix, bounds.x, bounds.getMaxY() - 4, 0.0F).uv(0, 0).color(0, 0, 0, 0).endVert