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 index 3705e4aeb..f8262de4c 100644 --- 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 @@ -28,26 +28,37 @@ import dev.architectury.utils.value.IntValue; import me.shedaniel.math.Point; 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.config.ConfigManagerImpl; +import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; import me.shedaniel.rei.impl.client.gui.config.components.ConfigCategoriesListWidget; +import me.shedaniel.rei.impl.client.gui.config.components.ConfigEntriesListWidget; import me.shedaniel.rei.impl.client.gui.config.options.AllREIConfigCategories; +import me.shedaniel.rei.impl.client.gui.config.options.CompositeOption; import me.shedaniel.rei.impl.client.gui.config.options.OptionCategory; +import me.shedaniel.rei.impl.client.gui.config.options.OptionGroup; import me.shedaniel.rei.impl.client.gui.credits.CreditsScreen; -import me.shedaniel.rei.impl.client.gui.widget.HoleWidget; +import me.shedaniel.rei.impl.client.gui.modules.Menu; 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 org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class REIConfigScreen extends Screen { private final Screen parent; private final List categories; private final List widgets = new ArrayList<>(); + private final Map, ?> options = new HashMap<>(); private OptionCategory activeCategory; + @Nullable + private Menu menu; public REIConfigScreen(Screen parent) { this(parent, AllREIConfigCategories.CATEGORIES); @@ -59,6 +70,15 @@ public REIConfigScreen(Screen parent, List categories) { this.categories = categories; Preconditions.checkArgument(!categories.isEmpty(), "Categories cannot be empty!"); this.activeCategory = categories.get(0); + + ConfigObjectImpl config = ConfigManagerImpl.getInstance().getConfig(); + for (OptionCategory category : categories) { + for (OptionGroup group : category.getGroups()) { + for (CompositeOption option : group.getOptions()) { + ((Map, Object>) this.options).put(option, option.getBind().apply(config)); + } + } + } } @Override @@ -71,10 +91,12 @@ public void init() { })); this.widgets.add(Widgets.createLabel(new Point(width / 2, 12), this.title)); int sideWidth = (int) (width / 3.8); + Widget[] list = {ConfigEntriesListWidget.create(new Rectangle(12 + sideWidth, 32, width - 20 - sideWidth, height - 32 - 32), activeCategory.getGroups())}; 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); + list[0] = ConfigEntriesListWidget.create(new Rectangle(12 + sideWidth, 32, width - 20 - sideWidth, height - 32 - 32), activeCategory.getGroups()); } @Override @@ -82,7 +104,11 @@ 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)); + this.widgets.add(Widgets.delegate(() -> list[0])); + } + + public Map, ?> getOptions() { + return options; } @Override @@ -106,6 +132,8 @@ public List children() { @Override public boolean charTyped(char character, int modifiers) { + if (menu != null && menu.charTyped(character, modifiers)) + return true; for (GuiEventListener listener : children()) if (listener.charTyped(character, modifiers)) return true; @@ -114,14 +142,25 @@ public boolean charTyped(char character, int modifiers) { @Override public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + if (menu != null && menu.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)) + return true; 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 mouseClicked(double mouseX, double mouseY, int button) { + if (menu != null && menu.mouseClicked(mouseX, mouseY, button)) + return true; + return super.mouseClicked(mouseX, mouseY, button); + } + @Override public boolean mouseReleased(double mouseX, double mouseY, int button) { + if (menu != null && menu.mouseReleased(mouseX, mouseY, button)) + return true; for (GuiEventListener entry : children()) if (entry.mouseReleased(mouseX, mouseY, button)) return true; @@ -130,9 +169,21 @@ public boolean mouseReleased(double mouseX, double mouseY, int button) { @Override public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + if (menu != null && menu.mouseScrolled(mouseX, mouseY, amount)) + return true; for (GuiEventListener listener : children()) if (listener.mouseScrolled(mouseX, mouseY, amount)) return true; return super.mouseScrolled(mouseX, mouseY, amount); } + + public void openMenu(Menu menu) { + this.menu = menu; + this.widgets.add(menu); + } + + public void closeMenu() { + this.widgets.remove(menu); + this.menu = null; + } } 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 index e56e04f5f..cfacac0ac 100644 --- 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 @@ -24,7 +24,6 @@ package me.shedaniel.rei.impl.client.gui.config.components; import dev.architectury.utils.value.IntValue; -import me.shedaniel.clothconfig2.api.scroll.ScrollingContainer; import me.shedaniel.math.Rectangle; import me.shedaniel.rei.api.client.gui.widgets.Widget; import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds; 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 index 177c0104d..190d90cf2 100644 --- 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 @@ -34,7 +34,7 @@ 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))) + Label label = Widgets.createLabel(new Point(21, 7), category.getName().copy().withStyle(style -> style.withColor(0xFFC0C0C0))) .leftAligned(); Font font = Minecraft.getInstance().font; Rectangle bounds = new Rectangle(0, 0, label.getBounds().getMaxX(), 7 * 3); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigEntriesListWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigEntriesListWidget.java new file mode 100644 index 000000000..6f580d3a3 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigEntriesListWidget.java @@ -0,0 +1,44 @@ +/* + * 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.Rectangle; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds; +import me.shedaniel.rei.impl.client.gui.config.options.OptionGroup; +import me.shedaniel.rei.impl.client.gui.widget.ListWidget; +import me.shedaniel.rei.impl.client.gui.widget.ScrollableViewWidget; +import me.shedaniel.rei.impl.common.util.RectangleUtils; + +import java.util.List; + +public class ConfigEntriesListWidget { + public static Widget create(Rectangle bounds, List groups) { + WidgetWithBounds list = ListWidget.builderOf(RectangleUtils.inset(bounds, 6, 6), groups, + (index, entry) -> ConfigGroupWidget.create(entry, bounds.width - 12 - 6)) + .gap(7) + .build(); + return ScrollableViewWidget.create(bounds, list.withPadding(0, 5), true); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigGroupWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigGroupWidget.java new file mode 100644 index 000000000..76d41e4f1 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigGroupWidget.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.components; + +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +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.CompositeOption; +import me.shedaniel.rei.impl.client.gui.config.options.OptionGroup; +import net.minecraft.client.gui.GuiComponent; + +import java.util.ArrayList; +import java.util.List; + +public class ConfigGroupWidget { + public static WidgetWithBounds create(OptionGroup entry, int width) { + List widgets = new ArrayList<>(); + int height = 0; + WidgetWithBounds groupTitle = Widgets.createLabel(new Point(0, 3), entry.getGroupName().copy().withStyle(style -> style.withColor(0xFFC0C0C0).withUnderlined(true))) + .leftAligned(); + groupTitle = groupTitle.withPadding(0, 0, 0, 6); + widgets.add(groupTitle); + height = Math.max(height, groupTitle.getBounds().getMaxY()); + + for (CompositeOption option : entry.getOptions()) { + WidgetWithBounds widget = ConfigOptionWidget.create(option, width); + widgets.add(Widgets.withTranslate(widget, 0, height, 0)); + height = Math.max(height, height + widget.getBounds().getMaxY()); + + if (entry.getOptions().get(entry.getOptions().size() - 1) != option) { + int y = height; + widgets.add(Widgets.createDrawableWidget((helper, matrices, mouseX, mouseY, delta) -> { + for (int x = 0; x <= width; x += 4) { + GuiComponent.fill(matrices, x, y + 1, x + 2, y + 2, 0xFF757575); + } + })); + height += 7; + } + } + + Rectangle bounds = new Rectangle(0, 0, width, height); + return Widgets.concatWithBounds(bounds, widgets); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionValueWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionValueWidget.java new file mode 100644 index 000000000..bfa7d66b7 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionValueWidget.java @@ -0,0 +1,94 @@ +/* + * 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 com.mojang.math.Matrix4f; +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.api.client.util.MatrixUtils; +import me.shedaniel.rei.api.common.util.CollectionUtils; +import me.shedaniel.rei.impl.client.gui.config.REIConfigScreen; +import me.shedaniel.rei.impl.client.gui.config.options.CompositeOption; +import me.shedaniel.rei.impl.client.gui.config.options.OptionValueEntry; +import me.shedaniel.rei.impl.client.gui.modules.Menu; +import me.shedaniel.rei.impl.client.gui.modules.entries.ToggleMenuEntry; +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; + +import java.util.Map; + +import static me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils.literal; + +public class ConfigOptionValueWidget { + public static WidgetWithBounds create(CompositeOption option) { + Map, ?> options = ((REIConfigScreen) Minecraft.getInstance().screen).getOptions(); + OptionValueEntry entry = option.getEntry(); + T value = (T) options.get(option); + Component text; + + if (entry instanceof OptionValueEntry.Selection selection) { + text = selection.getOption(value); + } else { + text = literal(value.toString()); + } + + Label label = Widgets.createLabel(new Point(), text).rightAligned() + .color(0xFFE0E0E0) + .hoveredColor(0xFFE0E0E0); + Matrix4f[] matrix = {new Matrix4f()}; + + if (entry instanceof OptionValueEntry.Selection selection) { + int noOfOptions = selection.getOptions().size(); + if (noOfOptions == 2) { + label.clickable().onClick($ -> { + ((Map, Object>) options).put(option, selection.getOptions().get((selection.getOptions().indexOf((T) options.get(option)) + 1) % 2)); + label.setMessage(selection.getOption((T) options.get(option))); + }); + } else if (noOfOptions >= 2) { + label.clickable().onClick($ -> { + Menu menu = new Menu(MatrixUtils.transform(matrix[0], label.getBounds()), CollectionUtils.map(selection.getOptions(), + opt -> ToggleMenuEntry.of(selection.getOption(opt), () -> false, o -> { + ((REIConfigScreen) Minecraft.getInstance().screen).closeMenu(); + ((Map, Object>) options).put(option, opt); + label.setMessage(selection.getOption(opt)); + }) + .withActive(() -> true)), false); + ((REIConfigScreen) Minecraft.getInstance().screen).closeMenu(); + ((REIConfigScreen) Minecraft.getInstance().screen).openMenu(menu); + }); + } + } + + return Widgets.concatWithBounds(() -> new Rectangle(-label.getBounds().width, 0, label.getBounds().width + 8, 14), + label, + Widgets.createDrawableWidget((helper, matrices, mouseX, mouseY, delta) -> matrix[0] = matrices.last().pose()), + Widgets.withTranslate(Widgets.createTexturedWidget(new ResourceLocation("roughlyenoughitems:textures/gui/config/selector.png"), + new Rectangle(1, 1, 4, 6), 0, 0, 1, 1, 1, 1), 0, 0.5, 0) + ); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionWidget.java new file mode 100644 index 000000000..9707b3ab7 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionWidget.java @@ -0,0 +1,105 @@ +/* + * 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 com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Matrix4f; +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.Widget; +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.CompositeOption; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.util.FormattedCharSequence; + +import java.util.ArrayList; +import java.util.List; + +import static me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils.translatable; + +public class ConfigOptionWidget { + public static WidgetWithBounds create(CompositeOption option, int width) { + List widgets = new ArrayList<>(); + int[] height = {12}; + widgets.add(Widgets.createLabel(new Point(0, 0), option.getName().copy().withStyle(style -> style.withColor(0xFFC0C0C0))) + .leftAligned()); + WidgetWithBounds optionValue = ConfigOptionValueWidget.create(option); + widgets.add(Widgets.withTranslate(optionValue, () -> Matrix4f.createTranslateMatrix(width - optionValue.getBounds().width - optionValue.getBounds().x, 0, 0))); + widgets.add(new WidgetWithBounds() { + final MutableComponent description = option.getDescription().copy().withStyle(style -> style.withColor(0xFF757575)); + final List split = Minecraft.getInstance().font.split(description, width); + final boolean hasPreview = option.hasPreview(); + final Label preview = Widgets.createLabel(new Point(), translatable("config.rei.texts.preview")) + .color(0xFFA5F4FF) + .hoveredColor(0xFFD1FAFF) + .noShadow() + .clickable() + .rightAligned(); + boolean nextLinePreview = false; + + { + height[0] += 12 * split.size(); + if (hasPreview) { + int lastWidth = Minecraft.getInstance().font.width(split.get(split.size() - 1)); + if (lastWidth + preview.getBounds().width + 10 > width) { + nextLinePreview = true; + height[0] += 12; + } + } + } + + @Override + public Rectangle getBounds() { + return new Rectangle(0, 12, width, 12 + 12 * split.size()); + } + + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + for (int i = 0; i < split.size(); i++) { + Minecraft.getInstance().font.draw(poses, split.get(i), 0, 12 + 12 * i, -1); + } + + if (hasPreview) { + if (nextLinePreview) { + this.preview.setPoint(new Point(width, 12 + 12 * split.size())); + } else { + this.preview.setPoint(new Point(width, 12 + 12 * split.size() - 12)); + } + + this.preview.render(poses, mouseX, mouseY, delta); + } + } + + @Override + public List children() { + return List.of(preview); + } + }); + return Widgets.concatWithBounds(new Rectangle(0, 0, width, height[0]), widgets); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java index 706d41200..58827c465 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java @@ -25,6 +25,7 @@ import me.shedaniel.rei.api.client.gui.config.*; import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; +import me.shedaniel.rei.impl.client.gui.config.options.preview.ThemePreviewer; import java.util.function.BiConsumer; import java.util.function.Function; @@ -35,11 +36,12 @@ interface AllREIConfigOptions { static CompositeOption make(String id, Function bind, BiConsumer save) { return new CompositeOption<>(translatable("config.rei.options." + id), - translatable("config.rei.options." + id + ".desc"), bind); + translatable("config.rei.options." + id + ".desc"), bind, save); } CompositeOption THEME = make("appearance.theme", i -> i.appearance.theme, (i, v) -> i.appearance.theme = v) - .enumOptions(); + .enumOptions() + .previewer(ThemePreviewer.INSTANCE); CompositeOption RECIPE_BORDER = make("appearance.recipe_border", i -> i.appearance.recipeBorder, (i, v) -> i.appearance.recipeBorder = v) .enumOptions(); CompositeOption REDUCED_MOTION = make("appearance.reduced_motion", i -> i.basics.reduceMotion, (i, v) -> i.basics.reduceMotion = v) @@ -53,7 +55,7 @@ static CompositeOption make(String id, Function bind // TODO: NATIVE KEYBINDS CompositeOption USE_NATIVE_KEYBINDS = make("keybinds.use_native_keybinds", i -> i.basics.keyBindings.useNativeKeybinds, (i, v) -> i.basics.keyBindings.useNativeKeybinds = v) .enabledDisabled(); - CompositeOption CHEATS_MODE = make("cheats.cheats", i -> i.basics.cheating, (i, v) -> i.basics.cheating = v) + CompositeOption CHEATS_MODE = make("cheats.mode", i -> i.basics.cheating, (i, v) -> i.basics.cheating = v) .enumOptions(); CompositeOption CHEATS_METHOD = make("cheats.method", i -> i.basics.cheatingStyle, (i, v) -> i.basics.cheatingStyle = v) .enumOptions(); 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 index 7d7eb46af..11f9126db 100644 --- 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 @@ -25,19 +25,25 @@ import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Nullable; +import java.util.function.BiConsumer; import java.util.function.Function; public class CompositeOption { private final Component name; private final Component description; private final Function bind; + private final BiConsumer save; + @Nullable + private ConfigPreviewer previewer; private OptionValueEntry entry; - public CompositeOption(Component name, Component description, Function bind) { + public CompositeOption(Component name, Component description, Function bind, BiConsumer save) { this.name = name; this.description = description; this.bind = bind; + this.save = save; } public CompositeOption entry(OptionValueEntry entry) { @@ -60,4 +66,38 @@ public CompositeOption enabledDisabled() { public CompositeOption enumOptions(T... entry) { return this.entry(OptionValueEntry.enumOptions(entry)); } + + public CompositeOption previewer(ConfigPreviewer previewer) { + this.previewer = previewer; + return this; + } + + public Component getName() { + return name; + } + + public Component getDescription() { + return description; + } + + public Function getBind() { + return bind; + } + + public BiConsumer getSave() { + return save; + } + + public OptionValueEntry getEntry() { + return entry; + } + + @Nullable + public ConfigPreviewer getPreviewer() { + return previewer; + } + + public boolean hasPreview() { + return previewer != null; + } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/ConfigPreviewer.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/ConfigPreviewer.java new file mode 100644 index 000000000..0dfd5cba0 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/ConfigPreviewer.java @@ -0,0 +1,32 @@ +/* + * 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.widgets.WidgetWithBounds; + +import java.util.function.Supplier; + +public interface ConfigPreviewer { + WidgetWithBounds preview(int width, Supplier value); +} 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 index 3bf883e05..82f151d8c 100644 --- 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 @@ -27,7 +27,7 @@ import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; -interface ConfigUtils { +public interface ConfigUtils { static MutableComponent literal(String text) { return new TextComponent(text); } 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 index ad8fdaa28..342525516 100644 --- 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 @@ -55,4 +55,8 @@ public ResourceLocation getIcon() { public Component getName() { return name; } + + public List getGroups() { + return groups; + } } 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 index f0ea512aa..36373ac1e 100644 --- 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 @@ -40,4 +40,12 @@ public OptionGroup add(CompositeOption option) { this.options.add(option); return this; } + + public Component getGroupName() { + return groupName; + } + + public List> getOptions() { + return options; + } } 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 index 43c918681..6ec876266 100644 --- 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 @@ -28,6 +28,7 @@ import java.util.List; +import static me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils.literal; import static me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils.translatable; public interface OptionValueEntry { @@ -39,8 +40,8 @@ static OptionValueEntry noOp() { static OptionValueEntry ofBoolean(Component falseText, Component trueText) { return new Selection() { @Override - public List getOptions() { - return List.of(falseText, trueText); + public List getOptions() { + return List.of(false, true); } @Override @@ -61,23 +62,23 @@ static OptionValueEntry enabledDisabled() { } static OptionValueEntry enumOptions(T... array) { - Class type = array.getClass().getComponentType(); + Class type = (Class) array.getClass().getComponentType(); Object[] constants = type.getEnumConstants(); return new Selection() { @Override - public List getOptions() { - return CollectionUtils.map(constants, o -> getOption((T) o)); + public List getOptions() { + return CollectionUtils.map(constants, type::cast); } @Override public Component getOption(T value) { - return null; + return literal(value.toString()); } }; } interface Selection extends OptionValueEntry { - List getOptions(); + List getOptions(); Component getOption(T value); } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/preview/ThemePreviewer.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/preview/ThemePreviewer.java new file mode 100644 index 000000000..df5ac9be6 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/preview/ThemePreviewer.java @@ -0,0 +1,49 @@ +/* + * 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.preview; + +import me.shedaniel.clothconfig2.api.animator.ValueAnimator; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.config.AppearanceTheme; +import me.shedaniel.rei.api.client.gui.widgets.Panel; +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.ConfigPreviewer; +import me.shedaniel.rei.impl.client.gui.widget.basewidgets.PanelWidget; + +import java.util.List; +import java.util.function.Supplier; + +public enum ThemePreviewer implements ConfigPreviewer { + INSTANCE; + + @Override + public WidgetWithBounds preview(int width, Supplier value) { + Panel base = Widgets.createCategoryBase(new Rectangle(width * 10 / 2, 3, width * 10 / 8, 20)); + ((PanelWidget) base).setDarkBackgroundAlpha(ValueAnimator.ofFloat() + .withConvention(() -> value.get() == AppearanceTheme.DARK ? 1.0F : 0.0F, ValueAnimator.typicalTransitionTime()) + .asFloat()); + return Widgets.concatWithBounds(new Rectangle(0, 0, width, 26), List.of(base)); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ListWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ListWidget.java index 7f66ff7cb..c7beda3ed 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ListWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ListWidget.java @@ -48,7 +48,7 @@ public static WidgetBuilder builderOfWidgets(Rec public static abstract class AbstractBuilder> { protected final Rectangle bounds; protected IntValue selected = new IntValue() { - private int value; + private int value = -1; @Override public void accept(int i) { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/basewidgets/PanelWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/basewidgets/PanelWidget.java index eb36cc4d7..447456eae 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/basewidgets/PanelWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/basewidgets/PanelWidget.java @@ -38,6 +38,7 @@ import me.shedaniel.rei.impl.client.gui.InternalTextures; import net.minecraft.client.gui.GuiComponent; import net.minecraft.client.gui.components.events.GuiEventListener; +import org.jetbrains.annotations.ApiStatus; import java.util.Collections; import java.util.List; @@ -51,7 +52,7 @@ public final class PanelWidget extends Panel { private int xTextureOffset = 0; private int yTextureOffset = RecipeBorderType.DEFAULT.getYOffset(); private Predicate rendering = Predicates.alwaysTrue(); - private final NumberAnimator darkBackgroundAlpha = ValueAnimator.ofFloat() + private NumberAnimator darkBackgroundAlpha = ValueAnimator.ofFloat() .withConvention(() -> REIRuntime.getInstance().isDarkThemeEnabled() ? 1.0F : 0.0F, ValueAnimator.typicalTransitionTime()) .asFloat(); @@ -103,6 +104,11 @@ public void setRendering(Predicate rendering) { this.rendering = Objects.requireNonNull(rendering); } + @ApiStatus.Internal + public void setDarkBackgroundAlpha(NumberAnimator darkBackgroundAlpha) { + this.darkBackgroundAlpha = darkBackgroundAlpha; + } + @Override public Rectangle getBounds() { return bounds; diff --git a/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json b/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json index 1d91875ad..9eeddb001 100755 --- a/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json +++ b/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json @@ -375,5 +375,6 @@ "config.rei.value.trueFalse.false": "False", "config.rei.value.trueFalse.true": "True", "config.rei.value.enabledDisabled.false": "Disabled", - "config.rei.value.enabledDisabled.true": "Enabled" + "config.rei.value.enabledDisabled.true": "Enabled", + "config.rei.texts.preview": "Preview..." }