diff options
| author | shedaniel <daniel@shedaniel.me> | 2023-11-07 17:38:48 +0800 |
|---|---|---|
| committer | shedaniel <daniel@shedaniel.me> | 2024-04-16 00:38:18 +0900 |
| commit | d8008dc4e83afc5428814e95c7c2a456c608e053 (patch) | |
| tree | e322284c983bbf8f5ed5e758e3fe94e694441410 /runtime | |
| parent | 54ffa6bc8868e8e5e1337806d0a681b316096cde (diff) | |
| download | RoughlyEnoughItems-d8008dc4e83afc5428814e95c7c2a456c608e053.tar.gz RoughlyEnoughItems-d8008dc4e83afc5428814e95c7c2a456c608e053.tar.bz2 RoughlyEnoughItems-d8008dc4e83afc5428814e95c7c2a456c608e053.zip | |
Implement Config Search
Diffstat (limited to 'runtime')
16 files changed, 716 insertions, 57 deletions
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 768e8355a..cfb1b6c64 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 @@ -31,6 +31,7 @@ import me.shedaniel.clothconfig2.api.Modifier; import me.shedaniel.clothconfig2.api.ModifierKeyCode; import me.shedaniel.math.Point; import me.shedaniel.math.Rectangle; +import me.shedaniel.math.impl.PointHelper; import me.shedaniel.rei.api.client.REIRuntime; import me.shedaniel.rei.api.client.gui.widgets.Widget; import me.shedaniel.rei.api.client.gui.widgets.Widgets; @@ -43,8 +44,11 @@ import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; 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.components.ConfigSearchListWidget; import me.shedaniel.rei.impl.client.gui.config.options.*; import me.shedaniel.rei.impl.client.gui.modules.Menu; +import me.shedaniel.rei.impl.client.gui.widget.HoleWidget; +import me.shedaniel.rei.impl.client.gui.widget.basewidgets.TextFieldWidget; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.events.GuiEventListener; @@ -59,15 +63,17 @@ import java.util.List; import java.util.Map; import java.util.function.BiConsumer; +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 class REIConfigScreen extends Screen implements ConfigAccess { private final Screen parent; private final List<OptionCategory> categories; private final List<Widget> widgets = new ArrayList<>(); - private final Map<CompositeOption<?>, ?> defaultOptions = new HashMap<>(); - private final Map<CompositeOption<?>, ?> options = new HashMap<>(); + private final Map<String, ?> defaultOptions = new HashMap<>(); + private final Map<String, ?> options = new HashMap<>(); private OptionCategory activeCategory; + private boolean searching; @Nullable private Menu menu; @Nullable @@ -91,8 +97,8 @@ public class REIConfigScreen extends Screen implements ConfigAccess { for (OptionCategory category : this.categories) { for (OptionGroup group : category.getGroups()) { for (CompositeOption<?> option : group.getOptions()) { - ((Map<CompositeOption<?>, Object>) this.defaultOptions).put(option, option.getBind().apply(defaultConfig)); - ((Map<CompositeOption<?>, Object>) this.options).put(option, option.getBind().apply(config)); + ((Map<String, Object>) this.defaultOptions).put(option.getId(), option.getBind().apply(defaultConfig)); + ((Map<String, Object>) this.options).put(option.getId(), option.getBind().apply(config)); } } } @@ -107,7 +113,7 @@ public class REIConfigScreen extends Screen implements ConfigAccess { for (OptionGroup group : category.getGroups()) { group.getOptions().replaceAll(option -> { if (option.isRequiresLevel()) { - return new CompositeOption<>(option.getName(), option.getDescription(), i -> 0, (i, v) -> new Object()) + return new CompositeOption<>(option.getId(), option.getName(), option.getDescription(), i -> 0, (i, v) -> new Object()) .entry(value -> translatable("config.rei.texts.requires_level").withStyle(ChatFormatting.RED)) .defaultValue(() -> 1); } else { @@ -124,27 +130,53 @@ public class REIConfigScreen extends Screen implements ConfigAccess { this.widgets.clear(); this.widgets.add(Widgets.createLabel(new Point(width / 2, 12), this.title)); int sideWidth = (int) Math.round(width / 4.2); - boolean singlePane = width - 20 - sideWidth <= 330; - int singleSideWidth = 32 + 6 + 4; - Mutable<Widget> list = new MutableObject<>(createEntriesList(singlePane, singleSideWidth, sideWidth)); - IntValue selectedCategory = new IntValue() { - @Override - public void accept(int index) { - REIConfigScreen.this.activeCategory = categories.get(index); - list.setValue(createEntriesList(singlePane, singleSideWidth, sideWidth)); - } - - @Override - public int getAsInt() { - return categories.indexOf(activeCategory); - } - }; - if (!singlePane) { - this.widgets.add(ConfigCategoriesListWidget.create(new Rectangle(8, 32, sideWidth, height - 32 - 32), categories, selectedCategory)); + if (this.searching) { + this.widgets.add(Widgets.createButton(new Rectangle(8, 32, sideWidth, 20), literal("↩ ").append(translatable("gui.back"))) + .onClick(button -> setSearching(false))); + this.widgets.add(HoleWidget.createBackground(new Rectangle(8 + sideWidth + 4, 32, width - 16 - sideWidth - 4, 20), () -> 0, 32)); + TextFieldWidget textField = new TextFieldWidget(new Rectangle(8 + sideWidth + 4 + 6, 32 + 6, width - 16 - sideWidth - 4 - 10, 12)) { + @Override + protected void renderSuggestion(PoseStack matrices, int x, int y) { + int color; + if (containsMouse(PointHelper.ofMouse()) || isFocused()) { + color = 0xddeaeaea; + } else { + color = -6250336; + } + this.font.drawShadow(matrices, this.font.plainSubstrByWidth(this.getSuggestion(), this.getWidth()), x, y, color); + } + }; + textField.setHasBorder(false); + textField.setMaxLength(9000); + this.widgets.add(textField); + this.widgets.add(Widgets.createDrawableWidget((helper, matrices, mouseX, mouseY, delta) -> { + textField.setSuggestion(!textField.isFocused() && textField.getText().isEmpty() ? I18n.get("config.rei.texts.search_options") : null); + })); + this.widgets.add(ConfigSearchListWidget.create(this, this.categories, textField, new Rectangle(8, 32 + 20 + 4, width - 16, height - 32 - (32 + 20 + 4)))); } else { - this.widgets.add(ConfigCategoriesListWidget.createTiny(new Rectangle(8, 32, singleSideWidth - 4, height - 32 - 32), categories, selectedCategory)); + boolean singlePane = width - 20 - sideWidth <= 330; + int singleSideWidth = 32 + 6 + 4; + Mutable<Widget> list = new MutableObject<>(createEntriesList(singlePane, singleSideWidth, sideWidth)); + IntValue selectedCategory = new IntValue() { + @Override + public void accept(int index) { + REIConfigScreen.this.activeCategory = categories.get(index); + list.setValue(createEntriesList(singlePane, singleSideWidth, sideWidth)); + } + + @Override + public int getAsInt() { + return categories.indexOf(activeCategory); + } + }; + if (!singlePane) { + this.widgets.add(ConfigCategoriesListWidget.create(new Rectangle(8, 32, sideWidth, height - 32 - 32), categories, selectedCategory)); + } else { + this.widgets.add(ConfigCategoriesListWidget.createTiny(new Rectangle(8, 32, singleSideWidth - 4, height - 32 - 32), categories, selectedCategory)); + } + this.widgets.add(Widgets.delegate(list::getValue)); } - this.widgets.add(Widgets.delegate(list::getValue)); + this.widgets.add(Widgets.createButton(new Rectangle(width / 2 - 150 - 10, height - 26, 150, 20), translatable("gui.cancel")).onClick(button -> { Minecraft.getInstance().setScreen(this.parent); })); @@ -171,11 +203,11 @@ public class REIConfigScreen extends Screen implements ConfigAccess { return ConfigEntriesListWidget.create(this, new Rectangle(singlePane ? 8 + singleSideWidth : 12 + sideWidth, 32, singlePane ? width - 16 - singleSideWidth : width - 20 - sideWidth, height - 32 - 32), activeCategory.getGroups()); } - public Map<CompositeOption<?>, ?> getDefaultOptions() { + public Map<String, ?> getDefaultOptions() { return defaultOptions; } - public Map<CompositeOption<?>, ?> getOptions() { + public Map<String, ?> getOptions() { return options; } @@ -363,17 +395,17 @@ public class REIConfigScreen extends Screen implements ConfigAccess { @Override public <T> T get(CompositeOption<T> option) { - return (T) getOptions().get(option); + return (T) getOptions().get(option.getId()); } @Override public <T> void set(CompositeOption<T> option, T value) { - ((Map<CompositeOption<?>, Object>) getOptions()).put(option, value); + ((Map<String, Object>) getOptions()).put(option.getId(), value); } @Override public <T> T getDefault(CompositeOption<T> option) { - return (T) getDefaultOptions().get(option); + return (T) getDefaultOptions().get(option.getId()); } @Override @@ -393,4 +425,13 @@ public class REIConfigScreen extends Screen implements ConfigAccess { public CompositeOption<ModifierKeyCode> getFocusedKeycode() { return this.focusedKeycodeOption; } + + public void setSearching(boolean searching) { + this.searching = searching; + this.init(this.minecraft, this.width, this.height); + } + + public boolean isSearching() { + return searching; + } } 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 3ab77951c..072c73a8d 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 @@ -27,30 +27,56 @@ import dev.architectury.utils.value.IntValue; 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.common.util.CollectionUtils; import me.shedaniel.rei.impl.client.gui.config.options.OptionCategory; 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 org.apache.commons.lang3.mutable.Mutable; +import org.apache.commons.lang3.mutable.MutableObject; import java.util.List; public class ConfigCategoriesListWidget { public static Widget create(Rectangle bounds, List<OptionCategory> categories, IntValue selected) { - WidgetWithBounds list = ListWidget.builderOf(RectangleUtils.inset(bounds, 3, 5), categories, - (index, entry) -> ConfigCategoryEntryWidget.create(entry, bounds.width - 6)) + final Mutable<WidgetWithBounds> list = new MutableObject<>(null); + list.setValue(ListWidget.builderOfWidgets(RectangleUtils.inset(bounds, 3, 5), + CollectionUtils.concatUnmodifiable(List.of(ConfigSearchWidget.create(() -> list.getValue() != null && list.getValue().getBounds().height + 6 > bounds.height ? bounds.width - 6 - 6 : bounds.width - 6)), + CollectionUtils.map(categories, entry -> ConfigCategoryEntryWidget.create(entry, bounds.width - 6)))) .gap(3) - .isSelectable((index, entry) -> true) - .selected(selected) - .build(); - return ScrollableViewWidget.create(bounds, list.withPadding(0, 5), true); + .isSelectable((index, entry) -> index != 0) + .selected(new IntValue() { + @Override + public void accept(int value) { + selected.accept(value - 1); + } + + @Override + public int getAsInt() { + return selected.getAsInt() + 1; + } + }) + .build()); + return ScrollableViewWidget.create(bounds, list.getValue().withPadding(0, 5), true); } public static Widget createTiny(Rectangle bounds, List<OptionCategory> categories, IntValue selected) { - WidgetWithBounds list = ListWidget.builderOf(RectangleUtils.inset(bounds, (bounds.width - 6 - 16) / 2, 9), categories, - (index, entry) -> ConfigCategoryEntryWidget.createTiny(entry)) + WidgetWithBounds list = ListWidget.builderOfWidgets(RectangleUtils.inset(bounds, (bounds.width - 6 - 16) / 2, 9), + CollectionUtils.concatUnmodifiable(List.of(ConfigSearchWidget.createTiny()), + CollectionUtils.map(categories, ConfigCategoryEntryWidget::createTiny))) .gap(7) - .isSelectable((index, entry) -> true) - .selected(selected) + .isSelectable((index, entry) -> index != 0) + .selected(new IntValue() { + @Override + public void accept(int value) { + selected.accept(value - 1); + } + + @Override + public int getAsInt() { + return selected.getAsInt() + 1; + } + }) .build(); return ScrollableViewWidget.create(bounds, list.withPadding(0, 9), true); } 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 index b1ecca932..906148b75 100644 --- 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 @@ -37,7 +37,7 @@ import java.util.List; public class ConfigEntriesListWidget { public static Widget create(ConfigAccess access, Rectangle bounds, List<OptionGroup> groups) { WidgetWithBounds list = ListWidget.builderOf(RectangleUtils.inset(bounds, 6, 6), groups, - (index, entry) -> ConfigGroupWidget.create(access, entry, bounds.width - 12 - 6)) + (index, entry) -> ConfigGroupWidget.create(access, entry, bounds.width - 12 - 6, false)) .gap(7) .calculateTotalHeightDynamically(true) .build(); 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 index 063596f1a..e41c233b2 100644 --- 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 @@ -37,6 +37,7 @@ import me.shedaniel.rei.impl.client.gui.config.options.OptionGroup; import me.shedaniel.rei.impl.client.gui.config.options.preview.AccessibilityDisplayPreviewer; import me.shedaniel.rei.impl.client.gui.config.options.preview.InterfacePreviewer; import me.shedaniel.rei.impl.client.gui.config.options.preview.TooltipPreviewer; +import me.shedaniel.rei.impl.client.gui.text.TextTransformations; import net.minecraft.client.gui.GuiComponent; import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.Nullable; @@ -59,13 +60,13 @@ public class ConfigGroupWidget { SPECIAL_GROUPS.put(group, Pair.of(location, constructor)); } - public static WidgetWithBounds create(ConfigAccess access, OptionGroup entry, int width) { - WidgetWithBounds groupTitle = Widgets.createLabel(new Point(0, 3), entry.getGroupName().copy().withStyle(style -> style.withColor(0xFFC0C0C0).withUnderlined(true))) + public static WidgetWithBounds create(ConfigAccess access, OptionGroup entry, int width, boolean applyPreview) { + WidgetWithBounds groupTitle = Widgets.createLabel(new Point(0, 3), TextTransformations.highlightText(entry.getGroupName().copy(), entry.getGroupNameHighlight(), style -> style.withColor(0xFFC0C0C0).withUnderlined(true))) .leftAligned() .withPadding(0, 0, 0, 6); WidgetWithBounds contents; - if (SPECIAL_GROUPS.containsKey(entry)) { + if (applyPreview && SPECIAL_GROUPS.containsKey(entry)) { Pair<PreviewLocation, SpecialGroupConstructor> pair = SPECIAL_GROUPS.get(entry); PreviewLocation location = pair.getLeft(); int halfWidth = width * 6 / 10 - 2; 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 index a449700a0..f2a47a332 100644 --- 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 @@ -38,6 +38,7 @@ import me.shedaniel.rei.api.client.util.MatrixUtils; import me.shedaniel.rei.impl.client.gui.config.ConfigAccess; import me.shedaniel.rei.impl.client.gui.config.options.CompositeOption; import me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils; +import me.shedaniel.rei.impl.client.gui.text.TextTransformations; import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.events.GuiEventListener; @@ -57,17 +58,17 @@ public class ConfigOptionWidget { int[] stableHeight = {12}; int[] height = {12}; Label fieldNameLabel; - widgets.add(fieldNameLabel = Widgets.createLabel(new Point(0, 0), option.getName().copy().withStyle(style -> style.withColor(0xFFC0C0C0))) + widgets.add(fieldNameLabel = Widgets.createLabel(new Point(0, 0), TextTransformations.highlightText(option.getName().copy(), option.getOptionNameHighlight(), style -> style.withColor(0xFFC0C0C0))) .leftAligned()); WidgetWithBounds optionValue = ConfigOptionValueWidget.create(access, option, width - 10 - fieldNameLabel.getBounds().width); widgets.add(Widgets.withTranslate(optionValue, () -> Matrix4f.createTranslateMatrix(width - optionValue.getBounds().width - optionValue.getBounds().x, 0, 0))); widgets.add(new WidgetWithBounds() { final MutableComponent description = Util.make(() -> { - MutableComponent description = option.getDescription().copy().withStyle(style -> style.withColor(0xFF757575)); + MutableComponent description = option.getDescription().copy(); if (description.getString().endsWith(".desc")) { return literal(""); } else { - return description; + return TextTransformations.highlightText(description, option.getOptionDescriptionHighlight(), style -> style.withColor(0xFF757575)); } }); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigSearchListWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigSearchListWidget.java new file mode 100644 index 000000000..034203b88 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigSearchListWidget.java @@ -0,0 +1,127 @@ +package me.shedaniel.rei.impl.client.gui.config.components; + +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.widgets.TextField; +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.ConfigAccess; +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.widget.ListWidget; +import me.shedaniel.rei.impl.client.gui.widget.ScrollableViewWidget; +import me.shedaniel.rei.impl.client.gui.widget.basewidgets.TextFieldWidget; +import me.shedaniel.rei.impl.common.util.RectangleUtils; +import org.apache.commons.lang3.mutable.Mutable; +import org.apache.commons.lang3.mutable.MutableObject; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Consumer; + +public class ConfigSearchListWidget { + public static WidgetWithBounds create(ConfigAccess access, List<OptionCategory> categories, TextField textField, Rectangle bounds) { + Mutable<WidgetWithBounds> list = new MutableObject<>(null); + Consumer<String> responder = string -> { + Collection<ConfigSearchWidget.SearchResult> results = ConfigSearchWidget.matchResult(categories, string); + list.setValue(createList(access, results, string, bounds)); + }; + ((TextFieldWidget) textField).setResponder(responder); + responder.accept(textField.getText()); + + return ScrollableViewWidget.create(bounds, Widgets.delegateWithBounds(list::getValue), true); + } + + private static WidgetWithBounds createList(ConfigAccess access, Collection<ConfigSearchWidget.SearchResult> results, String searchTerm, Rectangle bounds) { + return ListWidget.builderOfWidgets(RectangleUtils.inset(bounds, 6, 6), + collectResultWidgets(access, results, searchTerm, bounds)) + .gap(7) + .calculateTotalHeightDynamically(true) + .build() + .withPadding(0, 5); + } + + private static List<WidgetWithBounds> collectResultWidgets(ConfigAccess access, Collection<ConfigSearchWidget.SearchResult> results, String searchTerm, Rectangle bounds) { + List<ConfigSearchWidget.SearchResult> collapsedResults = new ArrayList<>(); + for (ConfigSearchWidget.SearchResult result : results) { + if (result instanceof ConfigSearchWidget.IndividualResult individualResult) { + int lastMatchGroup = -1; + for (int i = 0; i < collapsedResults.size(); i++) { + ConfigSearchWidget.SearchResult prev = collapsedResults.get(i); + if (prev instanceof ConfigSearchWidget.IndividualResult prevInd && prevInd.group().getGroupName().getString().equals(individualResult.group().getGroupName().getString())) { + lastMatchGroup = i; + } + } + if (lastMatchGroup == -1) { + collapsedResults.add(result); + } else { + collapsedResults.add(lastMatchGroup + 1, result); + } + } else { + collapsedResults.add(result); + } + } + + List<WidgetWithBounds> widgets = new ArrayList<>(); + ConfigSearchWidget.SearchResult last = null; + List<CompositeOption<?>> merge = null; + for (ConfigSearchWidget.SearchResult result : collapsedResults) { + if (last instanceof ConfigSearchWidget.IndividualResult lastInd && result instanceof ConfigSearchWidget.IndividualResult currInd) { + if (lastInd.group().getGroupName().getString().equals(currInd.group().getGroupName().getString())) { + if (merge != null) { + merge.add(((ConfigSearchWidget.IndividualResult) currInd.decompose(searchTerm)).option()); + } else { + merge = new ArrayList<>(List.of(((ConfigSearchWidget.IndividualResult) lastInd.decompose(searchTerm)).option(), + ((ConfigSearchWidget.IndividualResult) currInd.decompose(searchTerm)).option())); + } + + last = result; + continue; + } + } + + if (last != null) { + // Commit last + if (merge != null) { + OptionGroup group = ((ConfigSearchWidget.IndividualResult) last).group().copy(); + group.getOptions().clear(); + group.getOptions().addAll(merge); + merge = null; + widgets.add(createSearchResult(access, group, bounds.width - 12 - 6)); + } else { + widgets.add(createSearchResult(access, last.decompose(searchTerm), bounds.width - 12 - 6)); + } + } + last = result; + } + + if (last != null) { + // Commit last + if (merge != null) { + OptionGroup group = ((ConfigSearchWidget.IndividualResult) last).group().copy(); + group.getOptions().clear(); + group.getOptions().addAll(merge); + widgets.add(createSearchResult(access, group, bounds.width - 12 - 6)); + } else { + widgets.add(createSearchResult(access, last.decompose(searchTerm), bounds.width - 12 - 6)); + } + } + + return widgets; + } + + private static WidgetWithBounds createSearchResult(ConfigAccess access, Object result, int width) { + if (result instanceof OptionCategory category) return Widgets.noOp(); + if (result instanceof OptionGroup group) { + return ConfigGroupWidget.create(access, group, width, false); + } + if (result instanceof ConfigSearchWidget.IndividualResult individualResult) { + OptionGroup group = individualResult.group().copy(); + group.getOptions().clear(); + group.getOptions().add(individualResult.option()); + return ConfigGroupWidget.create(access, group, width, false); + } + return Widgets.noOp(); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigSearchWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigSearchWidget.java new file mode 100644 index 000000000..e0012a575 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigSearchWidget.java @@ -0,0 +1,373 @@ +package me.shedaniel.rei.impl.client.gui.config.components; + +import com.mojang.blaze3d.vertex.PoseStack; +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.REIConfigScreen; +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 net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.resources.ResourceLocation; + +import java.util.*; +import java.util.function.IntSupplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils.translatable; + +public class ConfigSearchWidget { + public static WidgetWithBounds create(IntSupplier width) { + Label label = Widgets.createLabel(new Point(21, 6), translatable("config.rei.texts.search_options")) + .leftAligned(); + Font font = Minecraft.getInstance().font; + Rectangle bounds = new Rectangle(0, 0, label.getBounds().getMaxX(), 7 * 3); + return Widgets.concatWithBounds( + bounds, + new Widget() { + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + boolean hovering = new Rectangle(-1, -1, width.getAsInt() + 2, 21).contains(mouseX, mouseY); + for (Widget widget : List.of(Widgets.createFilledRectangle(new Rectangle(1, 1, width.getAsInt() - 2, 18), hovering ? 0x50FFFFFF : 0x25FFFFFF), + Widgets.createFilledRectangle(new Rectangle(-1, -1, width.getAsInt() + 2, 1), hovering ? 0x90FFFFFF : 0x45FFFFFF), + Widgets.createFilledRectangle(new Rectangle(-1, 20, width.getAsInt() + 2, 1), hovering ? 0x90FFFFFF : 0x45FFFFFF), + Widgets.createFilledRectangle(new Rectangle(-1, 0, 1, 20), hovering ? 0x90FFFFFF : 0x45FFFFFF), + Widgets.createFilledRectangle(new Rectangle(width.getAsInt(), 0, 1, 20), hovering ? 0x90FFFFFF : 0x45FFFFFF))) { + widget.render(poses, mouseX, mouseY, delta); + } + label.setColor(hovering ? 0xFFE1E1E1 : 0xFFC0C0C0); + } + + @Override + public List<? extends GuiEventListener> children() { + return List.of(); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (new Rectangle(-1, -1, width.getAsInt() + 2, 21).contains(mouseX, mouseY)) { + Widgets.produceClickSound(); + ((REIConfigScreen) Minecraft.getInstance().screen).setSearching(true); + return true; + } + + return false; + } + }, + Widgets.withTranslate(label, 0, 0.5, 0), + Widgets.createTexturedWidget(new ResourceLocation("roughlyenoughitems:textures/gui/config/search_options.png"), new Rectangle(3, 3, 16, 16), 0, 0, 1, 1, 1, 1) + + ); + } + + public static WidgetWithBounds createTiny() { + Rectangle bounds = new Rectangle(0, 0, 16, 16); + return Widgets.withTooltip(Widgets.concatWithBounds( + bounds, + new Widget() { + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + boolean hovering = new Rectangle(-1, -1, 18, 18).contains(mouseX, mouseY); + poses.pushPose(); + poses.translate(-0.5, -0.5, 0); + for (Widget widget : List.of(Widgets.createFilledRectangle(new Rectangle(-1, -1, 18, 18), hovering ? 0x50FFFFFF : 0x25FFFFFF), + Widgets.createFilledRectangle(new Rectangle(-3, -3, 22, 1), hovering ? 0x90FFFFFF : 0x45FFFFFF), + Widgets.createFilledRectangle(new Rectangle(-3, 18, 22, 1), hovering ? 0x90FFFFFF : 0x45FFFFFF), + Widgets.createFilledRectangle(new Rectangle(-3, -2, 1, 20), hovering ? 0x90FFFFFF : 0x45FFFFFF), + Widgets.createFilledRectangle(new Rectangle(18, -2, 1, 20), hovering ? 0x90FFFFFF : 0x45FFFFFF))) { + widget.render(poses, mouseX, mouseY, delta); + } + poses.popPose(); + } + + @Override + public List<? extends GuiEventListener> children() { + return List.of(); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (new Rectangle(-1, -1, 18, 18).contains(mouseX, mouseY)) { + Widgets.produceClickSound(); + ((REIConfigScreen) Minecraft.getInstance().screen).setSearchin |
