aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/dev/isxander/yacl/api/Option.java47
-rw-r--r--src/main/java/dev/isxander/yacl/api/OptionFlag.java27
-rw-r--r--src/main/java/dev/isxander/yacl/gui/CategoryWidget.java5
-rw-r--r--src/main/java/dev/isxander/yacl/gui/OptionListWidget.java62
-rw-r--r--src/main/java/dev/isxander/yacl/gui/RequireRestartScreen.java2
-rw-r--r--src/main/java/dev/isxander/yacl/gui/SearchFieldWidget.java7
-rw-r--r--src/main/java/dev/isxander/yacl/gui/YACLScreen.java35
-rw-r--r--src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java11
-rw-r--r--src/main/java/dev/isxander/yacl/impl/OptionImpl.java20
-rw-r--r--src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java4
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"))