diff options
Diffstat (limited to 'src')
10 files changed, 167 insertions, 53 deletions
diff --git a/src/main/java/dev/isxander/yacl/api/Option.java b/src/main/java/dev/isxander/yacl/api/Option.java index ced0772..10f2d10 100644 --- a/src/main/java/dev/isxander/yacl/api/Option.java +++ b/src/main/java/dev/isxander/yacl/api/Option.java @@ -1,5 +1,6 @@ package dev.isxander.yacl.api; +import com.google.common.collect.ImmutableSet; import dev.isxander.yacl.impl.OptionImpl; import net.minecraft.text.MutableText; import net.minecraft.text.Text; @@ -7,8 +8,7 @@ import net.minecraft.util.Formatting; import org.apache.commons.lang3.Validate; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -47,6 +47,11 @@ public interface Option<T> { @NotNull Class<T> typeClass(); /** + * Tasks that needs to be executed upon applying changes. + */ + @NotNull ImmutableSet<OptionFlag> flags(); + + /** * Checks if the pending value is not equal to the current set value */ boolean changed(); @@ -54,6 +59,7 @@ public interface Option<T> { /** * If true, modifying this option recommends a restart. */ + @Deprecated boolean requiresRestart(); /** @@ -69,8 +75,10 @@ public interface Option<T> { /** * Applies the pending value to the bound value. * Cannot be undone. + * + * @return if there were changes to apply {@link Option#changed()} */ - void applyValue(); + boolean applyValue(); /** * Sets the pending value to the bound value. @@ -101,7 +109,7 @@ public interface Option<T> { private Binding<T> binding; - private boolean requiresRestart; + private final Set<OptionFlag> flags = new HashSet<>(); private final Class<T> typeClass; @@ -180,11 +188,38 @@ public interface Option<T> { } /** + * Adds a flag to the option. + * Upon applying changes, all flags are executed. + * {@link Option#flags()} + */ + public Builder<T> flag(@NotNull OptionFlag... flag) { + Validate.notNull(flag, "`flag` must not be null"); + + this.flags.addAll(Arrays.asList(flag)); + return this; + } + + /** + * Adds a flag to the option. + * Upon applying changes, all flags are executed. + * {@link Option#flags()} + */ + public Builder<T> flags(@NotNull Collection<OptionFlag> flags) { + Validate.notNull(flags, "`flags` must not be null"); + + this.flags.addAll(flags); + return this; + } + + /** * Dictates whether the option should require a restart. * {@link Option#requiresRestart()} */ + @Deprecated public Builder<T> requiresRestart(boolean requiresRestart) { - this.requiresRestart = requiresRestart; + if (requiresRestart) flag(OptionFlag.GAME_RESTART); + else flags.remove(OptionFlag.GAME_RESTART); + return this; } @@ -201,7 +236,7 @@ public interface Option<T> { concatenatedTooltip.append(line); } - return new OptionImpl<>(name, concatenatedTooltip, controlGetter, binding, requiresRestart, typeClass); + return new OptionImpl<>(name, concatenatedTooltip, controlGetter, binding, ImmutableSet.copyOf(flags), typeClass); } } } diff --git a/src/main/java/dev/isxander/yacl/api/OptionFlag.java b/src/main/java/dev/isxander/yacl/api/OptionFlag.java new file mode 100644 index 0000000..203a674 --- /dev/null +++ b/src/main/java/dev/isxander/yacl/api/OptionFlag.java @@ -0,0 +1,27 @@ +package dev.isxander.yacl.api; + +import dev.isxander.yacl.gui.RequireRestartScreen; +import net.minecraft.client.MinecraftClient; + +import java.util.function.Consumer; + +/** + * Code that is executed upon certain options being applied. + * Each flag is executed only once per save, no matter the amount of options with the flag. + */ +@FunctionalInterface +public interface OptionFlag extends Consumer<MinecraftClient> { + /** + * Warns the user that a game restart is required for the changes to take effect + */ + OptionFlag GAME_RESTART = client -> client.setScreen(new RequireRestartScreen(client.currentScreen)); + + /** + * Reloads chunks upon applying (F3+A) + */ + OptionFlag RELOAD_CHUNKS = client -> client.worldRenderer.reload(); + + OptionFlag WORLD_RENDER_UPDATE = client -> client.worldRenderer.scheduleTerrainUpdate(); + + OptionFlag ASSET_RELOAD = MinecraftClient::reloadResourcesConcurrently; +} diff --git a/src/main/java/dev/isxander/yacl/gui/CategoryWidget.java b/src/main/java/dev/isxander/yacl/gui/CategoryWidget.java index 65daef3..31dacb9 100644 --- a/src/main/java/dev/isxander/yacl/gui/CategoryWidget.java +++ b/src/main/java/dev/isxander/yacl/gui/CategoryWidget.java @@ -7,7 +7,10 @@ public class CategoryWidget extends TooltipButtonWidget { private final int categoryIndex; public CategoryWidget(YACLScreen screen, ConfigCategory category, int categoryIndex, int x, int y, int width, int height) { - super(screen, x, y, width, height, category.name(), category.tooltip(), btn -> screen.changeCategory(screen.categoryButtons.indexOf(btn))); + super(screen, x, y, width, height, category.name(), category.tooltip(), btn -> { + screen.searchFieldWidget.setText(""); + screen.changeCategory(screen.categoryButtons.indexOf(btn)); + }); this.categoryIndex = categoryIndex; } diff --git a/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java b/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java index ca6fc5d..5b80e51 100644 --- a/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java +++ b/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java @@ -30,34 +30,51 @@ import java.util.function.Supplier; public class OptionListWidget extends ElementListWidget<OptionListWidget.Entry> { private final YACLScreen yaclScreen; - public OptionListWidget(ConfigCategory category, YACLScreen screen, MinecraftClient client, int width, int height) { + public OptionListWidget(YACLScreen screen, MinecraftClient client, int width, int height) { super(client, width / 3 * 2, height, 0, height, 22); this.yaclScreen = screen; left = width - this.width; right = width; - for (OptionGroup group : category.groups()) { - Supplier<Boolean> viewableSupplier; - GroupSeparatorEntry groupSeparatorEntry = null; - if (!group.isRoot()) { - groupSeparatorEntry = new GroupSeparatorEntry(group, screen); - viewableSupplier = groupSeparatorEntry::isExpanded; - addEntry(groupSeparatorEntry); - } else { - viewableSupplier = () -> true; - } + refreshOptions(); + } - List<OptionEntry> optionEntries = new ArrayList<>(); - for (Option<?> option : group.options()) { - OptionEntry entry = new OptionEntry(option.controller().provideWidget(screen, Dimension.ofInt(getRowLeft(), 0, getRowWidth(), 20)), viewableSupplier); - addEntry(entry); - optionEntries.add(entry); - } + public void refreshOptions() { + super.children().clear(); + + List<ConfigCategory> categories = new ArrayList<>(); + if (yaclScreen.currentCategoryIdx == -1) { + categories.addAll(yaclScreen.config.categories()); + } else { + categories.add(yaclScreen.config.categories().get(yaclScreen.currentCategoryIdx)); + } + + for (ConfigCategory category : categories) { + for (OptionGroup group : category.groups()) { + Supplier<Boolean> viewableSupplier; + GroupSeparatorEntry groupSeparatorEntry = null; + if (!group.isRoot()) { + groupSeparatorEntry = new GroupSeparatorEntry(group, yaclScreen); + viewableSupplier = groupSeparatorEntry::isExpanded; + addEntry(groupSeparatorEntry); + } else { + viewableSupplier = () -> true; + } - if (groupSeparatorEntry != null) { - groupSeparatorEntry.setOptionEntries(optionEntries); + List<OptionEntry> optionEntries = new ArrayList<>(); + for (Option<?> option : group.options()) { + OptionEntry entry = new OptionEntry(option.controller().provideWidget(yaclScreen, Dimension.ofInt(getRowLeft(), 0, getRowWidth(), 20)), viewableSupplier); + addEntry(entry); + optionEntries.add(entry); + } + + if (groupSeparatorEntry != null) { + groupSeparatorEntry.setOptionEntries(optionEntries); + } } } + + setScrollAmount(0); } /* @@ -126,6 +143,11 @@ public class OptionListWidget extends ElementListWidget<OptionListWidget.Entry> /* END cloth config code */ @Override + public int getRowWidth() { + return Math.min(396, (int)(width / 1.3f)); + } + + @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { for (Entry child : children()) { if (child != getEntryAtPosition(mouseX, mouseY) && child instanceof OptionEntry optionEntry) @@ -168,7 +190,7 @@ public class OptionListWidget extends ElementListWidget<OptionListWidget.Entry> @Override protected int getScrollbarPositionX() { - return left + super.getScrollbarPositionX(); + return left + width - (int)(width * 0.05f); } @Override diff --git a/src/main/java/dev/isxander/yacl/gui/RequireRestartScreen.java b/src/main/java/dev/isxander/yacl/gui/RequireRestartScreen.java index 807292e..3c46738 100644 --- a/src/main/java/dev/isxander/yacl/gui/RequireRestartScreen.java +++ b/src/main/java/dev/isxander/yacl/gui/RequireRestartScreen.java @@ -7,7 +7,7 @@ import net.minecraft.text.Text; import net.minecraft.util.Formatting; public class RequireRestartScreen extends ConfirmScreen { - protected RequireRestartScreen(Screen parent) { + public RequireRestartScreen(Screen parent) { super(option -> { if (option) MinecraftClient.getInstance().scheduleStop(); else MinecraftClient.getInstance().setScreen(parent); diff --git a/src/main/java/dev/isxander/yacl/gui/SearchFieldWidget.java b/src/main/java/dev/isxander/yacl/gui/SearchFieldWidget.java index fedebdb..68f050f 100644 --- a/src/main/java/dev/isxander/yacl/gui/SearchFieldWidget.java +++ b/src/main/java/dev/isxander/yacl/gui/SearchFieldWidget.java @@ -37,6 +37,13 @@ public class SearchFieldWidget extends TextFieldWidget { super.write(text); } + @Override + public void eraseCharacters(int characterOffset) { + yaclScreen.optionList.setScrollAmount(0); + + super.eraseCharacters(characterOffset); + } + public Text getEmptyText() { return emptyText; } diff --git a/src/main/java/dev/isxander/yacl/gui/YACLScreen.java b/src/main/java/dev/isxander/yacl/gui/YACLScreen.java index adb6007..9b73d8d 100644 --- a/src/main/java/dev/isxander/yacl/gui/YACLScreen.java +++ b/src/main/java/dev/isxander/yacl/gui/YACLScreen.java @@ -2,6 +2,7 @@ package dev.isxander.yacl.gui; import dev.isxander.yacl.api.ConfigCategory; import dev.isxander.yacl.api.Option; +import dev.isxander.yacl.api.OptionFlag; import dev.isxander.yacl.api.YetAnotherConfigLib; import dev.isxander.yacl.api.utils.Dimension; import dev.isxander.yacl.api.utils.OptionUtils; @@ -13,7 +14,9 @@ import net.minecraft.text.Text; import net.minecraft.util.Formatting; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; public class YACLScreen extends Screen { @@ -65,18 +68,16 @@ public class YACLScreen extends Screen { categoryDim.move(0, 21); } - searchFieldWidget = new SearchFieldWidget(this, textRenderer, width / 3 / 2 - paddedWidth / 2 + 1, height - 71, paddedWidth - 2, 18, Text.translatable("yacl.gui.search"), Text.translatable("yacl.gui.search")); - Dimension<Integer> actionDim = Dimension.ofInt(width / 3 / 2, height - padding - 20, paddedWidth, 20); finishedSaveButton = new TooltipButtonWidget(this, actionDim.x() - actionDim.width() / 2, actionDim.y(), actionDim.width(), actionDim.height(), Text.empty(), Text.empty(), (btn) -> { saveButtonMessage = null; if (pendingChanges()) { - AtomicBoolean requiresRestart = new AtomicBoolean(false); + Set<OptionFlag> flags = new HashSet<>(); OptionUtils.forEachOptions(config, option -> { - if (option.requiresRestart() && option.changed()) - requiresRestart.set(true); - option.applyValue(); + if (option.applyValue()) { + flags.addAll(option.flags()); + } }); OptionUtils.forEachOptions(config, option -> { if (option.changed()) { @@ -85,9 +86,8 @@ public class YACLScreen extends Screen { } }); config.saveFunction().run(); - if (requiresRestart.get()) { - client.setScreen(new RequireRestartScreen(this)); - } + + flags.forEach(flag -> flag.accept(client)); } else close(); }); actionDim.expand(-actionDim.width() / 2 - 2, 0).move(-actionDim.width() / 2 - 2, -22); @@ -105,14 +105,15 @@ public class YACLScreen extends Screen { OptionUtils.forEachOptions(config, Option::forgetPendingValue); }); + searchFieldWidget = new SearchFieldWidget(this, textRenderer, width / 3 / 2 - paddedWidth / 2 + 1, undoButton.y - 22, paddedWidth - 2, 18, Text.translatable("yacl.gui.search"), Text.translatable("yacl.gui.search")); + updateActionAvailability(); addDrawableChild(searchFieldWidget); addDrawableChild(cancelResetButton); addDrawableChild(undoButton); addDrawableChild(finishedSaveButton); - ConfigCategory currentCategory = config.categories().get(currentCategoryIdx); - optionList = new OptionListWidget(currentCategory, this, client, width, height); + optionList = new OptionListWidget(this, client, width, height); addSelectableChild(optionList); config.initConsumer().accept(this); @@ -154,7 +155,7 @@ public class YACLScreen extends Screen { public void changeCategory(int idx) { currentCategoryIdx = idx; - refreshGUI(); + optionList.refreshOptions(); } private void updateActionAvailability() { @@ -170,6 +171,12 @@ public class YACLScreen extends Screen { @Override public void tick() { searchFieldWidget.tick(); + if (!searchFieldWidget.getText().isEmpty() && currentCategoryIdx != -1) { + changeCategory(-1); + } + if (searchFieldWidget.getText().isEmpty() && currentCategoryIdx == -1) { + changeCategory(0); + } updateActionAvailability(); @@ -207,10 +214,6 @@ public class YACLScreen extends Screen { return pendingChanges.get(); } - private void refreshGUI() { - init(client, width, height); - } - @Override public boolean shouldCloseOnEsc() { if (pendingChanges()) { diff --git a/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java b/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java index b531dec..2cdc8a6 100644 --- a/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java +++ b/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java @@ -1,8 +1,10 @@ package dev.isxander.yacl.impl; +import com.google.common.collect.ImmutableSet; import dev.isxander.yacl.api.Binding; import dev.isxander.yacl.api.ButtonOption; import dev.isxander.yacl.api.Controller; +import dev.isxander.yacl.api.OptionFlag; import dev.isxander.yacl.gui.YACLScreen; import net.minecraft.text.Text; import org.jetbrains.annotations.ApiStatus; @@ -64,6 +66,11 @@ public class ButtonOptionImpl implements ButtonOption { } @Override + public @NotNull ImmutableSet<OptionFlag> flags() { + return ImmutableSet.of(); + } + + @Override public boolean requiresRestart() { return false; } @@ -84,8 +91,8 @@ public class ButtonOptionImpl implements ButtonOption { } @Override - public void applyValue() { - + public boolean applyValue() { + return false; } @Override diff --git a/src/main/java/dev/isxander/yacl/impl/OptionImpl.java b/src/main/java/dev/isxander/yacl/impl/OptionImpl.java index d63f20b..445420e 100644 --- a/src/main/java/dev/isxander/yacl/impl/OptionImpl.java +++ b/src/main/java/dev/isxander/yacl/impl/OptionImpl.java @@ -1,8 +1,10 @@ package dev.isxander.yacl.impl; +import com.google.common.collect.ImmutableSet; import dev.isxander.yacl.api.Binding; import dev.isxander.yacl.api.Controller; import dev.isxander.yacl.api.Option; +import dev.isxander.yacl.api.OptionFlag; import net.minecraft.text.Text; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -16,7 +18,8 @@ public class OptionImpl<T> implements Option<T> { private final Text tooltip; private final Controller<T> controller; private final Binding<T> binding; - private final boolean requiresRestart; + + private final ImmutableSet<OptionFlag> flags; private final Class<T> typeClass; @@ -27,14 +30,14 @@ public class OptionImpl<T> implements Option<T> { @Nullable Text tooltip, @NotNull Function<Option<T>, Controller<T>> controlGetter, @NotNull Binding<T> binding, - boolean requiresRestart, + ImmutableSet<OptionFlag> flags, @NotNull Class<T> typeClass ) { this.name = name; this.tooltip = tooltip; this.controller = controlGetter.apply(this); this.binding = binding; - this.requiresRestart = requiresRestart; + this.flags = flags; this.typeClass = typeClass; this.pendingValue = binding().getValue(); } @@ -65,8 +68,13 @@ public class OptionImpl<T> implements Option<T> { } @Override + public @NotNull ImmutableSet<OptionFlag> flags() { + return flags; + } + + @Override public boolean requiresRestart() { - return requiresRestart; + return flags.contains(OptionFlag.GAME_RESTART); } @Override @@ -85,10 +93,12 @@ public class OptionImpl<T> implements Option<T> { } @Override - public void applyValue() { + public boolean applyValue() { if (changed()) { binding().setValue(pendingValue); + return true; } + return false; } @Override diff --git a/src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java b/src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java index b105ef4..d1ef4d0 100644 --- a/src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java +++ b/src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java @@ -50,7 +50,7 @@ public class ModMenuIntegration implements ModMenuApi { .name(Text.of("Control Examples")) .tooltip(Text.of("Example Category Description")) .group(OptionGroup.createBuilder() - .name(Text.of("Boolean Controllers but it has a super long name that needs to wrap")) + .name(Text.of("Boolean Controllers")) .tooltip(Text.of("Test!")) .collapsed(true) .option(Option.createBuilder(boolean.class) @@ -62,7 +62,7 @@ public class ModMenuIntegration implements ModMenuApi { (value) -> TestSettings.booleanToggle = value ) .controller(BooleanController::new) - .requiresRestart(true) + .flag(OptionFlag.GAME_RESTART) .build()) .option(Option.createBuilder(boolean.class) .name(Text.of("Custom Boolean Toggle")) |