Skip to content

Commit

Permalink
Implement Config Search
Browse files Browse the repository at this point in the history
  • Loading branch information
shedaniel committed Nov 7, 2023
1 parent f59af49 commit 3f6f643
Show file tree
Hide file tree
Showing 16 changed files with 719 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
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;
Expand All @@ -43,16 +44,22 @@
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.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.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;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import org.apache.commons.compress.harmony.unpack200.bytecode.forms.WideForm;
import org.apache.commons.lang3.mutable.Mutable;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jetbrains.annotations.Nullable;
Expand All @@ -63,15 +70,17 @@
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
Expand All @@ -95,8 +104,8 @@ public REIConfigScreen(Screen parent, List<OptionCategory> categories) {
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));
}
}
}
Expand All @@ -111,7 +120,7 @@ private void cleanRequiresLevel() {
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 {
Expand All @@ -128,27 +137,53 @@ public void init() {
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);
}));
Expand All @@ -175,11 +210,11 @@ private Widget createEntriesList(boolean singlePane, int singleSideWidth, int si
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;
}

Expand Down Expand Up @@ -367,17 +402,17 @@ public void closeMenu() {

@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
Expand All @@ -397,4 +432,13 @@ public void focusKeycode(CompositeOption<ModifierKeyCode> option) {
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,56 @@
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
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;
Expand All @@ -59,13 +60,13 @@ public static void addPreview(OptionGroup group, PreviewLocation location, Speci
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
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;
Expand All @@ -57,17 +58,17 @@ public static <T> WidgetWithBounds create(ConfigAccess access, CompositeOption<T
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));
}
});

Expand Down
Loading

0 comments on commit 3f6f643

Please sign in to comment.