From 8b8ded201bda1597bb8bacdda7f9882d6026976f Mon Sep 17 00:00:00 2001 From: shedaniel Date: Wed, 1 Feb 2023 02:18:42 +0800 Subject: Close #1249 --- .../rei/impl/client/config/ConfigManagerImpl.java | 54 ++- .../collapsible/CollapsibleConfigManager.java | 102 ++++ .../client/config/entries/ButtonsConfigEntry.java | 114 +++++ .../config/entries/FilteringAddRuleScreen.java | 5 + .../config/entries/FilteringCategoriesScreen.java | 5 + .../config/entries/FilteringRuleOptionsScreen.java | 378 --------------- .../config/entries/FilteringRulesScreen.java | 20 +- .../client/config/entries/FilteringScreen.java | 6 + .../client/config/entries/ReloadPluginsEntry.java | 117 ----- .../filtering/rules/SearchFilteringRuleType.java | 24 +- .../gui/performance/entry/PerformanceEntry.java | 92 ---- .../collapsible/CollapsibleEntriesScreen.java | 316 ++++++++++++ .../screen/collapsible/CollapsibleEntryWidget.java | 296 ++++++++++++ .../CustomCollapsibleEntrySelectionScreen.java | 529 +++++++++++++++++++++ .../gui/screen/generic/OptionEntriesScreen.java | 378 +++++++++++++++ .../widget/DynamicErrorFreeEntryListWidget.java | 17 +- .../widget/entrylist/EntryListSearchManager.java | 12 +- .../impl/common/entry/type/EntryRegistryImpl.java | 6 +- .../impl/common/entry/type/FilteredEntryList.java | 4 +- .../common/entry/type/PreFilteredEntryList.java | 21 +- .../collapsed/CollapsibleEntryRegistryImpl.java | 27 ++ .../assets/roughlyenoughitems/lang/en_us.json | 14 + 22 files changed, 1913 insertions(+), 624 deletions(-) create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/config/collapsible/CollapsibleConfigManager.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/ButtonsConfigEntry.java delete mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringRuleOptionsScreen.java delete mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/ReloadPluginsEntry.java delete mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/collapsible/CollapsibleEntriesScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/collapsible/CollapsibleEntryWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/collapsible/selection/CustomCollapsibleEntrySelectionScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/generic/OptionEntriesScreen.java 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 d016b9152..681356a99 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 @@ -47,6 +47,8 @@ import me.shedaniel.clothconfig2.api.Modifier; import me.shedaniel.clothconfig2.api.ModifierKeyCode; import me.shedaniel.clothconfig2.gui.entries.KeyCodeEntry; import me.shedaniel.clothconfig2.gui.entries.TextListEntry; +import me.shedaniel.rei.RoughlyEnoughItemsCore; +import me.shedaniel.rei.RoughlyEnoughItemsCoreClient; import me.shedaniel.rei.api.client.REIRuntime; import me.shedaniel.rei.api.client.config.ConfigManager; import me.shedaniel.rei.api.client.config.addon.ConfigAddonRegistry; @@ -59,17 +61,24 @@ import me.shedaniel.rei.api.client.gui.config.CheatingMode; import me.shedaniel.rei.api.client.gui.config.DisplayScreenType; import me.shedaniel.rei.api.client.gui.config.SyntaxHighlightingMode; import me.shedaniel.rei.api.client.overlay.ScreenOverlay; +import me.shedaniel.rei.api.client.registry.entry.CollapsibleEntryRegistry; import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; import me.shedaniel.rei.api.common.category.CategoryIdentifier; import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.plugins.PluginManager; import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.impl.client.REIRuntimeImpl; 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.credits.CreditsScreen; -import me.shedaniel.rei.impl.client.gui.performance.entry.PerformanceEntry; +import me.shedaniel.rei.impl.client.gui.performance.PerformanceScreen; +import me.shedaniel.rei.impl.client.gui.screen.ConfigReloadingScreen; +import me.shedaniel.rei.impl.client.gui.screen.collapsible.CollapsibleEntriesScreen; +import me.shedaniel.rei.impl.client.search.argument.Argument; import me.shedaniel.rei.impl.common.InternalLogger; +import me.shedaniel.rei.impl.common.entry.type.collapsed.CollapsibleEntryRegistryImpl; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.ChatFormatting; @@ -89,6 +98,7 @@ import net.minecraft.network.chat.TextColor; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.InteractionResult; +import org.apache.commons.lang3.tuple.Triple; import org.jetbrains.annotations.ApiStatus; import java.util.*; @@ -153,6 +163,7 @@ public class ConfigManagerImpl implements ConfigManager { InternalLogger.getInstance().info("Config loaded"); saveConfig(); FavoritesConfigManager.getInstance().syncFrom(this); + CollapsibleConfigManager.getInstance().syncFrom(this); } public static Jankson buildJankson(Jankson.Builder builder) { @@ -414,9 +425,46 @@ public class ConfigManagerImpl implements ConfigManager { )) .withStyle(ChatFormatting.GRAY) ).build(); + CollapsibleConfigManager.CollapsibleConfigObject collapsibleConfigObject = new CollapsibleConfigManager.CollapsibleConfigObject(); + collapsibleConfigObject.disabledGroups.addAll(CollapsibleConfigManager.getInstance().getConfig().disabledGroups); + collapsibleConfigObject.customGroups.addAll(CollectionUtils.map(CollapsibleConfigManager.getInstance().getConfig().customGroups, CollapsibleConfigManager.CustomGroup::copy)); + builder.getOrCreateCategory(new TranslatableComponent("config.roughlyenoughitems.functionality")).getEntries().add(0, new ButtonsConfigEntry(220, + Triple.of(new TranslatableComponent("text.rei.collapsible.entries"), $ -> {}, editedSink -> { + Minecraft.getInstance().setScreen(new CollapsibleEntriesScreen(Minecraft.getInstance().screen, collapsibleConfigObject, editedSink)); + })).withSaveRunnable(() -> { + CollapsibleConfigManager.CollapsibleConfigObject actualConfig = CollapsibleConfigManager.getInstance().getConfig(); + actualConfig.disabledGroups.clear(); + actualConfig.disabledGroups.addAll(collapsibleConfigObject.disabledGroups); + actualConfig.customGroups.clear(); + actualConfig.customGroups.addAll(collapsibleConfigObject.customGroups); + CollapsibleConfigManager.getInstance().saveConfig(); + ((CollapsibleEntryRegistryImpl) CollapsibleEntryRegistry.getInstance()).recollectCustomEntries(); + })); builder.getOrCreateCategory(Component.translatable("config.roughlyenoughitems.advanced")).getEntries().add(0, feedbackEntry); - builder.getOrCreateCategory(Component.translatable("config.roughlyenoughitems.advanced")).getEntries().add(0, new ReloadPluginsEntry(220)); - builder.getOrCreateCategory(Component.translatable("config.roughlyenoughitems.advanced")).getEntries().add(0, new PerformanceEntry(220)); + builder.getOrCreateCategory(Component.translatable("config.roughlyenoughitems.advanced")).getEntries().add(0, new ButtonsConfigEntry(220, + Triple.of(new TranslatableComponent("text.rei.reload_config"), $ -> {}, $ -> { + RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.clear(); + RoughlyEnoughItemsCoreClient.reloadPlugins(null, null); + }), + Triple.of(new TranslatableComponent("text.rei.reload_search"), button -> { + button.active = button.active && Argument.hasCache(); + }, $ -> { + Argument.resetCache(true); + })) { + @Override + public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) { + if (PluginManager.areAnyReloading()) { + Screen screen = Minecraft.getInstance().screen; + Minecraft.getInstance().setScreen(new ConfigReloadingScreen(new TranslatableComponent("text.rei.config.is.reloading"), PluginManager::areAnyReloading, () -> Minecraft.getInstance().setScreen(screen))); + } else { + super.render(matrices, index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta); + } + } + }); + builder.getOrCreateCategory(Component.translatable("config.roughlyenoughitems.advanced")).getEntries().add(0, new ButtonsConfigEntry(220, + Triple.of(new TranslatableComponent("text.rei.performance"), $ -> {}, $ -> { + Minecraft.getInstance().setScreen(new PerformanceScreen(Minecraft.getInstance().screen)); + }))); } ConfigAddonRegistryImpl addonRegistry = (ConfigAddonRegistryImpl) ConfigAddonRegistry.getInstance(); if (!addonRegistry.getAddons().isEmpty()) { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/collapsible/CollapsibleConfigManager.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/collapsible/CollapsibleConfigManager.java new file mode 100644 index 000000000..edcca423d --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/collapsible/CollapsibleConfigManager.java @@ -0,0 +1,102 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 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.config.collapsible; + +import me.shedaniel.autoconfig.AutoConfig; +import me.shedaniel.autoconfig.ConfigData; +import me.shedaniel.autoconfig.annotation.Config; +import me.shedaniel.autoconfig.serializer.JanksonConfigSerializer; +import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.Jankson; +import me.shedaniel.rei.api.client.config.entry.EntryStackProvider; +import me.shedaniel.rei.impl.client.config.ConfigManagerImpl; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.InteractionResult; +import org.jetbrains.annotations.ApiStatus; + +import java.util.ArrayList; +import java.util.List; + +import static me.shedaniel.rei.impl.client.config.ConfigManagerImpl.buildJankson; + +@ApiStatus.Internal +public class CollapsibleConfigManager { + private static final CollapsibleConfigManager INSTANCE = new CollapsibleConfigManager(); + private CollapsibleConfigObject object; + + public CollapsibleConfigManager() { + AutoConfig.register(CollapsibleConfigObject.class, (definition, configClass) -> new JanksonConfigSerializer<>(definition, configClass, buildJankson(Jankson.builder()))); + } + + public static CollapsibleConfigManager getInstance() { + return INSTANCE; + } + + public void saveConfig() { + AutoConfig.getConfigHolder(CollapsibleConfigObject.class).registerLoadListener((configHolder, configObject) -> { + object = configObject; + return InteractionResult.PASS; + }); + AutoConfig.getConfigHolder(CollapsibleConfigObject.class).save(); + } + + public CollapsibleConfigObject getConfig() { + if (object == null) { + object = AutoConfig.getConfigHolder(CollapsibleConfigObject.class).getConfig(); + } + return object; + } + + public void syncFrom(ConfigManagerImpl manager) { + manager.saveConfig(); + this.saveConfig(); + } + + @Config(name = "roughlyenoughitems/collapsible") + @Environment(EnvType.CLIENT) + public static final class CollapsibleConfigObject implements ConfigData { + public List disabledGroups = new ArrayList<>(); + public List customGroups = new ArrayList<>(); + } + + public static final class CustomGroup { + public ResourceLocation id = new ResourceLocation("missingno"); + public String name = "Invalid"; + public List> stacks = new ArrayList<>(); + + public CustomGroup() { + } + + public CustomGroup(ResourceLocation id, String name, List> stacks) { + this.id = id; + this.name = name; + this.stacks = stacks; + } + + public CustomGroup copy() { + return new CustomGroup(this.id, this.name, new ArrayList<>(this.stacks)); + } + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/ButtonsConfigEntry.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/ButtonsConfigEntry.java new file mode 100644 index 000000000..707c1eca5 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/ButtonsConfigEntry.java @@ -0,0 +1,114 @@ +/* + * 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.config.entries; + +import com.mojang.blaze3d.platform.Window; +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import net.minecraft.util.Unit; +import org.apache.commons.lang3.tuple.Triple; +import org.jetbrains.annotations.ApiStatus; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +@ApiStatus.Internal +public class ButtonsConfigEntry extends AbstractConfigListEntry { + private final int width; + private final List children; + private boolean edited; + private Runnable saveRunnable = () -> {}; + + public ButtonsConfigEntry(int width, Triple, Consumer>... buttons) { + super(Component.empty(), false); + this.width = width; + this.children = Arrays.stream(buttons).map(pair -> { + return (AbstractWidget) new Button(0, 0, 0, 20, pair.getLeft(), button -> { + pair.getRight().accept(() -> this.edited = true); + }) { + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + pair.getMiddle().accept(this); + super.render(poses, mouseX, mouseY, delta); + } + }; + }).toList(); + } + + public ButtonsConfigEntry withSaveRunnable(Runnable runnable) { + this.saveRunnable = runnable; + return this; + } + + @Override + public Unit getValue() { + return Unit.INSTANCE; + } + + @Override + public Optional getDefaultValue() { + return Optional.of(Unit.INSTANCE); + } + + @Override + public void save() { + this.saveRunnable.run(); + } + + @Override + public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) { + super.render(matrices, index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta); + Window window = Minecraft.getInstance().getWindow(); + for (AbstractWidget widget : this.children) { + widget.active = this.isEditable(); + widget.y = y; + widget.setWidth(width / this.children.size() - (this.children.size() == 1 ? 0 : 2)); + widget.x = x + entryWidth / 2 - width / 2 + (widget.getWidth() + 2) * this.children.indexOf(widget); + widget.render(matrices, mouseX, mouseY, delta); + } + } + + @Override + public List children() { + return children; + } + + @Override + public List narratables() { + return children; + } + + @Override + public boolean isEdited() { + return edited; + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringAddRuleScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringAddRuleScreen.java index 438359cd5..3f2f220ac 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringAddRuleScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringAddRuleScreen.java @@ -78,6 +78,11 @@ public class FilteringAddRuleScreen extends Screen { this.font.drawShadow(matrices, this.title.getVisualOrderText(), this.width / 2.0F - this.font.width(this.title) / 2.0F, 12.0F, -1); } + @Override + public void onClose() { + this.minecraft.setScreen(parent); + } + public static class RulesList extends DynamicElementListWidget { private boolean inFocus; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringCategoriesScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringCategoriesScreen.java index b9f63385d..5bb6a98a9 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringCategoriesScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringCategoriesScreen.java @@ -76,6 +76,11 @@ public class FilteringCategoriesScreen extends Screen { this.font.drawShadow(matrices, this.title.getVisualOrderText(), this.width / 2.0F - this.font.width(this.title) / 2.0F, 12.0F, -1); } + @Override + public void onClose() { + this.minecraft.setScreen(parent); + } + private static class ListWidget extends DynamicElementListWidget { private boolean inFocus; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringRuleOptionsScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringRuleOptionsScreen.java deleted file mode 100644 index bc7f9807c..000000000 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringRuleOptionsScreen.java +++ /dev/null @@ -1,378 +0,0 @@ -/* - * 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.config.entries; - -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.PoseStack; -import me.shedaniel.clothconfig2.gui.widget.DynamicElementListWidget; -import me.shedaniel.math.Rectangle; -import me.shedaniel.rei.api.client.entry.filtering.FilteringRule; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.EditBox; -import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.narration.NarratableEntry; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.resources.sounds.SimpleSoundInstance; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.FormattedText; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.util.FormattedCharSequence; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -public abstract class FilteringRuleOptionsScreen> extends Screen { - private RulesList rulesList; - Screen parent; - public T rule; - - public FilteringRuleOptionsScreen(T rule, Screen screen) { - super(Component.translatable("config.roughlyenoughitems.filteringRulesScreen")); - this.rule = rule; - this.parent = screen; - } - - @Override - public void init() { - super.init(); - if (rulesList != null) save(); - { - Component doneText = Component.translatable("gui.done"); - int width = Minecraft.getInstance().font.width(doneText); - addRenderableWidget(new Button(this.width - 4 - width - 10, 4, width + 10, 20, doneText, button -> { - save(); - minecraft.setScreen(parent); - })); - } - rulesList = addWidget(new RulesList(minecraft, width, height, 30, height, BACKGROUND_LOCATION)); - addEntries(ruleEntry -> rulesList.addItem(ruleEntry)); - } - - public abstract void addEntries(Consumer entryConsumer); - - public abstract void save(); - - public void addText(Consumer entryConsumer, FormattedText text) { - for (FormattedCharSequence s : font.split(text, width - 80)) { - entryConsumer.accept(new TextRuleEntry(rule, s)); - } - } - - public void addEmpty(Consumer entryConsumer, int height) { - entryConsumer.accept(new EmptyRuleEntry(rule, height)); - } - - @Override - public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { - this.rulesList.render(matrices, mouseX, mouseY, delta); - super.render(matrices, mouseX, mouseY, delta); - this.font.drawShadow(matrices, this.title.getVisualOrderText(), this.width / 2.0F - this.font.width(this.title) / 2.0F, 12.0F, -1); - } - - public static class RulesList extends DynamicElementListWidget { - public RulesList(Minecraft client, int width, int height, int top, int bottom, ResourceLocation backgroundLocation) { - super(client, width, height, top, bottom, backgroundLocation); - } - - @Override - protected int addItem(RuleEntry item) { - return super.addItem(item); - } - - @Override - public int getItemWidth() { - return width - 40; - } - - @Override - protected int getScrollbarPosition() { - return width - 14; - } - } - - public static abstract class RuleEntry extends DynamicElementListWidget.ElementEntry { - private final FilteringRule rule; - - public RuleEntry(FilteringRule rule) { - this.rule = rule; - } - - public FilteringRule getRule() { - return rule; - } - } - - public static class TextRuleEntry extends RuleEntry { - private final FormattedCharSequence text; - - public TextRuleEntry(FilteringRule rule, FormattedCharSequence text) { - super(rule); - this.text = text; - } - - @Override - public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { - Minecraft.getInstance().font.drawShadow(matrices, text, x + 5, y, -1); - } - - @Override - public int getItemHeight() { - return 12; - } - - @Override - public List children() { - return Collections.emptyList(); - } - - @Override - public List narratables() { - return Collections.emptyList(); - } - } - - public static class EmptyRuleEntry extends RuleEntry { - private final int height; - - public EmptyRuleEntry(FilteringRule rule, int height) { - super(rule); - this.height = height; - } - - @Override - public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { - } - - @Override - public int getItemHeight() { - return height; - } - - @Override - public List children() { - return Collections.emptyList(); - } - - @Override - public List narratables() { - return Collections.emptyList(); - } - } - - public static class TextFieldRuleEntry extends RuleEntry { - private final EditBox widget; - - public TextFieldRuleEntry(int width, FilteringRule rule, Consumer widgetConsumer) { - super(rule); - this.widget = new EditBox(Minecraft.getInstance().font, 0, 0, width, 18, Component.nullToEmpty("")); - widgetConsumer.accept(widget); - } - - @Override - public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { - widget.x = x + 2; - widget.y = y + 2; - widget.render(matrices, mouseX, mouseY, delta); - } - - @Override - public int getItemHeight() { - return 20; - } - - public EditBox getWidget() { - return widget; - } - - @Override - public List children() { - return Collections.singletonList(widget); - } - - @Override - public List narratables() { - return Collections.singletonList(widget); - } - } - - public static class BooleanRuleEntry extends RuleEntry { - private boolean b; - private final Button widget; - - public BooleanRuleEntry(int width, boolean b, FilteringRule rule, Function textFunction) { - super(rule); - this.b = b; - this.widget = new Button(0, 0, 100, 20, textFunction.apply(b), button -> { - this.b = !this.b; - button.setMessage(textFunction.apply(this.b)); - }); - } - - public boolean getBoolean() { - return b; - } - - @Override - public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { - widget.x = x + 2; - widget.y = y; - widget.render(matrices, mouseX, mouseY, delta); - } - - @Override - public int getItemHeight() { - return 20; - } - - @Override - public List children() { - return Collections.singletonList(widget); - } - - @Override - public List narratables() { - return Collections.singletonList(widget); - } - } - - public static class SubRulesEntry extends RuleEntry { - private static final ResourceLocation CONFIG_TEX = new ResourceLocation("cloth-config2", "textures/gui/cloth_config.png"); - private final CategoryLabelWidget widget; - private final List rules; - private final List children; - private boolean expanded; - private Supplier name; - - public SubRulesEntry(FilteringRule rule, Supplier name, List rules) { - super(rule); - this.rules = rules; - this.widget = new CategoryLabelWidget(); - this.name = name; - this.expanded = true; - this.children = new ArrayList<>(rules); - this.children.add(widget); - } - - public List getRules() { - return rules; - } - - @Override - public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { - RenderSystem.setShaderTexture(0, CONFIG_TEX); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - this.widget.rectangle.x = x + 3; - this.widget.rectangle.y = y; - this.widget.rectangle.width = entryWidth - 6; - this.widget.rectangle.height = 24; - this.blit(matrices, x + 3, y + 5, 24, (this.widget.rectangle.contains(mouseX, mouseY) ? 18 : 0) + (this.expanded ? 9 : 0), 9, 9); - Minecraft.getInstance().font.drawShadow(matrices, this.name.get().getVisualOrderText(), (float) x + 3 + 15, (float) (y + 6), this.widget.rectangle.contains(mouseX, mouseY) ? -1638890 : -1); - - for (RuleEntry performanceEntry : this.rules) { - performanceEntry.setParent(this.getParent()); - } - - if (this.expanded) { - int yy = y + 24; - - RuleEntry entry; - for (Iterator iterator = this.rules.iterator(); iterator.hasNext(); yy += entry.getItemHeight()) { - entry = iterator.next(); - entry.render(matrices, -1, yy, x + 3 + 15, entryWidth - 15 - 3, entry.getItemHeight(), mouseX, mouseY, isHovered && this.getFocused() == entry, delta); - } - } - } - - @Override - public int getMorePossibleHeight() { - if (!this.expanded) { - return -1; - } else { - List list = new ArrayList<>(); - int i = 24; - - for (RuleEntry entry : this.rules) { - i += entry.getItemHeight(); - if (entry.getMorePossibleHeight() >= 0) { - list.add(i + entry.getMorePossibleHeight()); - } - } - - list.add(i); - return list.stream().max(Integer::compare).orElse(0) - this.getItemHeight(); - } - } - - @Override - public int getItemHeight() { - if (!this.expanded) { - return 24; - } else { - int i = 24; - - RuleEntry entry; - for (Iterator iterator = this.rules.iterator(); iterator.hasNext(); i += entry.getItemHeight()) { - entry = iterator.next(); - } - - return i; - } - } - - @Override - public List children() { - return children; - } - - @Override - public List narratables() { - return Collections.emptyList(); - } - - public class CategoryLabelWidget implements GuiEventListener { - private final Rectangle rectangle = new Rectangle(); - - public CategoryLabelWidget() { - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (this.rectangle.contains(mouseX, mouseY)) { - SubRulesEntry.this.expanded = !SubRulesEntry.this.expanded; - Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); - return true; - } else { - return false; - } - } - } - } -} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringRulesScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringRulesScreen.java index 6d899d5a5..015f5703a 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringRulesScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringRulesScreen.java @@ -37,6 +37,7 @@ import me.shedaniel.rei.impl.client.entry.filtering.FilteringContextType; import me.shedaniel.rei.impl.client.entry.filtering.rules.ManualFilteringRule; import me.shedaniel.rei.impl.client.entry.filtering.rules.SearchFilteringRuleType; import me.shedaniel.rei.impl.client.gui.InternalTextures; +import me.shedaniel.rei.impl.client.gui.screen.generic.OptionEntriesScreen; import me.shedaniel.rei.impl.client.gui.widget.EntryWidget; import me.shedaniel.rei.impl.common.entry.type.FilteringLogic; import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper; @@ -105,6 +106,11 @@ public class FilteringRulesScreen extends Screen { this.font.drawShadow(matrices, this.title.getVisualOrderText(), this.width / 2.0F - this.font.width(this.title) / 2.0F, 12.0F, -1); } + @Override + public void onClose() { + this.minecraft.setScreen(parent); + } + public static class RulesList extends DynamicElementListWidget { private boolean inFocus; @@ -261,26 +267,26 @@ public class FilteringRulesScreen extends Screen { } private static Function placeholderScreen(FilteringRule r) { - class PlaceholderScreen extends FilteringRuleOptionsScreen> { + class PlaceholderScreen extends OptionEntriesScreen { public PlaceholderScreen(Screen parent) { - super(r, parent); + super(new TranslatableComponent("config.roughlyenoughitems.filteringRulesScreen"), parent); } @Override - public void addEntries(Consumer entryConsumer) { + public void addEntries(Consumer entryConsumer) { addEmpty(entryConsumer, 10); Function function = bool -> { return Component.translatable("rule.roughlyenoughitems.filtering.search.show." + bool); }; Map> stacks = FilteringLogic.hidden(List.of(r), false, false, EntryRegistry.getInstance().getEntryStacks().collect(Collectors.toList())); - entryConsumer.accept(new SubRulesEntry(rule, () -> function.apply(true), - Collections.singletonList(new SearchFilteringRuleType.EntryStacksRuleEntry(rule, + entryConsumer.accept(new SubListEntry(() -> function.apply(true), + Collections.singletonList(new SearchFilteringRuleType.EntryStacksRuleEntry( Suppliers.ofInstance(CollectionUtils.map(stacks.get(FilteringContextType.SHOWN), stack -> (EntryWidget) Widgets.createSlot(new Rectangle(0, 0, 18, 18)).disableBackground().entry(stack.unwrap().normalize()))))))); addEmpty(entryConsumer, 10); - entryConsumer.accept(new SubRulesEntry(rule, () -> function.apply(false), - Collections.singletonList(new SearchFilteringRuleType.EntryStacksRuleEntry(rule, + entryConsumer.accept(new SubListEntry(() -> function.apply(false), + Collections.singletonList(new SearchFilteringRuleType.EntryStacksRuleEntry( Suppliers.ofInstance(CollectionUtils.map(stacks.get(FilteringContextType.HIDDEN), stack -> (EntryWidget) Widgets.createSlot(new Rectangle(0, 0, 18, 18)).disableBackground().entry(stack.unwrap().normalize()))))))); } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java index 063c6765e..fe310f282 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java @@ -163,6 +163,12 @@ public class FilteringScreen extends Screen { this.searchField.isMain = false; } + @Override + public void onClose() { + this.minecraft.setScreen(parent); + this.parent = null; + } + private static Rectangle updateInnerBounds(Rectangle bounds) { int width = Math.max(Mth.floor((bounds.width - 2 - 6) / (float) entrySize()), 1); return new Rectangle((int) (bounds.getCenterX() - width * entrySize() / 2f), bounds.y + 5, width * entrySize(), bounds.height); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/ReloadPluginsEntry.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/ReloadPluginsEntry.java deleted file mode 100644 index adcd084ea..000000000 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/ReloadPluginsEntry.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.config.entries; - -import com.google.common.collect.ImmutableList; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.vertex.PoseStack; -import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; -import me.shedaniel.rei.RoughlyEnoughItemsCore; -import me.shedaniel.rei.RoughlyEnoughItemsCoreClient; -import me.shedaniel.rei.api.common.plugins.PluginManager; -import me.shedaniel.rei.impl.client.gui.screen.ConfigReloadingScreen; -import me.shedaniel.rei.impl.client.search.argument.Argument; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.narration.NarratableEntry; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.network.chat.Component; -import net.minecraft.util.Unit; -import org.jetbrains.annotations.ApiStatus; - -import java.util.List; -import java.util.Optional; - -@ApiStatus.Internal -public class ReloadPluginsEntry extends AbstractConfigListEntry { - private int width; - private AbstractWidget reloadPluginsButton = new Button(0, 0, 0, 20, Component.empty(), button -> { - RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.clear(); - RoughlyEnoughItemsCoreClient.reloadPlugins(null, null); - }) { - @Override - public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { - if (PluginManager.areAnyReloading()) { - Screen screen = Minecraft.getInstance().screen; - Minecraft.getInstance().setScreen(new ConfigReloadingScreen(Component.translatable("text.rei.config.is.reloading"), PluginManager::areAnyReloading, () -> Minecraft.getInstance().setScreen(screen), null)); - } else { - super.render(matrices, mouseX, mouseY, delta); - } - } - }; - private AbstractWidget reloadSearchButton = new Button(0, 0, 0, 20, Component.empty(), button -> { - Argument.resetCache(true); - }); - private List children = ImmutableList.of(reloadPluginsButton, reloadSearchButton); - - public ReloadPluginsEntry(int width) { - super(Component.empty(), false); - this.width = width; - reloadPluginsButton.setMessage(Component.translatable("text.rei.reload_config")); - reloadSearchButton.setMessage(Component.translatable("text.rei.reload_search")); - } - - @Override - public Unit getValue() { - return Unit.INSTANCE; - } - - @Override - public Optional getDefaultValue() { - return Optional.of(Unit.INSTANCE); - } - - @Override - public void save() { - - } - - @Override - public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) { - super.render(matrices, index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta); - Window window = Minecraft.getInstance().getWindow(); - this.reloadPluginsButton.active = this.isEditable(); - this.reloadPluginsButton.y = y; - this.reloadPluginsButton.setWidth(width / 2 - 2); - this.reloadPluginsButton.x = x + entryWidth / 2 - width / 2; - this.reloadPluginsButton.render(matrices, mouseX, mouseY, delta); - this.reloadSearchButton.active = this.isEditable() && Argument.hasCache(); - this.reloadSearchButton.y = y; - this.reloadSearchButton.setWidth(width / 2 - 2); - this.reloadSearchButton.x = x + entryWidth / 2 + 2; - this.reloadSearchButton.render(matrices, mouseX, mouseY, delta); - } - - @Override - public List children() { - return children; - } - - @Override - public List narratables() { - return children; - } -} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/SearchFilteringRuleType.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/SearchFilteringRuleType.java index 9ffb425d8..b2fe82b92 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/SearchFilteringRuleType.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/SearchFilteringRuleType.java @@ -26,13 +26,12 @@ package me.shedaniel.rei.impl.client.entry.filtering.rules; import com.google.common.collect.Lists; import com.mojang.blaze3d.vertex.PoseStack; import me.shedaniel.math.Rectangle; -import me.shedaniel.rei.api.client.entry.filtering.FilteringRule; import me.shedaniel.rei.api.client.entry.filtering.FilteringRuleType; import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; import me.shedaniel.rei.api.client.search.SearchFilter; import me.shedaniel.rei.api.client.search.SearchProvider; import me.shedaniel.rei.api.common.entry.EntryStack; -import me.shedaniel.rei.impl.client.config.entries.FilteringRuleOptionsScreen; +import me.shedaniel.rei.impl.client.gui.screen.generic.OptionEntriesScreen; import me.shedaniel.rei.impl.client.gui.widget.BatchedEntryRendererManager; import me.shedaniel.rei.impl.client.gui.widget.EntryWidget; import net.minecraft.ChatFormatting; @@ -89,16 +88,16 @@ public enum SearchFilteringRuleType implements FilteringRuleType createEntryScreen(SearchFilteringRule rule) { - return screen -> new FilteringRuleOptionsScreen<>(rule, screen) { - TextFieldRuleEntry entry = null; - BooleanRuleEntry show = null; + return screen -> new OptionEntriesScreen(new TranslatableComponent("config.roughlyenoughitems.filteringRulesScreen"), screen) { + TextFieldListEntry entry = null; + BooleanListEntry show = null; List entryStacks = new ArrayList<>(); @Override - public void addEntries(Consumer entryConsumer) { + public void addEntries(Consumer entryConsumer) { addEmpty(entryConsumer, 10); addText(entryConsumer, Component.translatable("rule.roughlyenoughitems.filtering.search.filter").withStyle(ChatFormatting.GRAY)); - entryConsumer.accept(entry = new TextFieldRuleEntry(width - 36, rule, widget -> { + entryConsumer.accept(entry = new TextFieldListEntry(width - 36, widget -> { widget.setMaxLength(9999); widget.setResponder(searchTerm -> { SearchFilter filter = SearchProvider.getInstance().createFilter(searchTerm); @@ -116,10 +115,10 @@ public enum SearchFilteringRuleType implements FilteringRuleType function = bool -> { return Component.translatable("rule.roughlyenoughitems.filtering.search.show." + bool); }; - entryConsumer.accept(show = new BooleanRuleEntry(width - 36, show == null ? rule.show : show.getBoolean(), rule, function)); + entryConsumer.accept(show = new BooleanListEntry(width - 36, show == null ? rule.show : show.getBoolean(), function)); addEmpty(entryConsumer, 10); - entryConsumer.accept(new SubRulesEntry(rule, () -> function.apply(show == null ? rule.show : show.getBoolean()), - Collections.singletonList(new EntryStacksRuleEntry(rule, () -> entryStacks)))); + entryConsumer.accept(new SubListEntry(() -> function.apply(show == null ? rule.show : show.getBoolean()), + Collections.singletonList(new EntryStacksRuleEntry(() -> entryStacks)))); } @Override @@ -135,12 +134,11 @@ public enum SearchFilteringRuleType implements FilteringRuleType> entryStacks; private int totalHeight; - public EntryStacksRuleEntry(FilteringRule rule, Supplier> entryStacks) { - super(rule); + public EntryStacksRuleEntry(Supplier> entryStacks) { this.entryStacks = entryStacks; } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceEntry.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceEntry.java deleted file mode 100644 index 7f6031fe8..000000000 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceEntry.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.performance.entry; - -import com.google.common.collect.ImmutableList; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.vertex.PoseStack; -import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; -import me.shedaniel.rei.impl.client.gui.performance.PerformanceScreen; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.narration.NarratableEntry; -import net.minecraft.network.chat.Component; -import net.minecraft.util.Unit; -import org.jetbrains.annotations.ApiStatus; - -import java.util.List; -import java.util.Optional; - -@ApiStatus.Internal -public class PerformanceEntry extends AbstractConfigListEntry { - private int width; - private AbstractWidget buttonWidget = new Button(0, 0, 0, 20, Component.empty(), button -> { - Minecraft.getInstance().setScreen(new PerformanceScreen(Minecraft.getInstance().screen)); - }); - private List children = ImmutableList.of(buttonWidget); - - public PerformanceEntry(int width) { - super(Component.empty(), false); - this.width = width; - buttonWidget.setMessage(Component.translatable("text.rei.performance")); - } - - @Override - public Unit getValue() { - return Unit.INSTANCE; - } - - @Override - public Optional getDefaultValue() { - return Optional.of(Unit.INSTANCE); - } - - @Override - public void save() { - - } - - @Override - public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) { - super.render(matrices, index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta); - Window window = Minecraft.getInstance().getWindow(); - this.buttonWidget.active = this.isEditable(); - this.buttonWidget.y = y; - this.buttonWidget.x = x + entryWidth / 2 - width / 2; - this.buttonWidget.setWidth(width); - this.buttonWidget.render(matrices, mouseX, mouseY, delta); - } - - @Override - public List children() { - return children; - } - - @Override - public List narratables() { - return children; - } -} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/collapsible/CollapsibleEntriesScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/collapsible/CollapsibleEntriesScreen.java new file mode 100644 index 000000000..52c2f2361 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/collapsible/CollapsibleEntriesScreen.java @@ -0,0 +1,316 @@ +package me.shedaniel.rei.impl.client.gui.screen.collapsible; + +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.*; +import com.mojang.math.Matrix4f; +import me.shedaniel.clothconfig2.ClothConfigInitializer; +import me.shedaniel.clothconfig2.api.scroll.ScrollingContainer; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.config.entry.EntryStackProvider; +import me.shedaniel.rei.api.client.gui.widgets.CloseableScissors; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.registry.entry.CollapsibleEntryRegistry; +import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.util.CollectionUtils; +import me.shedaniel.rei.impl.client.config.collapsible.CollapsibleConfigManager; +import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; +import me.shedaniel.rei.impl.client.gui.screen.collapsible.selection.CustomCollapsibleEntrySelectionScreen; +import me.shedaniel.rei.impl.client.gui.screen.generic.OptionEntriesScreen; +import me.shedaniel.rei.impl.client.gui.widget.DynamicErrorFreeEntryListWidget; +import me.shedaniel.rei.impl.common.entry.type.EntryRegistryImpl; +import me.shedaniel.rei.impl.common.entry.type.collapsed.CollapsibleEntryRegistryImpl; +import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; + +import java.util.*; +import java.util.function.Consumer; + +public class CollapsibleEntriesScreen extends Screen { + private final Screen parent; + private final CollapsibleConfigManager.CollapsibleConfigObject configObject; + private final Runnable editedSink; + private final List widgets = new ArrayList<>(); + private ListWidget listWidget; + private boolean dirty = true; + + public CollapsibleEntriesScreen(Screen parent, CollapsibleConfigManager.CollapsibleConfigObject configObject, Runnable editedSink) { + super(new TranslatableComponent("text.rei.collapsible.entries")); + this.parent = parent; + this.configObject = configObject; + this.editedSink = editedSink; + this.prepareWidgets(configObject); + } + + public void prepareWidgets(CollapsibleConfigManager.CollapsibleConfigObject configObject) { + this.widgets.clear(); + + for (CollapsibleConfigManager.CustomGroup customEntry : configObject.customGroups) { + this.widgets.add(new CollapsibleEntryWidget(true, customEntry.id, new TextComponent(customEntry.name), + CollectionUtils.filterAndMap(customEntry.stacks, EntryStackProvider::isValid, EntryStackProvider::provide), configObject, editedSink, + () -> { + this.prepareWidgets(configObject); + this.dirty = true; + this.editedSink.run(); + })); + } + + CollapsibleEntryRegistryImpl collapsibleRegistry = (CollapsibleEntryRegistryImpl) CollapsibleEntryRegistry.getInstance(); + Multimap> entries = Multimaps.newListMultimap(new HashMap<>(), ArrayList::new); + for (HashedEntryStackWrapper wrapper : ((EntryRegistryImpl) EntryRegistry.getInstance()).getFilteredList().getList()) { + for (CollapsibleEntryRegistryImpl.Entry entry : collapsibleRegistry.getEntries()) { + if (entry.getMatcher().matches(wrapper.unwrap(), wrapper.hashExact())) { + entries.put(entry.getId(), wrapper.unwrap()); + } + } + } + + for (CollapsibleEntryRegistryImpl.Entry entry : collapsibleRegistry.getEntries()) { + this.widgets.add(new CollapsibleEntryWidget(false, entry.getId(), entry.getName(), entries.get(entry.getId()), configObject, editedSink, + () -> { + this.prepareWidgets(configObject); + this.dirty = true; + this.editedSink.run(); + })); + } + } + + @Override + public void init() { + super.init(); + { + Component backText = new TextComponent("↩ ").append(new TranslatableComponent("gui.back")); + addRenderableWidget(new Button(4, 4, font.width(backText) + 10, 20, backText, + button -> this.onClose())); + } + { + Component addText = new TextComponent(" + "); + addRenderableWidget(new Button(width - 4 - 20, 4, 20, 20, addText, $ -> { + setupCustom(new ResourceLocation("custom:" + UUID.randomUUID()), "", new ArrayList<>(), this.configObject, () -> { + this.prepareWidgets(configObject); + this.dirty = true; + this.editedSink.run(); + }); + })); + } + + this.listWidget = new ListWidget(width, height, 30); + ((List) this.children()).add(this.listWidget); + this.dirty = true; + } + + public static void setupCustom(ResourceLocation id, String name, List> stacks, CollapsibleConfigManager.CollapsibleConfigObject configObject, Runnable markDirty) { + Minecraft.getInstance().setScreen(new OptionEntriesScreen(new TranslatableComponent("text.rei.collapsible.entries.custom.title"), Minecraft.getInstance().screen) { + private TextFieldListEntry entry; + + @Override + public void addEntries(Consumer entryConsumer) { + addEmpty(entryConsumer, 10); + addText(entryConsumer, new TranslatableComponent("text.rei.collapsible.entries.custom.id").withStyle(ChatFormatting.GRAY) + .append(new TextComponent(" " + id).withStyle(ChatFormatting.DARK_GRAY))); + addEmpty(entryConsumer, 10); + addText(entryConsumer, new TranslatableComponent("text.rei.collapsible.entries.custom.name").withStyle(ChatFormatting.GRAY)); + entryConsumer.accept(this.entry = new TextFieldListEntry(width - 36, widget -> { + widget.setMaxLength(40); + if (this.entry != null) widget.setValue(this.entry.getWidget().getValue()); + else widget.setValue(name); + })); + addEmpty(entryConsumer, 10); + entryConsumer.accept(new ButtonListEntry(width - 36, $ -> new TranslatableComponent("text.rei.collapsible.entries.custom.select"), ($, button) -> { + CustomCollapsibleEntrySelectionScreen screen = new CustomCollapsibleEntrySelectionScreen(stacks); + screen.parent = this.minecraft.screen; + this.minecraft.setScreen(screen); + })); + } + + @Override + public void save() { + configObject.customGroups.removeIf(customGroup -> customGroup.id.equals(id)); + configObject.customGroups.add(new CollapsibleConfigManager.CustomGroup(id, this.entry.getWidget().getValue(), + CollectionUtils.map(stacks, EntryStackProvider::ofStack))); + markDirty.run(); + } + }); + } + + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + if (this.dirty) { + this.listWidget.clear(); + + for (CollapsibleEntryWidget widget : this.widgets) { + this.listWidget.add(widget); + } + + this.dirty = false; + } + +