aboutsummaryrefslogtreecommitdiff
path: root/src/client/java/dev/isxander/yacl/api
diff options
context:
space:
mode:
authorXander <xander@isxander.dev>2022-12-11 19:31:56 +0000
committerGitHub <noreply@github.com>2022-12-11 19:31:56 +0000
commitdd65110f60aa3e32c2970863a06a7682520cce5e (patch)
tree44612c6aa85374efa639462592ae706fb93f2575 /src/client/java/dev/isxander/yacl/api
parentb890c2f0eec3627e552f1c6cfc846c8a55663243 (diff)
downloadYetAnotherConfigLib-dd65110f60aa3e32c2970863a06a7682520cce5e.tar.gz
YetAnotherConfigLib-dd65110f60aa3e32c2970863a06a7682520cce5e.tar.bz2
YetAnotherConfigLib-dd65110f60aa3e32c2970863a06a7682520cce5e.zip
[Feature] Lists (#40)
Diffstat (limited to 'src/client/java/dev/isxander/yacl/api')
-rw-r--r--src/client/java/dev/isxander/yacl/api/ConfigCategory.java9
-rw-r--r--src/client/java/dev/isxander/yacl/api/ListOption.java217
-rw-r--r--src/client/java/dev/isxander/yacl/api/ListOptionEntry.java23
-rw-r--r--src/client/java/dev/isxander/yacl/api/Option.java10
-rw-r--r--src/client/java/dev/isxander/yacl/api/OptionGroup.java8
-rw-r--r--src/client/java/dev/isxander/yacl/api/utils/OptionUtils.java14
6 files changed, 268 insertions, 13 deletions
diff --git a/src/client/java/dev/isxander/yacl/api/ConfigCategory.java b/src/client/java/dev/isxander/yacl/api/ConfigCategory.java
index e9755dd..19c7f72 100644
--- a/src/client/java/dev/isxander/yacl/api/ConfigCategory.java
+++ b/src/client/java/dev/isxander/yacl/api/ConfigCategory.java
@@ -3,6 +3,7 @@ package dev.isxander.yacl.api;
import com.google.common.collect.ImmutableList;
import dev.isxander.yacl.impl.ConfigCategoryImpl;
import dev.isxander.yacl.impl.OptionGroupImpl;
+import dev.isxander.yacl.impl.utils.YACLConstants;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import org.apache.commons.lang3.Validate;
@@ -76,6 +77,11 @@ public interface ConfigCategory {
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;
}
@@ -91,6 +97,9 @@ public interface ConfigCategory {
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;
}
diff --git a/src/client/java/dev/isxander/yacl/api/ListOption.java b/src/client/java/dev/isxander/yacl/api/ListOption.java
new file mode 100644
index 0000000..895898c
--- /dev/null
+++ b/src/client/java/dev/isxander/yacl/api/ListOption.java
@@ -0,0 +1,217 @@
+package dev.isxander.yacl.api;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import dev.isxander.yacl.impl.ListOptionImpl;
+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.*;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * A list option that takes form as an option group for UX.
+ * You add this option through {@link ConfigCategory.Builder#group(OptionGroup)}. Do NOT add as an option.
+ * Users can add remove and reshuffle a list type. You can use any controller you wish, there are no dedicated
+ * controllers for list types. List options do not manipulate your list but get and set the list with a
+ * regular binding for simplicity.
+ *
+ * You may apply option flags like a normal option and collapse like a normal group, it is a merge of them both.
+ * Methods in this interface marked with {@link ApiStatus.Internal} should not be used, and could be subject to
+ * change at any time
+ * @param <T>
+ */
+public interface ListOption<T> extends OptionGroup, Option<List<T>> {
+ @Override
+ @NotNull ImmutableList<ListOptionEntry<T>> options();
+
+ /**
+ * Class of the entry type
+ */
+ @NotNull Class<T> elementTypeClass();
+
+ @ApiStatus.Internal
+ ListOptionEntry<T> insertNewEntryToTop();
+
+ @ApiStatus.Internal
+ void insertEntry(int index, ListOptionEntry<?> entry);
+
+ @ApiStatus.Internal
+ int indexOf(ListOptionEntry<?> entry);
+
+ @ApiStatus.Internal
+ void removeEntry(ListOptionEntry<?> entry);
+
+ @ApiStatus.Internal
+ void addRefreshListener(Runnable changedListener);
+
+ static <T> Builder<T> createBuilder(Class<T> typeClass) {
+ return new Builder<>(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;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Sets the tooltip to be used by the list. It is displayed like a normal
+ * group when you hover over the name. Entries do not allow a tooltip.
+ * <p>
+ * Can be invoked twice to append more lines.
+ * No need to wrap the text yourself, the gui does this itself.
+ *
+ * @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;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Sets the controller for the option.
+ * This is how you interact and change the options.
+ *
+ * @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;
+ }
+
+ /**
+ * Sets the binding for the option.
+ * Used for default, getter and setter.
+ *
+ * @see Binding
+ */
+ public Builder<T> binding(@NotNull Binding<List<T>> binding) {
+ Validate.notNull(binding, "`binding` cannot be null");
+
+ this.binding = binding;
+ return this;
+ }
+
+ /**
+ * Sets the binding for the option.
+ * Shorthand of {@link Binding#generic(Object, Supplier, Consumer)}
+ *
+ * @param def default value of the option, used to reset
+ * @param getter should return the current value of the option
+ * @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;
+ }
+
+ /**
+ * Sets if the option can be configured
+ *
+ * @see Option#available()
+ */
+ public Builder<T> available(boolean available) {
+ this.available = available;
+ return this;
+ }
+
+ /**
+ * 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 if the group should be collapsed by default.
+ * If not set, it will not be collapsed by default.
+ *
+ * @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);
+ }
+ }
+}
diff --git a/src/client/java/dev/isxander/yacl/api/ListOptionEntry.java b/src/client/java/dev/isxander/yacl/api/ListOptionEntry.java
new file mode 100644
index 0000000..e0a3424
--- /dev/null
+++ b/src/client/java/dev/isxander/yacl/api/ListOptionEntry.java
@@ -0,0 +1,23 @@
+package dev.isxander.yacl.api;
+
+import com.google.common.collect.ImmutableSet;
+import org.jetbrains.annotations.NotNull;
+
+public interface ListOptionEntry<T> extends Option<T> {
+ ListOption<T> parentGroup();
+
+ @Override
+ default @NotNull Class<T> typeClass() {
+ return parentGroup().elementTypeClass();
+ }
+
+ @Override
+ default @NotNull ImmutableSet<OptionFlag> flags() {
+ return parentGroup().flags();
+ }
+
+ @Override
+ default boolean available() {
+ return parentGroup().available();
+ }
+}
diff --git a/src/client/java/dev/isxander/yacl/api/Option.java b/src/client/java/dev/isxander/yacl/api/Option.java
index 772c816..394723f 100644
--- a/src/client/java/dev/isxander/yacl/api/Option.java
+++ b/src/client/java/dev/isxander/yacl/api/Option.java
@@ -71,12 +71,6 @@ public interface Option<T> {
boolean changed();
/**
- * If true, modifying this option recommends a restart.
- */
- @Deprecated
- boolean requiresRestart();
-
- /**
* Value in the GUI, ready to set the actual bound value or be undone.
*/
@NotNull T pendingValue();
@@ -109,6 +103,10 @@ public interface Option<T> {
*/
boolean isPendingValueDefault();
+ default boolean canResetToDefault() {
+ return true;
+ }
+
/**
* Adds a listener for when the pending value changes
*/
diff --git a/src/client/java/dev/isxander/yacl/api/OptionGroup.java b/src/client/java/dev/isxander/yacl/api/OptionGroup.java
index 3364bdf..6cc6c7f 100644
--- a/src/client/java/dev/isxander/yacl/api/OptionGroup.java
+++ b/src/client/java/dev/isxander/yacl/api/OptionGroup.java
@@ -31,7 +31,7 @@ public interface OptionGroup {
/**
* List of all options in the group
*/
- @NotNull ImmutableList<Option<?>> options();
+ @NotNull ImmutableList<? extends Option<?>> options();
/**
* Dictates if the group should be collapsed by default.
@@ -96,6 +96,9 @@ public interface OptionGroup {
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;
}
@@ -109,6 +112,9 @@ public interface OptionGroup {
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;
}
diff --git a/src/client/java/dev/isxander/yacl/api/utils/OptionUtils.java b/src/client/java/dev/isxander/yacl/api/utils/OptionUtils.java
index ab46b5b..22032bd 100644
--- a/src/client/java/dev/isxander/yacl/api/utils/OptionUtils.java
+++ b/src/client/java/dev/isxander/yacl/api/utils/OptionUtils.java
@@ -1,9 +1,6 @@
package dev.isxander.yacl.api.utils;
-import dev.isxander.yacl.api.ConfigCategory;
-import dev.isxander.yacl.api.Option;
-import dev.isxander.yacl.api.OptionGroup;
-import dev.isxander.yacl.api.YetAnotherConfigLib;
+import dev.isxander.yacl.api.*;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -16,9 +13,14 @@ public class OptionUtils {
public static void consumeOptions(YetAnotherConfigLib yacl, Function<Option<?>, Boolean> consumer) {
for (ConfigCategory category : yacl.categories()) {
for (OptionGroup group : category.groups()) {
- for (Option<?> option : group.options()) {
- if (consumer.apply(option)) return;
+ if (group instanceof ListOption<?> list) {
+ if (consumer.apply(list)) return;
+ } else {
+ for (Option<?> option : group.options()) {
+ if (consumer.apply(option)) return;
+ }
}
+
}
}
}