aboutsummaryrefslogtreecommitdiff
path: root/src/client/java/dev/isxander/yacl
diff options
context:
space:
mode:
authorisXander <xandersmith2008@gmail.com>2022-12-14 18:53:30 +0000
committerisXander <xandersmith2008@gmail.com>2022-12-14 18:53:30 +0000
commit51f4ae2d8e0a6cdcc7d50a037143f48a6132214a (patch)
tree9a48b8faf7c419cf9295e79b80e11e27fe8671b2 /src/client/java/dev/isxander/yacl
parent3d1f7eb6c149c14ef9eea98d2d8caa6768f8c51c (diff)
downloadYetAnotherConfigLib-51f4ae2d8e0a6cdcc7d50a037143f48a6132214a.tar.gz
YetAnotherConfigLib-51f4ae2d8e0a6cdcc7d50a037143f48a6132214a.tar.bz2
YetAnotherConfigLib-51f4ae2d8e0a6cdcc7d50a037143f48a6132214a.zip
lots of minor fixes with lists and abstract builders
Diffstat (limited to 'src/client/java/dev/isxander/yacl')
-rw-r--r--src/client/java/dev/isxander/yacl/api/ButtonOption.java71
-rw-r--r--src/client/java/dev/isxander/yacl/api/ConfigCategory.java86
-rw-r--r--src/client/java/dev/isxander/yacl/api/ListOption.java105
-rw-r--r--src/client/java/dev/isxander/yacl/api/Option.java142
-rw-r--r--src/client/java/dev/isxander/yacl/api/OptionGroup.java67
-rw-r--r--src/client/java/dev/isxander/yacl/api/PlaceholderCategory.java50
-rw-r--r--src/client/java/dev/isxander/yacl/api/YetAnotherConfigLib.java56
-rw-r--r--src/client/java/dev/isxander/yacl/gui/OptionListWidget.java35
-rw-r--r--src/client/java/dev/isxander/yacl/gui/controllers/ListEntryWidget.java3
-rw-r--r--src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java83
-rw-r--r--src/client/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java98
-rw-r--r--src/client/java/dev/isxander/yacl/impl/GenericBindingImpl.java2
-rw-r--r--src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java33
-rw-r--r--src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java130
-rw-r--r--src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java76
-rw-r--r--src/client/java/dev/isxander/yacl/impl/OptionImpl.java156
-rw-r--r--src/client/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java56
-rw-r--r--src/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java83
-rw-r--r--src/client/java/dev/isxander/yacl/mixin/client/SimpleOptionAccessor.java2
19 files changed, 774 insertions, 560 deletions
diff --git a/src/client/java/dev/isxander/yacl/api/ButtonOption.java b/src/client/java/dev/isxander/yacl/api/ButtonOption.java
index 1124a9a..2025840 100644
--- a/src/client/java/dev/isxander/yacl/api/ButtonOption.java
+++ b/src/client/java/dev/isxander/yacl/api/ButtonOption.java
@@ -20,31 +20,16 @@ public interface ButtonOption extends Option<BiConsumer<YACLScreen, ButtonOption
BiConsumer<YACLScreen, ButtonOption> action();
static Builder createBuilder() {
- return new Builder();
+ return new ButtonOptionImpl.BuilderImpl();
}
- class Builder {
- private Text name;
- private final List<Text> tooltipLines = new ArrayList<>();
- private boolean available = true;
- private Function<ButtonOption, Controller<BiConsumer<YACLScreen, ButtonOption>>> controlGetter;
- private BiConsumer<YACLScreen, ButtonOption> action;
-
- private Builder() {
-
- }
-
+ interface Builder {
/**
* Sets the name to be used by the option.
*
* @see Option#name()
*/
- public Builder name(@NotNull Text name) {
- Validate.notNull(name, "`name` cannot be null");
-
- this.name = name;
- return this;
- }
+ Builder name(@NotNull Text name);
/**
* Sets the tooltip to be used by the option.
@@ -53,19 +38,9 @@ public interface ButtonOption extends Option<BiConsumer<YACLScreen, ButtonOption
*
* @param tooltips text lines - merged with a new-line on {@link Option.Builder#build()}.
*/
- public Builder tooltip(@NotNull Text... tooltips) {
- Validate.notNull(tooltips, "`tooltips` cannot be empty");
-
- tooltipLines.addAll(List.of(tooltips));
- return this;
- }
-
- public Builder action(@NotNull BiConsumer<YACLScreen, ButtonOption> action) {
- Validate.notNull(action, "`action` cannot be null");
+ Builder tooltip(@NotNull Text... tooltips);
- this.action = action;
- return this;
- }
+ Builder action(@NotNull BiConsumer<YACLScreen, ButtonOption> action);
/**
* Action to be executed upon button press
@@ -73,22 +48,14 @@ public interface ButtonOption extends Option<BiConsumer<YACLScreen, ButtonOption
* @see ButtonOption#action()
*/
@Deprecated
- public Builder action(@NotNull Consumer<YACLScreen> action) {
- Validate.notNull(action, "`action` cannot be null");
-
- this.action = (screen, button) -> action.accept(screen);
- return this;
- }
+ Builder action(@NotNull Consumer<YACLScreen> action);
/**
* Sets if the option can be configured
*
* @see Option#available()
*/
- public Builder available(boolean available) {
- this.available = available;
- return this;
- }
+ Builder available(boolean available);
/**
* Sets the controller for the option.
@@ -96,28 +63,8 @@ public interface ButtonOption extends Option<BiConsumer<YACLScreen, ButtonOption
*
* @see dev.isxander.yacl.gui.controllers
*/
- public Builder controller(@NotNull Function<ButtonOption, Controller<BiConsumer<YACLScreen, ButtonOption>>> control) {
- Validate.notNull(control, "`control` cannot be null");
-
- this.controlGetter = control;
- return this;
- }
-
- public ButtonOption build() {
- Validate.notNull(name, "`name` must not be null when building `Option`");
- Validate.notNull(controlGetter, "`control` must not be null when building `Option`");
- Validate.notNull(action, "`action` must not be null when building `Option`");
-
- MutableText concatenatedTooltip = Text.empty();
- boolean first = true;
- for (Text line : tooltipLines) {
- if (!first) concatenatedTooltip.append("\n");
- first = false;
-
- concatenatedTooltip.append(line);
- }
+ Builder controller(@NotNull Function<ButtonOption, Controller<BiConsumer<YACLScreen, ButtonOption>>> control);
- return new ButtonOptionImpl(name, concatenatedTooltip, action, available, controlGetter);
- }
+ ButtonOption build();
}
}
diff --git a/src/client/java/dev/isxander/yacl/api/ConfigCategory.java b/src/client/java/dev/isxander/yacl/api/ConfigCategory.java
index 19c7f72..eecb9cb 100644
--- a/src/client/java/dev/isxander/yacl/api/ConfigCategory.java
+++ b/src/client/java/dev/isxander/yacl/api/ConfigCategory.java
@@ -39,32 +39,16 @@ public interface ConfigCategory {
* Creates a builder to construct a {@link ConfigCategory}
*/
static Builder createBuilder() {
- return new Builder();
+ return new ConfigCategoryImpl.BuilderImpl();
}
- class Builder {
- private Text name;
-
- private final List<Option<?>> rootOptions = new ArrayList<>();
- private final List<OptionGroup> groups = new ArrayList<>();
-
- private final List<Text> tooltipLines = new ArrayList<>();
-
- private Builder() {
-
- }
-
+ interface Builder {
/**
* Sets name of the category
*
* @see ConfigCategory#name()
*/
- public Builder name(@NotNull Text name) {
- Validate.notNull(name, "`name` cannot be null");
-
- this.name = name;
- return this;
- }
+ Builder name(@NotNull Text name);
/**
* Adds an option to the root group of the category.
@@ -74,17 +58,7 @@ public interface ConfigCategory {
* @see ConfigCategory#groups()
* @see OptionGroup#isRoot()
*/
- public Builder option(@NotNull Option<?> option) {
- Validate.notNull(option, "`option` must not be null");
-
- if (option instanceof ListOption<?> listOption) {
- YACLConstants.LOGGER.warn("Adding list option as an option is not supported! Rerouting to group!");
- return group(listOption);
- }
-
- this.rootOptions.add(option);
- return this;
- }
+ Builder option(@NotNull Option<?> option);
/**
* Adds multiple options to the root group of the category.
@@ -94,39 +68,21 @@ public interface ConfigCategory {
* @see ConfigCategory#groups()
* @see OptionGroup#isRoot()
*/
- public Builder options(@NotNull Collection<Option<?>> options) {
- Validate.notNull(options, "`options` must not be null");
-
- if (options.stream().anyMatch(ListOption.class::isInstance))
- throw new UnsupportedOperationException("List options must not be added as an option but a group!");
-
- this.rootOptions.addAll(options);
- return this;
- }
+ Builder options(@NotNull Collection<Option<?>> options);
/**
* Adds an option group.
* To add an option to the root group, use {@link Builder#option(Option)}
* To construct a group, use {@link OptionGroup#createBuilder()}
*/
- public Builder group(@NotNull OptionGroup group) {
- Validate.notNull(group, "`group` must not be null");
-
- this.groups.add(group);
- return this;
- }
+ Builder group(@NotNull OptionGroup group);
/**
* Adds multiple option groups.
* To add multiple options to the root group, use {@link Builder#options(Collection)}
* To construct a group, use {@link OptionGroup#createBuilder()}
*/
- public Builder groups(@NotNull Collection<OptionGroup> groups) {
- Validate.notEmpty(groups, "`groups` must not be empty");
-
- this.groups.addAll(groups);
- return this;
- }
+ Builder groups(@NotNull Collection<OptionGroup> groups);
/**
* Sets the tooltip to be used by the category.
@@ -135,32 +91,8 @@ public interface ConfigCategory {
*
* @param tooltips text lines - merged with a new-line on {@link Builder#build()}.
*/
- public Builder tooltip(@NotNull Text... tooltips) {
- Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
-
- tooltipLines.addAll(List.of(tooltips));
- return this;
- }
-
- public ConfigCategory build() {
- Validate.notNull(name, "`name` must not be null to build `ConfigCategory`");
-
- List<OptionGroup> combinedGroups = new ArrayList<>();
- combinedGroups.add(new OptionGroupImpl(Text.empty(), Text.empty(), ImmutableList.copyOf(rootOptions), false, true));
- combinedGroups.addAll(groups);
-
- Validate.notEmpty(combinedGroups, "at least one option must be added to build `ConfigCategory`");
-
- MutableText concatenatedTooltip = Text.empty();
- boolean first = true;
- for (Text line : tooltipLines) {
- if (!first) concatenatedTooltip.append("\n");
- first = false;
-
- concatenatedTooltip.append(line);
- }
+ Builder tooltip(@NotNull Text... tooltips);
- return new ConfigCategoryImpl(name, ImmutableList.copyOf(combinedGroups), concatenatedTooltip);
- }
+ ConfigCategory build();
}
}
diff --git a/src/client/java/dev/isxander/yacl/api/ListOption.java b/src/client/java/dev/isxander/yacl/api/ListOption.java
index 895898c..54ed3a5 100644
--- a/src/client/java/dev/isxander/yacl/api/ListOption.java
+++ b/src/client/java/dev/isxander/yacl/api/ListOption.java
@@ -51,36 +51,17 @@ public interface ListOption<T> extends OptionGroup, Option<List<T>> {
void addRefreshListener(Runnable changedListener);
static <T> Builder<T> createBuilder(Class<T> typeClass) {
- return new Builder<>(typeClass);
+ return new ListOptionImpl.BuilderImpl<>(typeClass);
}
- class Builder<T> {
- private Text name = Text.empty();
- private final List<Text> tooltipLines = new ArrayList<>();
- private Function<ListOptionEntry<T>, Controller<T>> controllerFunction;
- private Binding<List<T>> binding = null;
- private final Set<OptionFlag> flags = new HashSet<>();
- private T initialValue;
- private boolean collapsed = false;
- private boolean available = true;
- private final Class<T> typeClass;
-
- private Builder(Class<T> typeClass) {
- this.typeClass = typeClass;
- }
-
+ interface Builder<T> {
/**
* Sets name of the list, for UX purposes, a name should always be given,
* but isn't enforced.
*
* @see ListOption#name()
*/
- public Builder<T> name(@NotNull Text name) {
- Validate.notNull(name, "`name` must not be null");
-
- this.name = name;
- return this;
- }
+ Builder<T> name(@NotNull Text name);
/**
* Sets the tooltip to be used by the list. It is displayed like a normal
@@ -91,22 +72,12 @@ public interface ListOption<T> extends OptionGroup, Option<List<T>> {
*
* @param tooltips text lines - merged with a new-line on {@link Builder#build()}.
*/
- public Builder<T> tooltip(@NotNull Text... tooltips) {
- Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
-
- tooltipLines.addAll(List.of(tooltips));
- return this;
- }
+ Builder<T> tooltip(@NotNull Text... tooltips);
/**
* Sets the value that is used when creating new entries
*/
- public Builder<T> initial(@NotNull T initialValue) {
- Validate.notNull(initialValue, "`initialValue` cannot be empty");
-
- this.initialValue = initialValue;
- return this;
- }
+ Builder<T> initial(@NotNull T initialValue);
/**
* Sets the controller for the option.
@@ -114,12 +85,7 @@ public interface ListOption<T> extends OptionGroup, Option<List<T>> {
*
* @see dev.isxander.yacl.gui.controllers
*/
- public Builder<T> controller(@NotNull Function<ListOptionEntry<T>, Controller<T>> control) {
- Validate.notNull(control, "`control` cannot be null");
-
- this.controllerFunction = control;
- return this;
- }
+ Builder<T> controller(@NotNull Function<ListOptionEntry<T>, Controller<T>> control);
/**
* Sets the binding for the option.
@@ -127,12 +93,7 @@ public interface ListOption<T> extends OptionGroup, Option<List<T>> {
*
* @see Binding
*/
- public Builder<T> binding(@NotNull Binding<List<T>> binding) {
- Validate.notNull(binding, "`binding` cannot be null");
-
- this.binding = binding;
- return this;
- }
+ Builder<T> binding(@NotNull Binding<List<T>> binding);
/**
* Sets the binding for the option.
@@ -143,48 +104,28 @@ public interface ListOption<T> extends OptionGroup, Option<List<T>> {
* @param setter should set the option to the supplied value
* @see Binding
*/
- public Builder<T> binding(@NotNull List<T> def, @NotNull Supplier<@NotNull List<T>> getter, @NotNull Consumer<@NotNull List<T>> setter) {
- Validate.notNull(def, "`def` must not be null");
- Validate.notNull(getter, "`getter` must not be null");
- Validate.notNull(setter, "`setter` must not be null");
-
- this.binding = Binding.generic(def, getter, setter);
- return this;
- }
+ Builder<T> binding(@NotNull List<T> def, @NotNull Supplier<@NotNull List<T>> getter, @NotNull Consumer<@NotNull List<T>> setter);
/**
* Sets if the option can be configured
*
* @see Option#available()
*/
- public Builder<T> available(boolean available) {
- this.available = available;
- return this;
- }
+ Builder<T> available(boolean available);
/**
* 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;
- }
+ Builder<T> flag(@NotNull OptionFlag... flag);
/**
* 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;
- }
+ Builder<T> flags(@NotNull Collection<OptionFlag> flags);
/**
* Dictates if the group should be collapsed by default.
@@ -192,26 +133,8 @@ public interface ListOption<T> extends OptionGroup, Option<List<T>> {
*
* @see OptionGroup#collapsed()
*/
- public Builder<T> collapsed(boolean collapsible) {
- this.collapsed = collapsible;
- return this;
- }
-
- public ListOption<T> build() {
- Validate.notNull(controllerFunction, "`controller` must not be null");
- Validate.notNull(binding, "`binding` must not be null");
- Validate.notNull(initialValue, "`initialValue` must not be null");
-
- MutableText concatenatedTooltip = Text.empty();
- boolean first = true;
- for (Text line : tooltipLines) {
- if (!first) concatenatedTooltip.append("\n");
- first = false;
-
- concatenatedTooltip.append(line);
- }
-
- return new ListOptionImpl<>(name, concatenatedTooltip, binding, initialValue, typeClass, controllerFunction, ImmutableSet.copyOf(flags), collapsed, available);
- }
+ Builder<T> collapsed(boolean collapsible);
+
+ ListOption<T> build();
}
}
diff --git a/src/client/java/dev/isxander/yacl/api/Option.java b/src/client/java/dev/isxander/yacl/api/Option.java
index 394723f..406931f 100644
--- a/src/client/java/dev/isxander/yacl/api/Option.java
+++ b/src/client/java/dev/isxander/yacl/api/Option.java
@@ -119,43 +119,16 @@ public interface Option<T> {
* @param typeClass used to capture the type
*/
static <T> Builder<T> createBuilder(Class<T> typeClass) {
- return new Builder<>(typeClass);
+ return new OptionImpl.BuilderImpl<>(typeClass);
}
- class Builder<T> {
- private Text name = Text.literal("Name not specified!").formatted(Formatting.RED);
-
- private final List<Function<T, Text>> tooltipGetters = new ArrayList<>();
-
- private Function<Option<T>, Controller<T>> controlGetter;
-
- private Binding<T> binding;
-
- private boolean available = true;
-
- private boolean instant = false;
-
- private final Set<OptionFlag> flags = new HashSet<>();
-
- private final Class<T> typeClass;
-
- private final List<BiConsumer<Option<T>, T>> listeners = new ArrayList<>();
-
- private Builder(Class<T> typeClass) {
- this.typeClass = typeClass;
- }
-
+ interface Builder<T> {
/**
* Sets the name to be used by the option.
*
* @see Option#name()
*/
- public Builder<T> name(@NotNull Text name) {
- Validate.notNull(name, "`name` cannot be null");
-
- this.name = name;
- return this;
- }
+ Builder<T> name(@NotNull Text name);
/**
* Sets the tooltip to be used by the option.
@@ -163,13 +136,7 @@ public interface Option<T> {
*
* @param tooltipGetter function to get tooltip depending on value {@link Builder#build()}.
*/
- @SafeVarargs
- public final Builder<T> tooltip(@NotNull Function<T, Text>... tooltipGetter) {
- Validate.notNull(tooltipGetter, "`tooltipGetter` cannot be null");
-
- this.tooltipGetters.addAll(List.of(tooltipGetter));
- return this;
- }
+ Builder<T> tooltip(@NotNull Function<T, Text>... tooltipGetter);
/**
* Sets the tooltip to be used by the option.
@@ -178,12 +145,7 @@ public interface Option<T> {
*
* @param tooltips text lines - merged with a new-line on {@link Builder#build()}.
*/
- public Builder<T> tooltip(@NotNull Text... tooltips) {
- Validate.notNull(tooltips, "`tooltips` cannot be empty");
-
- this.tooltipGetters.addAll(Stream.of(tooltips).map(text -> (Function<T, Text>) t -> text).toList());
- return this;
- }
+ Builder<T> tooltip(@NotNull Text... tooltips);
/**
* Sets the controller for the option.
@@ -191,12 +153,7 @@ public interface Option<T> {
*
* @see dev.isxander.yacl.gui.controllers
*/
- public Builder<T> controller(@NotNull Function<Option<T>, Controller<T>> control) {
- Validate.notNull(control, "`control` cannot be null");
-
- this.controlGetter = control;
- return this;
- }
+ Builder<T> controller(@NotNull Function<Option<T>, Controller<T>> control);
/**
* Sets the binding for the option.
@@ -204,12 +161,7 @@ public interface Option<T> {
*
* @see Binding
*/
- public Builder<T> binding(@NotNull Binding<T> binding) {
- Validate.notNull(binding, "`binding` cannot be null");
-
- this.binding = binding;
- return this;
- }
+ Builder<T> binding(@NotNull Binding<T> binding);
/**
* Sets the binding for the option.
@@ -220,48 +172,28 @@ public interface Option<T> {
* @param setter should set the option to the supplied value
* @see Binding
*/
- public Builder<T> binding(@NotNull T def, @NotNull Supplier<@NotNull T> getter, @NotNull Consumer<@NotNull T> setter) {
- Validate.notNull(def, "`def` must not be null");
- Validate.notNull(getter, "`getter` must not be null");
- Validate.notNull(setter, "`setter` must not be null");
-
- this.binding = Binding.generic(def, getter, setter);
- return this;
- }
+ Builder<T> binding(@NotNull T def, @NotNull Supplier<@NotNull T> getter, @NotNull Consumer<@NotNull T> setter);
/**
* Sets if the option can be configured
*
* @see Option#available()
*/
- public Builder<T> available(boolean available) {
- this.available = available;
- return this;
- }
+ Builder<T> available(boolean available);
/**
* 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;
- }
+ Builder<T> flag(@NotNull OptionFlag... flag);
/**
* 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;
- }
+ Builder<T> flags(@NotNull Collection<OptionFlag> flags);
/**
* Instantly invokes the binder's setter when modified in the GUI.
@@ -269,66 +201,22 @@ public interface Option<T> {
* <p>
* Does not support {@link Option#flags()}!
*/
- public Builder<T> instant(boolean instant) {
- this.instant = instant;
- return this;
- }
+ Builder<T> instant(boolean instant);
/**
* Adds a listener to the option. Invoked upon changing the pending value.
*
* @see Option#addListener(BiConsumer)
*/
- public Builder<T> listener(@NotNull BiConsumer<Option<T>, T> listener) {
- this.listeners.add(listener);
- return this;
- }
+ Builder<T> listener(@NotNull BiConsumer<Option<T>, T> listener);
/**
* Adds multiple listeners to the option. Invoked upon changing the pending value.
*
* @see Option#addListener(BiConsumer)
*/
- public Builder<T> listeners(@NotNull Collection<BiConsumer<Option<T>, T>> listeners) {
- this.listeners.addAll(listeners);
- return this;
- }
+ Builder<T> listeners(@NotNull Collection<BiConsumer<Option<T>, T>> listeners);
- /**
- * Dictates whether the option should require a restart.
- * {@link Option#requiresRestart()}
- */
- @Deprecated
- public Builder<T> requiresRestart(boolean requiresRestart) {
- if (requiresRestart) flag(OptionFlag.GAME_RESTART);
- else flags.remove(OptionFlag.GAME_RESTART);
-
- return this;
- }
-
- public Option<T> build() {
- Validate.notNull(controlGetter, "`control` must not be null when building `Option`");
- Validate.notNull(binding, "`binding` must not be null when building `Option`");
- Validate.isTrue(!instant || flags.isEmpty(), "instant application does not support option flags");
-
- Function<T, Text> concatenatedTooltipGetter = value -> {
- MutableText concatenatedTooltip = Text.empty();
- boolean first = true;
- for (Function<T, Text> line : tooltipGetters) {
- if (!first) concatenatedTooltip.append("\n");
- first = false;
-
- concatenatedTooltip.append(line.apply(value));
- }
-
- return concatenatedTooltip;
- };
-
- if (instant) {
- listeners.add((opt, pendingValue) -> opt.applyValue());
- }
-
- return new OptionImpl<>(name, concatenatedTooltipGetter, controlGetter, binding, available, ImmutableSet.copyOf(flags), typeClass, listeners);
- }
+ Option<T> build();
}
}
diff --git a/src/client/java/dev/isxander/yacl/api/OptionGroup.java b/src/client/java/dev/isxander/yacl/api/OptionGroup.java
index 6cc6c7f..8dd9c14 100644
--- a/src/client/java/dev/isxander/yacl/api/OptionGroup.java
+++ b/src/client/java/dev/isxander/yacl/api/OptionGroup.java
@@ -48,30 +48,16 @@ public interface OptionGroup {
* Creates a builder to construct a {@link OptionGroup}
*/
static Builder createBuilder() {
- return new Builder();
+ return new OptionGroupImpl.BuilderImpl();
}
- class Builder {
- private Text name = Text.empty();
- private final List<Text> tooltipLines = new ArrayList<>();
- private final List<Option<?>> options = new ArrayList<>();
- private boolean collapsed = false;
-
- private Builder() {
-
- }
-
+ interface Builder {
/**
* Sets name of the group, can be {@link Text#empty()} to just separate options, like sodium.
*
* @see OptionGroup#name()
*/
- public Builder name(@NotNull Text name) {
- Validate.notNull(name, "`name` must not be null");
-
- this.name = name;
- return this;
- }
+ Builder name(@NotNull Text name);
/**
* Sets the tooltip to be used by the option group.
@@ -80,12 +66,7 @@ public interface OptionGroup {
*
* @param tooltips text lines - merged with a new-line on {@link Builder#build()}.
*/
- public Builder tooltip(@NotNull Text... tooltips) {
- Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
-
- tooltipLines.addAll(List.of(tooltips));
- return this;
- }
+ Builder tooltip(@NotNull Text... tooltips);
/**
* Adds an option to group.
@@ -93,15 +74,7 @@ public interface OptionGroup {
*
* @see OptionGroup#options()
*/
- public Builder option(@NotNull Option<?> option) {
- Validate.notNull(option, "`option` must not be null");
-
- if (option instanceof ListOption<?>)
- throw new UnsupportedOperationException("List options must not be added as an option but a group!");
-
- this.options.add(option);
- return this;
- }
+ Builder option(@NotNull Option<?> option);
/**
* Adds multiple options to group.
@@ -109,39 +82,15 @@ public interface OptionGroup {
*
* @see OptionGroup#options()
*/
- public Builder options(@NotNull Collection<? extends Option<?>> options) {
- Validate.notEmpty(options, "`options` must not be empty");
-
- if (options.stream().anyMatch(ListOption.class::isInstance))
- throw new UnsupportedOperationException("List options must not be added as an option but a group!");
-
- this.options.addAll(options);
- return this;
- }
+ Builder options(@NotNull Collection<? extends Option<?>> options);
/**
* Dictates if the group should be collapsed by default
*
* @see OptionGroup#collapsed()
*/
- public Builder collapsed(boolean collapsible) {
- this.collapsed = collapsible;
- return this;
- }
-
- public OptionGroup build() {
- Validate.notEmpty(options, "`options` must not be empty to build `OptionGroup`");
-
- MutableText concatenatedTooltip = Text.empty();
- boolean first = true;
- for (Text line : tooltipLines) {
- if (!first) concatenatedTooltip.append("\n");
- first = false;
-
- concatenatedTooltip.append(line);
- }
+ Builder collapsed(boolean collapsible);
- return new OptionGroupImpl(name, concatenatedTooltip, ImmutableList.copyOf(options), collapsed, false);
- }
+ OptionGroup build();
}
}
diff --git a/src/client/java/dev/isxander/yacl/api/PlaceholderCategory.java b/src/client/java/dev/isxander/yacl/api/PlaceholderCategory.java
index de7441c..7bcc821 100644
--- a/src/client/java/dev/isxander/yacl/api/PlaceholderCategory.java
+++ b/src/client/java/dev/isxander/yacl/api/PlaceholderCategory.java
@@ -24,31 +24,16 @@ public interface PlaceholderCategory extends ConfigCategory {
BiFunction<MinecraftClient, YACLScreen, Screen> screen();
static Builder createBuilder() {
- return new Builder();
+ return new PlaceholderCategoryImpl.BuilderImpl();
}
- class Builder {
- private Text name;
-
- private final List<Text> tooltipLines = new ArrayList<>();
-
- private BiFunction<MinecraftClient, YACLScreen, Screen> screenFunction;
-
- private Builder() {
-
- }
-
+ interface Builder {
/**
* Sets name of the category
*
* @see ConfigCategory#name()
*/
- public Builder name(@NotNull Text name) {
- Validate.notNull(name, "`name` cannot be null");
-
- this.name = name;
- return this;
- }
+ Builder name(@NotNull Text name);
/**
* Sets the tooltip to be used by the category.
@@ -57,38 +42,15 @@ public interface PlaceholderCategory extends ConfigCategory {
*
* @param tooltips text lines - merged with a new-line on {@link Builder#build()}.
*/
- public Builder tooltip(@NotNull Text... tooltips) {
- Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
-
- tooltipLines.addAll(List.of(tooltips));
- return this;
- }
+ Builder tooltip(@NotNull Text... tooltips);
/**
* Screen to open upon selecting this category
*
* @see PlaceholderCategory#screen()
*/
- public Builder screen(@NotNull BiFunction<MinecraftClient, YACLScreen, Screen> screenFunction) {
- Validate.notNull(screenFunction, "`screenFunction` cannot be null");
-
- this.screenFunction = screenFunction;
- return this;
- }
-
- public PlaceholderCategory build() {
- Validate.notNull(name, "`name` must not be null to build `ConfigCategory`");
-
- MutableText concatenatedTooltip = Text.empty();
- boolean first = true;
- for (Text line : tooltipLines) {
- if (!first) concatenatedTooltip.append("\n");
- first = false;
-
- concatenatedTooltip.append(line);
- }
+ Builder screen(@NotNull BiFunction<MinecraftClient, YACLScreen, Screen> screenFunction);
- return new PlaceholderCategoryImpl(name, screenFunction, concatenatedTooltip);
- }
+ PlaceholderCategory build();
}
}
diff --git a/src/client/java/dev/isxander/yacl/api/YetAnotherConfigLib.java b/src/client/java/dev/isxander/yacl/api/YetAnotherConfigLib.java
index ae6c060..f9a71d3 100644
--- a/src/client/java/dev/isxander/yacl/api/YetAnotherConfigLib.java
+++ b/src/client/java/dev/isxander/yacl/api/YetAnotherConfigLib.java
@@ -52,7 +52,7 @@ public interface YetAnotherConfigLib {
* Creates a builder to construct YACL
*/
static Builder createBuilder() {
- return new Builder();
+ return new YetAnotherConfigLibImpl.BuilderImpl();
}
/**
@@ -63,27 +63,13 @@ public interface YetAnotherConfigLib {
return builder.build(configInstance.getDefaults(), configInstance.getConfig(), createBuilder().save(configInstance::save)).build();
}
- class Builder {
- private Text title;
- private final List<ConfigCategory> categories = new ArrayList<>();
- private Runnable saveFunction = () -> {};
- private Consumer<YACLScreen> initConsumer = screen -> {};
-
- private Builder() {
-
- }
-
+ interface Builder {
/**
* Sets title of GUI for Minecraft narration
*
* @see YetAnotherConfigLib#title()
*/
- public Builder title(@NotNull Text title) {
- Validate.notNull(title, "`title` cannot be null");
-
- this.title = title;
- return this;
- }
+ Builder title(@NotNull Text title);
/**
* Adds a new category.
@@ -91,12 +77,7 @@ public interface YetAnotherConfigLib {
*
* @see YetAnotherConfigLib#categories()
*/
- public Builder category(@NotNull ConfigCategory category) {
- Validate.notNull(category, "`category` cannot be null");
-
- this.categories.add(category);
- return this;
- }
+ Builder category(@NotNull ConfigCategory category);
/**
* Adds multiple categories at once.
@@ -104,44 +85,23 @@ public interface YetAnotherConfigLib {
*
* @see YetAnotherConfigLib#categories()
*/
- public Builder categories(@NotNull Collection<? extends ConfigCategory> categories) {
- Validate.notNull(categories, "`categories` cannot be null");
-
- this.categories.addAll(categories);
- return this;
- }
+ Builder categories(@NotNull Collection<? extends ConfigCategory> categories);
/**
* Used to define a save function for when user clicks the Save Changes button
*
* @see YetAnotherConfigLib#saveFunction()
*/
- public Builder save(@NotNull Runnable saveFunction) {
- Validate.notNull(saveFunction, "`saveFunction` cannot be null");
-
- this.saveFunction = saveFunction;
- return this;
- }
+ Builder save(@NotNull Runnable saveFunction);
/**
* Defines a consumer that is accepted every time the YACL screen initialises
*
* @see YetAnotherConfigLib#initConsumer()
*/
- public Builder screenInit(@NotNull Consumer<YACLScreen> initConsumer) {
- Validate.notNull(initConsumer, "`initConsumer` cannot be null");
-
- this.initConsumer = initConsumer;
- return this;
- }
-
- public YetAnotherConfigLib build() {
- Validate.notNull(title, "`title must not be null to build `YetAnotherConfigLib`");
- Validate.notEmpty(categories, "`categories` must not be empty to build `YetAnotherConfigLib`");
- Validate.isTrue(!categories.stream().allMatch(category -> category instanceof PlaceholderCategory), "At least one regular category is required to build `YetAnotherConfigLib`");
+ Builder screenInit(@NotNull Consumer<YACLScreen> initConsumer);
- return new YetAnotherConfigLibImpl(title, ImmutableList.copyOf(categories), saveFunction, initConsumer);
- }
+ YetAnotherConfigLib build();
}
@FunctionalInterface
diff --git a/src/client/java/dev/isxander/yacl/gui/OptionListWidget.java b/src/client/java/dev/isxander/yacl/gui/OptionListWidget.java
index 8284b0e..c18597f 100644
--- a/src/client/java/dev/isxander/yacl/gui/OptionListWidget.java
+++ b/src/client/java/dev/isxander/yacl/gui/OptionListWidget.java
@@ -4,6 +4,8 @@ import com.google.common.collect.ImmutableList;
import dev.isxander.yacl.api.*;
import dev.isxander.yacl.api.utils.Dimension;
import dev.isxander.yacl.gui.controllers.ListEntryWidget;
+import dev.isxander.yacl.impl.ListOptionEntryImpl;
+import dev.isxander.yacl.impl.utils.YACLConstants;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.MultilineText;
import net.minecraft.client.font.TextRenderer;
@@ -65,7 +67,7 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr
List<OptionEntry> optionEntries = new ArrayList<>();
for (Option<?> option : group.options()) {
- OptionEntry entry = new OptionEntry(option, category, group, groupSeparatorEntry, option.controller().provideWidget(yaclScreen, getDefaultEntryPosition()));
+ OptionEntry entry = new OptionEntry(option, category, group, groupSeparatorEntry, option.controller().provideWidget(yaclScreen, getDefaultEntryDimension()));
addEntry(entry);
optionEntries.add(entry);
}
@@ -85,23 +87,31 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr
// find group separator for group
GroupSeparatorEntry groupSeparator = super.children().stream().filter(e -> e instanceof GroupSeparatorEntry gs && gs.group == listOption).map(GroupSeparatorEntry.class::cast).findAny().orElse(null);
- if (groupSeparator == null)
+ if (groupSeparator == null) {
+ YACLConstants.LOGGER.warn("Can't find group seperator to refresh list option entries for list option " + listOption.name());
return;
+ }
for (OptionEntry entry : groupSeparator.optionEntries)
super.removeEntry(entry);
-
groupSeparator.optionEntries.clear();
+
+ // if no entries, below loop won't run where addEntryBelow() recaches viewable children
+ if (listOption.options().isEmpty()) {
+ recacheViewableChildren();
+ return;
+ }
+
Entry lastEntry = groupSeparator;
for (ListOptionEntry<?> listOptionEntry : listOption.options()) {
- OptionEntry optionEntry = new OptionEntry(listOptionEntry, category, listOption, groupSeparator, listOptionEntry.controller().provideWidget(yaclScreen, getDefaultEntryPosition()));
+ OptionEntry optionEntry = new OptionEntry(listOptionEntry, category, listOption, groupSeparator, listOptionEntry.controller().provideWidget(yaclScreen, getDefaultEntryDimension()));
addEntryBelow(lastEntry, optionEntry);
groupSeparator.optionEntries.add(optionEntry);
lastEntry = optionEntry;
}
}
- public Dimension<Integer> getDefaultEntryPosition() {
+ public Dimension<Integer> getDefaultEntryDimension() {
return Dimension.ofInt(getRowLeft(), 0, getRowWidth(), 20);
}
@@ -245,9 +255,7 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr
this.category = category;
this.group = group;
this.groupSeparatorEntry = groupSeparatorEntry;
- if (option instanceof ListOptionEntry<?> listOptionEntry)
- this.widget = new ListEntryWidget(yaclScreen, listOptionEntry, widget);
- else this.widget = widget;
+ this.widget = widget;
this.categoryName = category.name().getString().toLowerCase();
this.groupName = group.name().getString().toLowerCase();
if (option.canResetToDefault() && this.widget.canReset()) {
@@ -306,7 +314,7 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr
@Override
public int getItemHeight() {
- return widget.getDimension().height() + 2;
+ return Math.max(widget.getDimension().height(), resetButton != null ? resetButton.getHeight() : 0) + 2;
}
@Override
@@ -377,13 +385,16 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr
}
public void setExpanded(boolean expanded) {
+ if (this.groupExpanded == expanded)
+ return;
+
this.groupExpanded = expanded;
updateExpandMinimizeText();
+ recacheViewableChildren();
}
protected void onExpandButtonPress() {
setExpanded(!isExpanded());
- recacheViewableChildren();
}
protected void updateExpandMinimizeText() {
@@ -447,6 +458,7 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr
this.addListButton = new TooltipButtonWidget(yaclScreen, resetListButton.getX() - 20, -50, 20, 20, Text.of("+"), Text.translatable("yacl.list.add_top"), btn -> {
group.insertNewEntryToTop();
+ setExpanded(true);
});
updateExpandMinimizeText();
@@ -480,15 +492,12 @@ public class OptionListWidget extends ElementListWidgetExt<OptionListWidget.Entr
private void minimizeIfUnavailable() {
if (!listOption.available() && isExpanded()) {
setExpanded(false);
- recacheViewableChildren();
}
}
@Override
protected void updateExpandMinimizeText() {
super.updateExpandMinimizeText();
- if (resetListButton != null && addListButton != null)
- resetListButton.visible = addListButton.visible = isExpanded();
expandMinimizeButton.active = listOption == null || listOption.available();
}
diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/ListEntryWidget.java b/src/client/java/dev/isxander/yacl/gui/controllers/ListEntryWidget.java
index a548efb..0a5d581 100644
--- a/src/client/java/dev/isxander/yacl/gui/controllers/ListEntryWidget.java
+++ b/src/client/java/dev/isxander/yacl/gui/controllers/ListEntryWidget.java
@@ -27,7 +27,7 @@ public class ListEntryWidget extends AbstractWidget implements ParentElement {
private boolean dragging;
public ListEntryWidget(YACLScreen screen, ListOptionEntry<?> listOptionEntry, AbstractWidget entryWidget) {
- super(entryWidget.getDimension());
+ super(entryWidget.getDimension().withHeight(Math.max(entryWidget.getDimension().height(), 20) - ((listOptionEntry.parentGroup().indexOf(listOptionEntry) == listOptionEntry.parentGroup().options().size() - 1) ? 0 : 2))); // -2 to remove the padding
this.listOptionEntry = listOptionEntry;
this.listOption = listOptionEntry.parentGroup();
this.optionNameString = listOptionEntry.name().getString().toLowerCase();
@@ -38,6 +38,7 @@ public class ListEntryWidget extends AbstractWidget implements ParentElement {
removeButton = new TooltipButtonWidget(screen, dim.xLimit() - 20, dim.y(), 20, 20, Text.of("\u274c"), Text.translatable("yacl.list.remove"), btn -> {
listOption.removeEntry(listOptionEntry);
+ updateButtonStates();
});
moveUpButton = new TooltipButtonWidget(screen, dim.x(), dim.y(), 20, 20, Text.of("\u2191"), Text.translatable("yacl.list.move_up"), btn -> {
diff --git a/src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java b/src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java
index f526d42..25260c4 100644
--- a/src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java
+++ b/src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java
@@ -3,14 +3,21 @@ package dev.isxander.yacl.impl;
import com.google.common.collect.ImmutableSet;
import dev.isxander.yacl.api.*;
import dev.isxander.yacl.gui.YACLScreen;
+import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
+import org.apache.commons.lang3.Validate;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.Function;
-public class ButtonOptionImpl implements ButtonOption {
+@ApiStatus.Internal
+public final class ButtonOptionImpl implements ButtonOption {
private final Text name;
private final Text tooltip;
private final BiConsumer<YACLScreen, ButtonOption> action;
@@ -134,4 +141,78 @@ public class ButtonOptionImpl implements ButtonOption {
throw new UnsupportedOperationException();
}
}
+
+ @ApiStatus.Internal
+ public static final class BuilderImpl implements ButtonOption.Builder {
+ private Text name;
+ private final List<Text> tooltipLines = new ArrayList<>();
+ private boolean available = true;
+ private Function<ButtonOption, Controller<BiConsumer<YACLScreen, ButtonOption>>> controlGetter;
+ private BiConsumer<YACLScreen, ButtonOption> action;
+
+ @Override
+ public ButtonOption.Builder name(@NotNull Text name) {
+ Validate.notNull(name, "`name` cannot be null");
+
+ this.name = name;
+ return this;
+ }
+
+ @Override
+ public ButtonOption.Builder tooltip(@NotNull Text... tooltips) {
+ Validate.notNull(tooltips, "`tooltips` cannot be empty");
+
+ tooltipLines.addAll(List.of(tooltips));
+ return this;
+ }
+
+ @Override
+ public ButtonOption.Builder action(@NotNull BiConsumer<YACLScreen, ButtonOption> action) {
+ Validate.notNull(action, "`action` cannot be null");
+
+ this.action = action;
+ return this;
+ }
+
+ @Override
+ @Deprecated
+ public ButtonOption.Builder action(@NotNull Consumer<YACLScreen> action) {
+ Validate.notNull(action, "`action` cannot be null");
+
+ this.action = (screen, button) -> action.accept(screen);
+ return this;
+ }
+
+ @Override
+ public ButtonOption.Builder available(boolean available) {
+ this.available = available;
+ return this;
+ }
+
+ @Override
+ public ButtonOption.Builder controller(@NotNull Function<ButtonOption, Controller<BiConsumer<YACLScreen, ButtonOption>>> control) {
+ Validate.notNull(control, "`control` cannot be null");
+
+ this.controlGetter = control;
+ return this;
+ }
+
+ @Override
+ public ButtonOption build() {
+ Validate.notNull(name, "`name` must not be null when building `Option`");
+ Validate.notNull(controlGetter, "`control` must not be null when building `Option`");
+ Validate.notNull(action, "`action` must not be null when building `Option`");
+
+ MutableText concatenatedTooltip = Text.empty();
+ boolean first = true;
+ for (Text line : tooltipLines) {
+ if (!first) concatenatedTooltip.append("\n");
+ first = false;
+
+ concatenatedTooltip.append(line);
+ }
+
+ return new ButtonOptionImpl(name, concatenatedTooltip, action, available, controlGetter);
+ }
+ }
}
diff --git a/src/client/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java b/src/client/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java
index 971fecf..b3da814 100644
--- a/src/client/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java
+++ b/src/client/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java
@@ -2,9 +2,107 @@ package dev.isxander.yacl.impl;
import com.google.common.collect.ImmutableList;
import dev.isxander.yacl.api.ConfigCategory;
+import dev.isxander.yacl.api.ListOption;
+import dev.isxander.yacl.api.Option;
import dev.isxander.yacl.api.OptionGroup;
+import dev.isxander.yacl.impl.utils.YACLConstants;
+import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
+import org.apache.commons.lang3.Validate;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+@ApiStatus.Internal
public record ConfigCategoryImpl(Text name, ImmutableList<OptionGroup> groups, Text tooltip) implements ConfigCategory {
+ @ApiStatus.Internal
+ public static final class BuilderImpl implements ConfigCategory.Builder {
+ private Text name;
+
+ private final List<Option<?>> rootOptions = new ArrayList<>();
+ private final List<OptionGroup> groups = new ArrayList<>();
+
+ private final List<Text> tooltipLines = new ArrayList<>();
+
+ @Override
+ public ConfigCategory.Builder name(@NotNull Text name) {
+ Validate.notNull(name, "`name` cannot be null");
+
+ this.name = name;
+ return this;
+ }
+
+ @Override
+ public ConfigCategory.Builder option(@NotNull Option<?> option) {
+ Validate.notNull(option, "`option` must not be null");
+
+ if (option instanceof ListOption<?> listOption) {
+ YACLConstants.LOGGER.warn("Adding list option as an option is not supported! Rerouting to group!");
+ return group(listOption);
+ }
+
+ this.rootOptions.add(option);
+ return this;
+ }
+
+ @Override
+ public ConfigCategory.Builder options(@NotNull Collection<Option<?>> options) {
+ Validate.notNull(options, "`options` must not be null");
+
+ if (options.stream().anyMatch(ListOption.class::isInstance))
+ throw new UnsupportedOperationException("List options must not be added as an option but a group!");
+
+ this.rootOptions.addAll(options);
+ return this;
+ }
+
+ @Override
+ public ConfigCategory.Builder group(@NotNull OptionGroup group) {
+ Validate.notNull(group, "`group` must not be null");
+
+ this.groups.add(group);
+ return this;
+ }
+
+ @Override
+ public ConfigCategory.Builder groups(@NotNull Collection<OptionGroup> groups) {
+ Validate.notEmpty(groups, "`groups` must not be empty");
+
+ this.groups.addAll(groups);
+ return this;
+ }
+
+ @Override
+ public ConfigCategory.Builder tooltip(@NotNull Text... tooltips) {
+ Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
+
+ tooltipLines.addAll(List.of(tooltips));
+ return this;
+ }
+
+ @Override
+ public ConfigCategory build() {
+ Validate.notNull(name, "`name` must not be null to build `ConfigCategory`");
+
+ List<OptionGroup> combinedGroups = new ArrayList<>();
+ combinedGroups.add(new OptionGroupImpl(Text.empty(), Text.empty(), ImmutableList.copyOf(rootOptions), false, true));
+ combinedGroups.addAll(groups);
+
+ Validate.notEmpty(combinedGroups, "at least one option must be added to build `ConfigCategory`");
+
+ MutableText concatenatedTooltip = Text.empty();
+ boolean first = true;
+ for (Text line : tooltipLines) {
+ if (!first) concatenatedTooltip.append("\n");
+ first = false;
+
+ concatenatedTooltip.append(line);
+ }
+ return new ConfigCategoryImpl(name, ImmutableList.copyOf(combinedGroups), concatenatedTooltip);
+ }
+ }
}
diff --git a/src/client/java/dev/isxander/yacl/impl/GenericBindingImpl.java b/src/client/java/dev/isxander/yacl/impl/GenericBindingImpl.java
index 1867bb6..0d668c6 100644
--- a/src/client/java/dev/isxander/yacl/impl/GenericBindingImpl.java
+++ b/src/client/java/dev/isxander/yacl/impl/GenericBindingImpl.java
@@ -5,7 +5,7 @@ import dev.isxander.yacl.api.Binding;
import java.util.function.Consumer;
import java.util.function.Supplier;
-public class GenericBindingImpl<T> implements Binding<T> {
+public final class GenericBindingImpl<T> implements Binding<T> {
private final T def;
private final Supplier<T> getter;
private final Consumer<T> setter;
diff --git a/src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java b/src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java
index dc7aa88..98188dc 100644
--- a/src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java
+++ b/src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java
@@ -1,13 +1,19 @@
package dev.isxander.yacl.impl;
import dev.isxander.yacl.api.*;
+import dev.isxander.yacl.api.utils.Dimension;
+import dev.isxander.yacl.gui.AbstractWidget;
+import dev.isxander.yacl.gui.YACLScreen;
+import dev.isxander.yacl.gui.controllers.ListEntryWidget;
import net.minecraft.text.Text;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.function.BiConsumer;
import java.util.function.Function;
-public class ListOptionEntryImpl<T> implements ListOptionEntry<T> {
+@ApiStatus.Internal
+public final class ListOptionEntryImpl<T> implements ListOptionEntry<T> {
private final ListOptionImpl<T> group;
private T value;
@@ -15,11 +21,11 @@ public class ListOptionEntryImpl<T> implements ListOptionEntry<T> {
private final Binding<T> binding;
private final Controller<T> controller;
- public ListOptionEntryImpl(ListOptionImpl<T> group, T initialValue, @NotNull Function<ListOptionEntry<T>, Controller<T>> controlGetter) {
+ ListOptionEntryImpl(ListOptionImpl<T> group, T initialValue, @NotNull Function<ListOptionEntry<T>, Controller<T>> controlGetter) {
this.group = group;
this.value = initialValue;
this.binding = new EntryBinding();
- this.controller = controlGetter.apply(this);
+ this.controller = new EntryController<>(controlGetter.apply(this), this);
}
@Override
@@ -102,6 +108,27 @@ public class ListOptionEntryImpl<T> implements ListOptionEntry<T> {
}
+ /**
+ * Open in case mods need to find the real controller type.
+ */
+ @ApiStatus.Internal
+ public record EntryController<T>(Controller<T> controller, ListOptionEntryImpl<T> entry) implements Controller<T> {
+ @Override
+ public Option<T> option() {
+ return controller.option();
+ }
+
+ @Override
+ public Text formatValue() {
+ return controller.formatValue();
+ }
+
+ @Override
+ public AbstractWidget provideWidget(YACLScreen screen, Dimension<Integer> widgetDimension) {
+ return new ListEntryWidget(screen, entry, controller.provideWidget(screen, widgetDimension));
+ }
+ }
+
private class EntryBinding implements Binding<T> {
@Override
public void setValue(T newValue) {
diff --git a/src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java b/src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java
index 128d3e7..1924205 100644
--- a/src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java
+++ b/src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java
@@ -3,17 +3,21 @@ package dev.isxander.yacl.impl;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import dev.isxander.yacl.api.*;
+import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
+import org.apache.commons.lang3.Validate;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
-public class ListOptionImpl<T> implements ListOption<T> {
+@ApiStatus.Internal
+public final class ListOptionImpl<T> implements ListOption<T> {
private final Text name;
private final Text tooltip;
private final Binding<List<T>> binding;
@@ -109,8 +113,8 @@ public class ListOptionImpl<T> implements ListOption<T> {
@Override
public void removeEntry(ListOptionEntry<?> entry) {
- entries.remove(entry);
- onRefresh();
+ if (entries.remove(entry))
+ onRefresh();
}
@Override
@@ -123,7 +127,6 @@ public class ListOptionImpl<T> implements ListOption<T> {
entries.clear();
entries.addAll(createEntries(value));
onRefresh();
- listeners.forEach(listener -> listener.accept(this, value));
}
@Override
@@ -205,4 +208,117 @@ public class ListOptionImpl<T> implements ListOption<T> {
return new ListOptionEntryImpl<>(ListOptionImpl.this, initialValue, controllerFunction);
}
}
+
+ @ApiStatus.Internal
+ public static final class BuilderImpl<T> implements ListOption.Builder<T> {
+ private Text name = Text.empty();
+ private final List<Text> tooltipLines = new ArrayList<>();
+ private Function<ListOptionEntry<T>, Controller<T>> controllerFunction;
+ private Binding<List<T>> binding = null;
+ private final Set<OptionFlag> flags = new HashSet<>();
+ private T initialValue;
+ private boolean collapsed = false;
+ private boolean available = true;
+ private final Class<T> typeClass;
+
+ public BuilderImpl(Class<T> typeClass) {
+ this.typeClass = typeClass;
+ }
+
+ @Override
+ public ListOption.Builder<T> name(@NotNull Text name) {
+ Validate.notNull(name, "`name` must not be null");
+
+ this.name = name;
+ return this;
+ }
+
+ @Override
+ public ListOption.Builder<T> tooltip(@NotNull Text... tooltips) {
+ Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
+
+ tooltipLines.addAll(List.of(tooltips));
+ return this;
+ }
+
+ @Override
+ public ListOption.Builder<T> initial(@NotNull T initialValue) {
+ Validate.notNull(initialValue, "`initialValue` cannot be empty");
+
+ this.initialValue = initialValue;
+ return this;
+ }
+
+ @Override
+ public ListOption.Builder<T> controller(@NotNull Function<ListOptionEntry<T>, Controller<T>> control) {
+ Validate.notNull(control, "`control` cannot be null");
+
+ this.controllerFunction = control;
+ return this;
+ }
+
+ @Override
+ public ListOption.Builder<T> binding(@NotNull Binding<List<T>> binding) {
+ Validate.notNull(binding, "`binding` cannot be null");
+
+ this.binding = binding;
+ return this;
+ }
+
+ @Override
+ public ListOption.Builder<T> binding(@NotNull List<T> def, @NotNull Supplier<@NotNull List<T>> getter, @NotNull Consumer<@NotNull List<T>> setter) {
+ Validate.notNull(def, "`def` must not be null");
+ Validate.notNull(getter, "`getter` must not be null");
+ Validate.notNull(setter, "`setter` must not be null");
+
+ this.binding = Binding.generic(def, getter, setter);
+ return this;
+ }
+
+ @Override
+ public ListOption.Builder<T> available(boolean available) {
+ this.available = available;
+ return this;
+ }
+
+ @Override
+ public ListOption.Builder<T> flag(@NotNull OptionFlag... flag) {
+ Validate.notNull(flag, "`flag` must not be null");
+
+ this.flags.addAll(Arrays.asList(flag));
+ return this;
+ }
+
+ @Override
+ public ListOption.Builder<T> flags(@NotNull Collection<OptionFlag> flags) {
+ Validate.notNull(flags, "`flags` must not be null");
+
+ this.flags.addAll(flags);
+ return this;
+ }
+
+ @Override
+ public ListOption.Builder<T> collapsed(boolean collapsible) {
+ this.collapsed = collapsible;
+ return this;
+ }
+
+ @Override
+ public ListOption<T> build() {
+ Validate.notNull(controllerFunction, "`controller` must not be null");
+ Validate.notNull(binding, "`binding` must not be null");
+ Validate.notNull(initialValue, "`initialValue` must not be null");
+
+ MutableText concatenatedTooltip = Text.empty();
+ boolean first = true;
+ for (Text line : tooltipLines) {
+ if (!first) concatenatedTooltip.append("\n");
+ first = false;
+
+ concatenatedTooltip.append(line);
+ }
+
+ return new ListOptionImpl<>(name, concatenatedTooltip, binding, initialValue, typeClass, controllerFunction, ImmutableSet.copyOf(flags), collapsed, available);
+ }
+ }
}
diff --git a/src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java b/src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java
index 02ef04c..89a2adf 100644
--- a/src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java
+++ b/src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java
@@ -1,10 +1,86 @@
package dev.isxander.yacl.impl;
import com.google.common.collect.ImmutableList;
+import dev.isxander.yacl.api.ListOption;
import dev.isxander.yacl.api.Option;
import dev.isxander.yacl.api.OptionGroup;
+import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
+import org.apache.commons.lang3.Validate;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+@ApiStatus.Internal
public record OptionGroupImpl(@NotNull Text name, @NotNull Text tooltip, ImmutableList<? extends Option<?>> options, boolean collapsed, boolean isRoot) implements OptionGroup {
+ @ApiStatus.Internal
+ public static final class BuilderImpl implements OptionGroup.Builder {
+ private Text name = Text.empty();
+ private final List<Text> tooltipLines = new ArrayList<>();
+ private final List<Option<?>> options = new ArrayList<>();
+ private boolean collapsed = false;
+
+ @Override
+ public OptionGroup.Builder name(@NotNull Text name) {
+ Validate.notNull(name, "`name` must not be null");
+
+ this.name = name;
+ return this;
+ }
+
+ @Override
+ public OptionGroup.Builder tooltip(@NotNull Text... tooltips) {
+ Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
+
+ tooltipLines.addAll(List.of(tooltips));
+ return this;
+ }
+
+ @Override
+ public OptionGroup.Builder option(@NotNull Option<?> option) {
+ Validate.notNull(option, "`option` must not be null");
+
+ if (option instanceof ListOption<?>)
+ throw new UnsupportedOperationException("List options must not be added as an option but a group!");
+
+ this.options.add(option);
+ return this;
+ }
+
+ @Override
+ public OptionGroup.Builder options(@NotNull Collection<? extends Option<?>> options) {
+ Validate.notEmpty(options, "`options` must not be empty");
+
+ if (options.stream().anyMatch(ListOption.class::isInstance))
+ throw new UnsupportedOperationException("List options must not be added as an option but a group!");
+
+ this.options.addAll(options);
+ return this;
+ }
+
+ @Override
+ public OptionGroup.Builder collapsed(boolean collapsible) {
+ this.collapsed = collapsible;
+ return this;
+ }
+
+ @Override
+ public OptionGroup build() {
+ Validate.notEmpty(options, "`options` must not be empty to build `OptionGroup`");
+
+ MutableText concatenatedTooltip = Text.empty();
+ boolean first = true;
+ for (Text line : tooltipLines) {
+ if (!first) concatenatedTooltip.append("\n");
+ first = false;
+
+ concatenatedTooltip.append(line);
+ }
+
+ return new OptionGroupImpl(name, concatenatedTooltip, ImmutableList.copyOf(options), collapsed, false);
+ }
+ }
}
diff --git a/src/client/java/dev/isxander/yacl/impl/OptionImpl.java b/src/client/java/dev/isxander/yacl/impl/OptionImpl.java
index 0cc156f..d2815e1 100644
--- a/src/client/java/dev/isxander/yacl/impl/OptionImpl.java
+++ b/src/client/java/dev/isxander/yacl/impl/OptionImpl.java
@@ -5,17 +5,23 @@ 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.MutableText;
import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import org.apache.commons.lang3.Validate;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
-public class OptionImpl<T> implements Option<T> {
+@ApiStatus.Internal
+public final class OptionImpl<T> implements Option<T> {
private final Text name;
private Text tooltip;
private final Controller<T> controller;
@@ -136,4 +142,146 @@ public class OptionImpl<T> implements Option<T> {
public void addListener(BiConsumer<Option<T>, T> changedListener) {
this.listeners.add(changedListener);
}
+
+ @ApiStatus.Internal
+ public static class BuilderImpl<T> implements Option.Builder<T> {
+ private Text name = Text.literal("Name not specified!").formatted(Formatting.RED);
+
+ private final List<Function<T, Text>> tooltipGetters = new ArrayList<>();
+
+ private Function<Option<T>, Controller<T>> controlGetter;
+
+ private Binding<T> binding;
+
+ private boolean available = true;
+
+ private boolean instant = false;
+
+ private final Set<OptionFlag> flags = new HashSet<>();
+
+ private final Class<T> typeClass;
+
+ private final List<BiConsumer<Option<T>, T>> listeners = new ArrayList<>();
+
+ public BuilderImpl(Class<T> typeClass) {
+ this.typeClass = typeClass;
+ }
+
+ @Override
+ public Option.Builder<T> name(@NotNull Text name) {
+ Validate.notNull(name, "`name` cannot be null");
+
+ this.name = name;
+ return this;
+ }
+
+ @Override
+ @SafeVarargs
+ public final Option.Builder<T> tooltip(@NotNull Function<T, Text>... tooltipGetter) {
+ Validate.notNull(tooltipGetter, "`tooltipGetter` cannot be null");
+
+ this.tooltipGetters.addAll(List.of(tooltipGetter));
+ return this;
+ }
+
+ @Override
+ public Option.Builder<T> tooltip(@NotNull Text... tooltips) {
+ Validate.notNull(tooltips, "`tooltips` cannot be empty");
+
+ this.tooltipGetters.addAll(Stream.of(tooltips).map(text -> (Function<T, Text>) t -> text).toList());
+ return this;
+ }
+
+ @Override
+ public Option.Builder<T> controller(@NotNull Function<Option<T>, Controller<T>> control) {
+ Validate.notNull(control, "`control` cannot be null");
+
+ this.controlGetter = control;
+ return this;
+ }
+
+ @Override
+ public Option.Builder<T> binding(@NotNull Binding<T> binding) {
+ Validate.notNull(binding, "`binding` cannot be null");
+
+ this.binding = binding;
+ return this;
+ }
+
+ @Override
+ public Option.Builder<T> binding(@NotNull T def, @NotNull Supplier<@NotNull T> getter, @NotNull Consumer<@NotNull T> setter) {
+ Validate.notNull(def, "`def` must not be null");
+ Validate.notNull(getter, "`getter` must not be null");
+ Validate.notNull(setter, "`setter` must not be null");
+
+ this.binding = Binding.generic(def, getter, setter);
+ return this;
+ }
+
+ @Override
+ public Option.Builder<T> available(boolean available) {
+ this.available = available;
+ return this;
+ }
+
+ @Override
+ public Option.Builder<T> flag(@NotNull OptionFlag... flag) {
+ Validate.notNull(flag, "`flag` must not be null");
+
+ this.flags.addAll(Arrays.asList(flag));
+ return this;
+ }
+
+ @Override
+ public Option.Builder<T> flags(@NotNull Collection<OptionFlag> flags) {
+ Validate.notNull(flags, "`flags` must not be null");
+
+ this.flags.addAll(flags);
+ return this;
+ }
+
+ @Override
+ public Option.Builder<T> instant(boolean instant) {
+ this.instant = instant;
+ return this;
+ }
+
+ @Override
+ public Option.Builder<T> listener(@NotNull BiConsumer<Option<T>, T> listener) {
+ this.listeners.add(listener);
+ return this;
+ }
+
+ @Override
+ public Option.Builder<T> listeners(@NotNull Collection<BiConsumer<Option<T>, T>> listeners) {
+ this.listeners.addAll(listeners);
+ return this;
+ }
+
+ @Override
+ public Option<T> build() {
+ Validate.notNull(controlGetter, "`control` must not be null when building `Option`");
+ Validate.notNull(binding, "`binding` must not be null when building `Option`");
+ Validate.isTrue(!instant || flags.isEmpty(), "instant application does not support option flags");
+
+ Function<T, Text> concatenatedTooltipGetter = value -> {
+ MutableText concatenatedTooltip = Text.empty();
+ boolean first = true;
+ for (Function<T, Text> line : tooltipGetters) {
+ if (!first) concatenatedTooltip.append("\n");
+ first = false;
+
+ concatenatedTooltip.append(line.apply(value));
+ }
+
+ return concatenatedTooltip;
+ };
+
+ if (instant) {
+ listeners.add((opt, pendingValue) -> opt.applyValue());
+ }
+
+ return new OptionImpl<>(name, concatenatedTooltipGetter, controlGetter, binding, available, ImmutableSet.copyOf(flags), typeClass, listeners);
+ }
+ }
}
diff --git a/src/client/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java b/src/client/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java
index a5180ad..28e5886 100644
--- a/src/client/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java
+++ b/src/client/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java
@@ -1,19 +1,75 @@
package dev.isxander.yacl.impl;
import com.google.common.collect.ImmutableList;
+import dev.isxander.yacl.api.ConfigCategory;
import dev.isxander.yacl.api.OptionGroup;
import dev.isxander.yacl.api.PlaceholderCategory;
import dev.isxander.yacl.gui.YACLScreen;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
+import org.apache.commons.lang3.Validate;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.BiFunction;
+@ApiStatus.Internal
public record PlaceholderCategoryImpl(Text name, BiFunction<MinecraftClient, YACLScreen, Screen> screen, Text tooltip) implements PlaceholderCategory {
@Override
public @NotNull ImmutableList<OptionGroup> groups() {
return ImmutableList.of();
}
+
+ @ApiStatus.Internal
+ public static final class BuilderImpl implements PlaceholderCategory.Builder {
+ private Text name;
+
+ private final List<Text> tooltipLines = new ArrayList<>();
+
+ private BiFunction<MinecraftClient, YACLScreen, Screen> screenFunction;
+
+ @Override
+ public PlaceholderCategory.Builder name(@NotNull Text name) {
+ Validate.notNull(name, "`name` cannot be null");
+
+ this.name = name;
+ return this;
+ }
+
+ @Override
+ public PlaceholderCategory.Builder tooltip(@NotNull Text... tooltips) {
+ Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
+
+ tooltipLines.addAll(List.of(tooltips));
+ return this;
+ }
+
+ @Override
+ public PlaceholderCategory.Builder screen(@NotNull BiFunction<MinecraftClient, YACLScreen, Screen> screenFunction) {
+ Validate.notNull(screenFunction, "`screenFunction` cannot be null");
+
+ this.screenFunction = screenFunction;
+ return this;
+ }
+
+ @Override
+ public PlaceholderCategory build() {
+ Validate.notNull(name, "`name` must not be null to build `ConfigCategory`");
+
+ MutableText concatenatedTooltip = Text.empty();
+ boolean first = true;
+ for (Text line : tooltipLines) {
+ if (!first) concatenatedTooltip.append("\n");
+ first = false;
+
+ concatenatedTooltip.append(line);
+ }
+
+ return new PlaceholderCategoryImpl(name, screenFunction, concatenatedTooltip);
+ }
+ }
}
diff --git a/src/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java b/src/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java
index 380929c..576c1bf 100644
--- a/src/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java
+++ b/src/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java
@@ -2,15 +2,23 @@ package dev.isxander.yacl.impl;
import com.google.common.collect.ImmutableList;
import dev.isxander.yacl.api.ConfigCategory;
+import dev.isxander.yacl.api.PlaceholderCategory;
import dev.isxander.yacl.api.YetAnotherConfigLib;
import dev.isxander.yacl.gui.YACLScreen;
import dev.isxander.yacl.impl.utils.YACLConstants;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
+import org.apache.commons.lang3.Validate;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
+@ApiStatus.Internal
public final class YetAnotherConfigLibImpl implements YetAnotherConfigLib {
private final Text title;
private final ImmutableList<ConfigCategory> categories;
@@ -56,29 +64,60 @@ public final class YetAnotherConfigLibImpl implements YetAnotherConfigLib {
return initConsumer;
}
- @Override
- public boolean equals(Object obj) {
- if (obj == this) return true;
- if (obj == null || obj.getClass() != this.getClass()) return false;
- var that = (YetAnotherConfigLibImpl) obj;
- return Objects.equals(this.title, that.title) &&
- Objects.equals(this.categories, that.categories) &&
- Objects.equals(this.saveFunction, that.saveFunction) &&
- Objects.equals(this.initConsumer, that.initConsumer);
- }
+ @ApiStatus.Internal
+ public static final class BuilderImpl implements YetAnotherConfigLib.Builder {
+ private Text title;
+ private final List<ConfigCategory> categories = new ArrayList<>();
+ private Runnable saveFunction = () -> {};
+ private Consumer<YACLScreen> initConsumer = screen -> {};
- @Override
- public int hashCode() {
- return Objects.hash(title, categories, saveFunction, initConsumer);
- }
+ @Override
+ public YetAnotherConfigLib.Builder title(@NotNull Text title) {
+ Validate.notNull(title, "`title` cannot be null");
- @Override
- public String toString() {
- return "YetAnotherConfigLibImpl[" +
- "title=" + title + ", " +
- "categories=" + categories + ", " +
- "saveFunction=" + saveFunction + ", " +
- "initConsumer=" + initConsumer + ']';
- }
+ this.title = title;
+ return this;
+ }
+
+ @Override
+ public YetAnotherConfigLib.Builder category(@NotNull ConfigCategory category) {
+ Validate.notNull(category, "`category` cannot be null");
+
+ this.categories.add(category);
+ return this;
+ }
+ @Override
+ public YetAnotherConfigLib.Builder categories(@NotNull Collection<? extends ConfigCategory> categories) {
+ Validate.notNull(categories, "`categories` cannot be null");
+
+ this.categories.addAll(categories);
+ return this;
+ }
+
+ @Override
+ public YetAnotherConfigLib.Builder save(@NotNull Runnable saveFunction) {
+ Validate.notNull(saveFunction, "`saveFunction` cannot be null");
+
+ this.saveFunction = saveFunction;
+ return this;
+ }
+
+ @Override
+ public YetAnotherConfigLib.Builder screenInit(@NotNull Consumer<YACLScreen> initConsumer) {
+ Validate.notNull(initConsumer, "`initConsumer` cannot be null");
+
+ this.initConsumer = initConsumer;
+ return this;
+ }
+
+ @Override
+ public YetAnotherConfigLib build() {
+ Validate.notNull(title, "`title must not be null to build `YetAnotherConfigLib`");
+ Validate.notEmpty(categories, "`categories` must not be empty to build `YetAnotherConfigLib`");
+ Validate.isTrue(!categories.stream().allMatch(category -> category instanceof PlaceholderCategory), "At least one regular category is required to build `YetAnotherConfigLib`");
+
+ return new YetAnotherConfigLibImpl(title, ImmutableList.copyOf(categories), saveFunction, initConsumer);
+ }
+ }
}
diff --git a/src/client/java/dev/isxander/yacl/mixin/client/SimpleOptionAccessor.java b/src/client/java/dev/isxander/yacl/mixin/client/SimpleOptionAccessor.java
index a48c808..4333f37 100644
--- a/src/client/java/dev/isxander/yacl/mixin/client/SimpleOptionAccessor.java
+++ b/src/client/java/dev/isxander/yacl/mixin/client/SimpleOptionAccessor.java
@@ -1,9 +1,11 @@
package dev.isxander.yacl.mixin.client;
import net.minecraft.client.option.SimpleOption;
+import org.jetbrains.annotations.ApiStatus;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
+@ApiStatus.Internal
@Mixin(SimpleOption.class)
public interface SimpleOptionAccessor<T> {
@Accessor