aboutsummaryrefslogtreecommitdiff
path: root/common/src/main/java/dev/isxander/yacl3/api
diff options
context:
space:
mode:
Diffstat (limited to 'common/src/main/java/dev/isxander/yacl3/api')
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/Binding.java64
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/ButtonOption.java50
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/ConfigCategory.java94
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/Controller.java28
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/LabelOption.java41
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/ListOption.java146
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/ListOptionEntry.java18
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/NameableEnum.java10
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/Option.java223
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/OptionAddable.java19
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/OptionDescription.java161
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/OptionFlag.java23
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/OptionGroup.java90
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/PlaceholderCategory.java55
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/YetAnotherConfigLib.java107
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/BooleanControllerBuilder.java16
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/ColorControllerBuilder.java14
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/ControllerBuilder.java9
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/CyclingListControllerBuilder.java15
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/DoubleFieldControllerBuilder.java10
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/DoubleSliderControllerBuilder.java10
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/EnumControllerBuilder.java12
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/FloatFieldControllerBuilder.java10
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/FloatSliderControllerBuilder.java10
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/IntegerFieldControllerBuilder.java10
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/IntegerSliderControllerBuilder.java10
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/LongFieldControllerBuilder.java10
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/LongSliderControllerBuilder.java10
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/NumberFieldControllerBuilder.java7
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/SliderControllerBuilder.java6
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/StringControllerBuilder.java10
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/TickBoxControllerBuilder.java10
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/controller/ValueFormattableController.java9
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/utils/Dimension.java33
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/utils/MutableDimension.java11
-rw-r--r--common/src/main/java/dev/isxander/yacl3/api/utils/OptionUtils.java39
36 files changed, 1400 insertions, 0 deletions
diff --git a/common/src/main/java/dev/isxander/yacl3/api/Binding.java b/common/src/main/java/dev/isxander/yacl3/api/Binding.java
new file mode 100644
index 0000000..f41b78b
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/Binding.java
@@ -0,0 +1,64 @@
+package dev.isxander.yacl3.api;
+
+import dev.isxander.yacl3.impl.GenericBindingImpl;
+import dev.isxander.yacl3.mixin.OptionInstanceAccessor;
+import net.minecraft.client.OptionInstance;
+import org.apache.commons.lang3.Validate;
+
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Controls modifying the bound option.
+ * Provides the default value, a setter and a getter.
+ */
+public interface Binding<T> {
+ void setValue(T value);
+
+ T getValue();
+
+ T defaultValue();
+
+ /**
+ * Creates a generic binding.
+ *
+ * @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
+ */
+ static <T> Binding<T> generic(T def, Supplier<T> getter, Consumer<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");
+
+ return new GenericBindingImpl<>(def, getter, setter);
+ }
+
+ /**
+ * Creates a {@link Binding} for Minecraft's {@link OptionInstance}
+ */
+ static <T> Binding<T> minecraft(OptionInstance<T> minecraftOption) {
+ Validate.notNull(minecraftOption, "`minecraftOption` must not be null");
+
+ return new GenericBindingImpl<>(
+ ((OptionInstanceAccessor<T>) (Object) minecraftOption).getInitialValue(),
+ minecraftOption::get,
+ minecraftOption::set
+ );
+ }
+
+ /**
+ * Creates an immutable binding that has no default and cannot be modified.
+ *
+ * @param value the value for the binding
+ */
+ static <T> Binding<T> immutable(T value) {
+ Validate.notNull(value, "`value` must not be null");
+
+ return new GenericBindingImpl<>(
+ value,
+ () -> value,
+ changed -> {}
+ );
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/ButtonOption.java b/common/src/main/java/dev/isxander/yacl3/api/ButtonOption.java
new file mode 100644
index 0000000..943f9ac
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/ButtonOption.java
@@ -0,0 +1,50 @@
+package dev.isxander.yacl3.api;
+
+import dev.isxander.yacl3.gui.YACLScreen;
+import dev.isxander.yacl3.impl.ButtonOptionImpl;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public interface ButtonOption extends Option<BiConsumer<YACLScreen, ButtonOption>> {
+ /**
+ * Action to be executed upon button press
+ */
+ BiConsumer<YACLScreen, ButtonOption> action();
+
+ static Builder createBuilder() {
+ return new ButtonOptionImpl.BuilderImpl();
+ }
+
+ interface Builder {
+ /**
+ * Sets the name to be used by the option.
+ *
+ * @see Option#name()
+ */
+ Builder name(@NotNull Component name);
+
+ Builder description(@NotNull OptionDescription description);
+
+ Builder action(@NotNull BiConsumer<YACLScreen, ButtonOption> action);
+
+ /**
+ * Action to be executed upon button press
+ *
+ * @see ButtonOption#action()
+ */
+ @Deprecated
+ Builder action(@NotNull Consumer<YACLScreen> action);
+
+ /**
+ * Sets if the option can be configured
+ *
+ * @see Option#available()
+ */
+ Builder available(boolean available);
+
+ ButtonOption build();
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/ConfigCategory.java b/common/src/main/java/dev/isxander/yacl3/api/ConfigCategory.java
new file mode 100644
index 0000000..d47b3ce
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/ConfigCategory.java
@@ -0,0 +1,94 @@
+package dev.isxander.yacl3.api;
+
+import com.google.common.collect.ImmutableList;
+import dev.isxander.yacl3.impl.ConfigCategoryImpl;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * Separates {@link Option}s or {@link OptionGroup}s into multiple distinct sections.
+ * Served to a user as a button in the left column,
+ * upon pressing, the options list is filled with options contained within this category.
+ */
+public interface ConfigCategory {
+ /**
+ * Name of category, displayed as a button on the left column.
+ */
+ @NotNull Component name();
+
+ /**
+ * Gets every {@link OptionGroup} in this category.
+ */
+ @NotNull ImmutableList<OptionGroup> groups();
+
+ /**
+ * Tooltip (or description) of the category.
+ * Rendered on hover.
+ */
+ @NotNull Component tooltip();
+
+ /**
+ * Creates a builder to construct a {@link ConfigCategory}
+ */
+ static Builder createBuilder() {
+ return new ConfigCategoryImpl.BuilderImpl();
+ }
+
+ interface Builder extends OptionAddable {
+ /**
+ * Sets name of the category
+ *
+ * @see ConfigCategory#name()
+ */
+ Builder name(@NotNull Component name);
+
+ /**
+ * Adds an option to the root group of the category.
+ * To add to another group, use {@link Builder#group(OptionGroup)}.
+ * To construct an option, use {@link Option#createBuilder(Class)}
+ *
+ * @see ConfigCategory#groups()
+ * @see OptionGroup#isRoot()
+ */
+ @Override
+ Builder option(@NotNull Option<?> option);
+
+ /**
+ * Adds multiple options to the root group of the category.
+ * To add to another group, use {@link Builder#groups(Collection)}.
+ * To construct an option, use {@link Option#createBuilder(Class)}
+ *
+ * @see ConfigCategory#groups()
+ * @see OptionGroup#isRoot()
+ */
+ @Override
+ Builder options(@NotNull Collection<? extends 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()}
+ */
+ 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()}
+ */
+ Builder groups(@NotNull Collection<OptionGroup> groups);
+
+ /**
+ * Sets the tooltip to be used by the category.
+ * 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()}.
+ */
+ Builder tooltip(@NotNull Component... tooltips);
+
+ ConfigCategory build();
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/Controller.java b/common/src/main/java/dev/isxander/yacl3/api/Controller.java
new file mode 100644
index 0000000..25e4465
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/Controller.java
@@ -0,0 +1,28 @@
+package dev.isxander.yacl3.api;
+
+import dev.isxander.yacl3.api.utils.Dimension;
+import dev.isxander.yacl3.gui.AbstractWidget;
+import dev.isxander.yacl3.gui.YACLScreen;
+import net.minecraft.network.chat.Component;
+
+/**
+ * Provides a widget to control the option.
+ */
+public interface Controller<T> {
+ /**
+ * Gets the dedicated {@link Option} for this controller
+ */
+ Option<T> option();
+
+ /**
+ * Gets the formatted value based on {@link Option#pendingValue()}
+ */
+ Component formatValue();
+
+ /**
+ * Provides a widget to display
+ *
+ * @param screen parent screen
+ */
+ AbstractWidget provideWidget(YACLScreen screen, Dimension<Integer> widgetDimension);
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/LabelOption.java b/common/src/main/java/dev/isxander/yacl3/api/LabelOption.java
new file mode 100644
index 0000000..a5f015e
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/LabelOption.java
@@ -0,0 +1,41 @@
+package dev.isxander.yacl3.api;
+
+import dev.isxander.yacl3.impl.LabelOptionImpl;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * A label option is an easier way of creating a label with a {@link dev.isxander.yacl3.gui.controllers.LabelController}.
+ * This option is immutable and cannot be disabled. Tooltips are supported through
+ * {@link Component} styling.
+ */
+public interface LabelOption extends Option<Component> {
+ @NotNull Component label();
+
+ /**
+ * Creates a new label option with the given label, skipping a builder for ease.
+ */
+ static LabelOption create(@NotNull Component label) {
+ return new LabelOptionImpl(label);
+ }
+
+ static Builder createBuilder() {
+ return new LabelOptionImpl.BuilderImpl();
+ }
+
+ interface Builder {
+ /**
+ * Appends a line to the label
+ */
+ Builder line(@NotNull Component line);
+
+ /**
+ * Appends multiple lines to the label
+ */
+ Builder lines(@NotNull Collection<? extends Component> lines);
+
+ LabelOption build();
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/ListOption.java b/common/src/main/java/dev/isxander/yacl3/api/ListOption.java
new file mode 100644
index 0000000..8094ee7
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/ListOption.java
@@ -0,0 +1,146 @@
+package dev.isxander.yacl3.api;
+
+import com.google.common.collect.ImmutableList;
+import dev.isxander.yacl3.api.controller.ControllerBuilder;
+import dev.isxander.yacl3.impl.ListOptionImpl;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+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();
+
+ @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() {
+ return new ListOptionImpl.BuilderImpl<>();
+ }
+
+ @Deprecated
+ static <T> Builder<T> createBuilder(Class<T> typeClass) {
+ return createBuilder();
+ }
+
+ interface Builder<T> {
+ /**
+ * Sets name of the list, for UX purposes, a name should always be given,
+ * but isn't enforced.
+ *
+ * @see ListOption#name()
+ */
+ Builder<T> name(@NotNull Component name);
+
+ Builder<T> description(@NotNull OptionDescription description);
+
+ /**
+ * Sets the value that is used when creating new entries
+ */
+ Builder<T> initial(@NotNull T initialValue);
+
+ Builder<T> controller(@NotNull Function<Option<T>, ControllerBuilder<T>> controller);
+
+ /**
+ * Sets the controller for the option.
+ * This is how you interact and change the options.
+ *
+ * @see dev.isxander.yacl3.gui.controllers
+ */
+ Builder<T> customController(@NotNull Function<ListOptionEntry<T>, Controller<T>> control);
+
+ /**
+ * Sets the binding for the option.
+ * Used for default, getter and setter.
+ *
+ * @see Binding
+ */
+ Builder<T> binding(@NotNull Binding<List<T>> binding);
+
+ /**
+ * 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
+ */
+ 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()
+ */
+ Builder<T> available(boolean available);
+
+ /**
+ * Adds a flag to the option.
+ * Upon applying changes, all flags are executed.
+ * {@link Option#flags()}
+ */
+ Builder<T> flag(@NotNull OptionFlag... flag);
+
+ /**
+ * Adds a flag to the option.
+ * Upon applying changes, all flags are executed.
+ * {@link Option#flags()}
+ */
+ Builder<T> flags(@NotNull Collection<OptionFlag> flags);
+
+ /**
+ * Dictates if the group should be collapsed by default.
+ * If not set, it will not be collapsed by default.
+ *
+ * @see OptionGroup#collapsed()
+ */
+ Builder<T> collapsed(boolean collapsible);
+
+ /**
+ * Adds a listener to the option. Invoked upon changing any of the list's entries.
+ *
+ * @see Option#addListener(BiConsumer)
+ */
+ ListOption.Builder<T> listener(@NotNull BiConsumer<Option<List<T>>, List<T>> listener);
+
+ /**
+ * Adds multiple listeners to the option. Invoked upon changing of any of the list's entries.
+ *
+ * @see Option#addListener(BiConsumer)
+ */
+ ListOption.Builder<T> listeners(@NotNull Collection<BiConsumer<Option<List<T>>, List<T>>> listeners);
+
+ ListOption<T> build();
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/ListOptionEntry.java b/common/src/main/java/dev/isxander/yacl3/api/ListOptionEntry.java
new file mode 100644
index 0000000..23ec657
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/ListOptionEntry.java
@@ -0,0 +1,18 @@
+package dev.isxander.yacl3.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 ImmutableSet<OptionFlag> flags() {
+ return parentGroup().flags();
+ }
+
+ @Override
+ default boolean available() {
+ return parentGroup().available();
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/NameableEnum.java b/common/src/main/java/dev/isxander/yacl3/api/NameableEnum.java
new file mode 100644
index 0000000..5a50207
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/NameableEnum.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl3.api;
+
+import net.minecraft.network.chat.Component;
+
+/**
+ * Used for the default value formatter of {@link dev.isxander.yacl3.gui.controllers.cycling.EnumController}
+ */
+public interface NameableEnum {
+ Component getDisplayName();
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/Option.java b/common/src/main/java/dev/isxander/yacl3/api/Option.java
new file mode 100644
index 0000000..31b7756
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/Option.java
@@ -0,0 +1,223 @@
+package dev.isxander.yacl3.api;
+
+import com.google.common.collect.ImmutableSet;
+import dev.isxander.yacl3.api.controller.ControllerBuilder;
+import dev.isxander.yacl3.impl.OptionImpl;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public interface Option<T> {
+ /**
+ * Name of the option
+ */
+ @NotNull Component name();
+
+ @NotNull OptionDescription description();
+
+ /**
+ * Tooltip (or description) of the option.
+ * Rendered on hover.
+ */
+ @Deprecated
+ @NotNull Component tooltip();
+
+ /**
+ * Widget provider for a type of option.
+ *
+ * @see dev.isxander.yacl3.gui.controllers
+ */
+ @NotNull Controller<T> controller();
+
+ /**
+ * Binding for the option.
+ * Controls setting, getting and default value.
+ *
+ * @see Binding
+ */
+ @NotNull Binding<T> binding();
+
+ /**
+ * If the option can be configured
+ */
+ boolean available();
+
+ /**
+ * Sets if the option can be configured after being built
+ *
+ * @see Option#available()
+ */
+ void setAvailable(boolean available);
+
+ /**
+ * 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();
+
+ /**
+ * Value in the GUI, ready to set the actual bound value or be undone.
+ */
+ @NotNull T pendingValue();
+
+ /**
+ * Sets the pending value
+ */
+ void requestSet(T value);
+
+ /**
+ * Applies the pending value to the bound value.
+ * Cannot be undone.
+ *
+ * @return if there were changes to apply {@link Option#changed()}
+ */
+ boolean applyValue();
+
+ /**
+ * Sets the pending value to the bound value.
+ */
+ void forgetPendingValue();
+
+ /**
+ * Sets the pending value to the default bound value.
+ */
+ void requestSetDefault();
+
+ /**
+ * Checks if the current pending value is equal to its default value
+ */
+ boolean isPendingValueDefault();
+
+ default boolean canResetToDefault() {
+ return true;
+ }
+
+ /**
+ * Adds a listener for when the pending value changes
+ */
+ void addListener(BiConsumer<Option<T>, T> changedListener);
+
+ static <T> Builder<T> createBuilder() {
+ return new OptionImpl.BuilderImpl<>();
+ }
+
+ /**
+ * Creates a builder to construct an {@link Option}
+ *
+ * @param <T> type of the option's value
+ * @param typeClass used to capture the type
+ */
+ @Deprecated
+ static <T> Builder<T> createBuilder(Class<T> typeClass) {
+ return createBuilder();
+ }
+
+ interface Builder<T> {
+ /**
+ * Sets the name to be used by the option.
+ *
+ * @see Option#name()
+ */
+ Builder<T> name(@NotNull Component name);
+
+ /**
+ * Sets the description to be used by the option.
+ * @see OptionDescription
+ * @param description the static description.
+ * @return this builder
+ */
+ Builder<T> description(@NotNull OptionDescription description);
+
+ /**
+ * Sets the function to get the description by the option's current value.
+ *
+ * @see OptionDescription
+ * @param descriptionFunction the function to get the description by the option's current value.
+ * @return this builder
+ */
+ Builder<T> description(@NotNull Function<T, OptionDescription> descriptionFunction);
+
+ Builder<T> controller(@NotNull Function<Option<T>, ControllerBuilder<T>> controllerBuilder);
+
+ /**
+ * Sets the controller for the option.
+ * This is how you interact and change the options.
+ *
+ * @see dev.isxander.yacl3.gui.controllers
+ */
+ Builder<T> customController(@NotNull Function<Option<T>, Controller<T>> control);
+
+ /**
+ * Sets the binding for the option.
+ * Used for default, getter and setter.
+ *
+ * @see Binding
+ */
+ Builder<T> binding(@NotNull Binding<T> binding);
+
+ /**
+ * 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
+ */
+ 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()
+ */
+ Builder<T> available(boolean available);
+
+ /**
+ * Adds a flag to the option.
+ * Upon applying changes, all flags are executed.
+ * {@link Option#flags()}
+ */
+ Builder<T> flag(@NotNull OptionFlag... flag);
+
+ /**
+ * Adds a flag to the option.
+ * Upon applying changes, all flags are executed.
+ * {@link Option#flags()}
+ */
+ Builder<T> flags(@NotNull Collection<? extends OptionFlag> flags);
+
+ /**
+ * Instantly invokes the binder's setter when modified in the GUI.
+ * Prevents the user from undoing the change
+ * <p>
+ * Does not support {@link Option#flags()}!
+ */
+ Builder<T> instant(boolean instant);
+
+ /**
+ * Adds a listener to the option. Invoked upon changing the pending value.
+ *
+ * @see Option#addListener(BiConsumer)
+ */
+ 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)
+ */
+ Builder<T> listeners(@NotNull Collection<BiConsumer<Option<T>, T>> listeners);
+
+ Option<T> build();
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/OptionAddable.java b/common/src/main/java/dev/isxander/yacl3/api/OptionAddable.java
new file mode 100644
index 0000000..97ab352
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/OptionAddable.java
@@ -0,0 +1,19 @@
+package dev.isxander.yacl3.api;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+public interface OptionAddable {
+ /**
+ * Adds an option to an abstract builder.
+ * To construct an option, use {@link Option#createBuilder(Class)}
+ */
+ OptionAddable option(@NotNull Option<?> option);
+
+ /**
+ * Adds multiple options to an abstract builder.
+ * To construct an option, use {@link Option#createBuilder(Class)}
+ */
+ OptionAddable options(@NotNull Collection<? extends Option<?>> options);
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/OptionDescription.java b/common/src/main/java/dev/isxander/yacl3/api/OptionDescription.java
new file mode 100644
index 0000000..40f1d68
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/OptionDescription.java
@@ -0,0 +1,161 @@
+package dev.isxander.yacl3.api;
+
+import dev.isxander.yacl3.gui.ImageRenderer;
+import dev.isxander.yacl3.impl.OptionDescriptionImpl;
+import net.minecraft.network.chat.CommonComponents;
+import net.minecraft.network.chat.Component;
+import net.minecraft.resources.ResourceLocation;
+
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Supplier;
+
+/**
+ * Provides all information for the description panel in the GUI.
+ * This provides no functional benefit, and is purely for UX.
+ */
+public interface OptionDescription {
+ /**
+ * The description of the option, this is automatically wrapped and supports all styling,
+ * including {@link net.minecraft.network.chat.ClickEvent}s and {@link net.minecraft.network.chat.HoverEvent}s.
+ * @return The description of the option, with all lines merged with \n.
+ */
+ Component text();
+
+ /**
+ * The image to display with the description. If the Optional is empty, no image has been provided.
+ * Usually, the image renderers are constructed asynchronously, so this method returns a {@link CompletableFuture}.
+ * <p>
+ * Image renderers are cached throughout the whole lifecycle of the game, and should not be generated more than once
+ * per image. See {@link ImageRenderer#getOrMakeAsync(ResourceLocation, Supplier)} for implementation details.
+ */
+ CompletableFuture<Optional<ImageRenderer>> image();
+
+ /**
+ * @return a new builder for an {@link OptionDescription}.
+ */
+ static Builder createBuilder() {
+ return new OptionDescriptionImpl.BuilderImpl();
+ }
+
+ static OptionDescription of(Component... description) {
+ return createBuilder().text(description).build();
+ }
+
+ OptionDescription EMPTY = new OptionDescriptionImpl(CommonComponents.EMPTY, CompletableFuture.completedFuture(Optional.empty()));
+
+ interface Builder {
+ /**
+ * Appends lines to the main description of the option. This can be called multiple times.
+ * On {@link Builder#build()}, the lines are merged with \n.
+ * @see OptionDescription#text()
+ *
+ * @param description the lines to append to the description.
+ * @return this builder
+ */
+ Builder text(Component... description);
+
+ /**
+ * Appends lines to the main description of the option. This can be called multiple times.
+ * On {@link Builder#build()}, the lines are merged with \n.
+ * @see OptionDescription#text()
+ *
+ * @param lines the lines to append to the description.
+ * @return this builder
+ */
+ Builder text(Collection<? extends Component> lines);
+
+ /**
+ * Sets a static image to display with the description. This is backed by a regular minecraft resource
+ * in your mod's /assets folder.
+ *
+ * @param image the location of the image to display from the resource manager
+ * @param width the width of the texture
+ * @param height the height of the texture
+ * @return this builder
+ */
+ Builder image(ResourceLocation image, int width, int height);
+
+ /**
+ * Sets a static image to display with the description. This is backed by a regular minecraft resource
+ * in your mod's /assets folder. This overload method allows you to specify a subsection of the texture to render.
+ *
+ * @param image the location of the image to display from the resource manager
+ * @param u the u coordinate
+ * @param v the v coordinate
+ * @param width the width of the subsection
+ * @param height the height of the subsection
+ * @param textureWidth the width of the whole texture file
+ * @param textureHeight the height of whole texture file
+ * @return this builder
+ */
+ Builder image(ResourceLocation image, float u, float v, int width, int height, int textureWidth, int textureHeight);
+
+ /**
+ * Sets a static image to display with the description. This is backed by a file on disk.
+ * The width and height is automatically determined from the image processing.
+ *
+ * @param path the absolute path to the image file
+ * @param uniqueLocation the unique identifier for the image, used for caching and resource manager registrar
+ * @return this builder
+ */
+ Builder image(Path path, ResourceLocation uniqueLocation);
+
+ /**
+ * Sets a static OR ANIMATED webP image to display with the description. This is backed by a regular minecraft resource
+ * in your mod's /assets folder.
+ *
+ * @param image the location of the image to display from the resource manager
+ * @return this builder
+ */
+ Builder webpImage(ResourceLocation image);
+
+ /**
+ * Sets a static OR ANIMATED webP image to display with the description. This is backed by a file on disk.
+ * The width and height is automatically determined from the image processing.
+ *
+ * @param path the absolute path to the image file
+ * @param uniqueLocation the unique identifier for the image, used for caching and resource manager registrar
+ * @return this builder
+ */
+ Builder webpImage(Path path, ResourceLocation uniqueLocation);
+
+ /**
+ * Sets a custom image renderer to display with the description.
+ * This is useful for rendering other abstract things relevant to your mod.
+ * <p>
+ * However, <strong>THIS IS NOT API SAFE!</strong> As part of the gui package, things
+ * may change that could break compatibility with future versions of YACL.
+ * A helpful utility (that is also not API safe) is {@link ImageRenderer#getOrMakeAsync(ResourceLocation, Supplier)}
+ * which will cache the image renderer for the whole game lifecycle and construct it asynchronously to the render thread.
+ * @param image the image renderer to display
+ * @return this builder
+ */
+ Builder customImage(CompletableFuture<Optional<ImageRenderer>> image);
+
+ /**
+ * Sets an animated GIF image to display with the description. This is backed by a regular minecraft resource
+ * in your mod's /assets folder.
+ *
+ * @param image the location of the image to display from the resource manager
+ * @return this builder
+ */
+ @Deprecated
+ Builder gifImage(ResourceLocation image);
+
+ /**
+ * Sets an animated GIF image to display with the description. This is backed by a file on disk.
+ * The width and height is automatically determined from the image processing.
+ *
+ * @param path the absolute path to the image file
+ * @param uniqueLocation the unique identifier for the image, used for caching and resource manager registrar
+ * @return this builder
+ */
+ @Deprecated
+ Builder gifImage(Path path, ResourceLocation uniqueLocation);
+
+ OptionDescription build();
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/OptionFlag.java b/common/src/main/java/dev/isxander/yacl3/api/OptionFlag.java
new file mode 100644
index 0000000..6f35495
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/OptionFlag.java
@@ -0,0 +1,23 @@
+package dev.isxander.yacl3.api;
+
+import dev.isxander.yacl3.gui.RequireRestartScreen;
+import net.minecraft.client.Minecraft;
+
+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<Minecraft> {
+ /** Warns the user that a game restart is required for the changes to take effect */
+ OptionFlag GAME_RESTART = client -> client.setScreen(new RequireRestartScreen(client.screen));
+
+ /** Reloads chunks upon applying (F3+A) */
+ OptionFlag RELOAD_CHUNKS = client -> client.levelRenderer.allChanged();
+
+ OptionFlag WORLD_RENDER_UPDATE = client -> client.levelRenderer.needsUpdate();
+
+ OptionFlag ASSET_RELOAD = Minecraft::delayTextureReload;
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/OptionGroup.java b/common/src/main/java/dev/isxander/yacl3/api/OptionGroup.java
new file mode 100644
index 0000000..ff31966
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/OptionGroup.java
@@ -0,0 +1,90 @@
+package dev.isxander.yacl3.api;
+
+import com.google.common.collect.ImmutableList;
+import dev.isxander.yacl3.impl.OptionGroupImpl;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * Serves as a separator between multiple chunks of options
+ * that may be too similar or too few to be placed in a separate {@link ConfigCategory}.
+ * Or maybe you just want your config to feel less dense.
+ */
+public interface OptionGroup {
+ /**
+ * Name of the option group, displayed as a separator in the option lists.
+ * Can be empty.
+ */
+ Component name();
+
+ OptionDescription description();
+
+ /**
+ * Tooltip displayed on hover.
+ */
+ @Deprecated
+ Component tooltip();
+
+ /**
+ * List of all options in the group
+ */
+ @NotNull ImmutableList<? extends Option<?>> options();
+
+ /**
+ * Dictates if the group should be collapsed by default.
+ */
+ boolean collapsed();
+
+ /**
+ * Always false when using the {@link Builder}
+ * used to not render the separator if true
+ */
+ boolean isRoot();
+
+ /**
+ * Creates a builder to construct a {@link OptionGroup}
+ */
+ static Builder createBuilder() {
+ return new OptionGroupImpl.BuilderImpl();
+ }
+
+ interface Builder extends OptionAddable {
+ /**
+ * Sets name of the group, can be {@link Component#empty()} to just separate options, like sodium.
+ *
+ * @see OptionGroup#name()
+ */
+ Builder name(@NotNull Component name);
+
+ Builder description(@NotNull OptionDescription description);
+
+ /**
+ * Adds an option to group.
+ * To construct an option, use {@link Option#createBuilder(Class)}
+ *
+ * @see OptionGroup#options()
+ */
+ @Override
+ Builder option(@NotNull Option<?> option);
+
+ /**
+ * Adds multiple options to group.
+ * To construct an option, use {@link Option#createBuilder(Class)}
+ *
+ * @see OptionGroup#options()
+ */
+ @Override
+ Builder options(@NotNull Collection<? extends Option<?>> options);
+
+ /**
+ * Dictates if the group should be collapsed by default
+ *
+ * @see OptionGroup#collapsed()
+ */
+ Builder collapsed(boolean collapsible);
+
+ OptionGroup build();
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/PlaceholderCategory.java b/common/src/main/java/dev/isxander/yacl3/api/PlaceholderCategory.java
new file mode 100644
index 0000000..6eb82c5
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/PlaceholderCategory.java
@@ -0,0 +1,55 @@
+package dev.isxander.yacl3.api;
+
+import dev.isxander.yacl3.gui.YACLScreen;
+import dev.isxander.yacl3.impl.PlaceholderCategoryImpl;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.screens.Screen;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.function.BiFunction;
+
+/**
+ * A placeholder category that actually just opens another screen,
+ * instead of displaying options.
+ * <p>
+ * Use of this is discouraged, as it is not very user-friendly and navigating to a placeholder
+ * tab that opens another screen is not very intuitive, making keyboard navigation impossible.
+ */
+public interface PlaceholderCategory extends ConfigCategory {
+ /**
+ * Function to create a screen to open upon changing to this category
+ */
+ BiFunction<Minecraft, YACLScreen, Screen> screen();
+
+ static Builder createBuilder() {
+ return new PlaceholderCategoryImpl.BuilderImpl();
+ }
+
+ interface Builder {
+ /**
+ * Sets name of the category
+ *
+ * @see ConfigCategory#name()
+ */
+ Builder name(@NotNull Component name);
+
+ /**
+ * Sets the tooltip to be used by the category.
+ * Can be invoked twice to append more lines.
+ * No need to wrap the Component yourself, the gui does this itself.
+ *
+ * @param tooltips Component lines - merged with a new-line on {@link dev.isxander.yacl3.api.PlaceholderCategory.Builder#build()}.
+ */
+ Builder tooltip(@NotNull Component... tooltips);
+
+ /**
+ * Screen to open upon selecting this category
+ *
+ * @see PlaceholderCategory#screen()
+ */
+ Builder screen(@NotNull BiFunction<Minecraft, YACLScreen, Screen> screenFunction);
+
+ PlaceholderCategory build();
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/YetAnotherConfigLib.java b/common/src/main/java/dev/isxander/yacl3/api/YetAnotherConfigLib.java
new file mode 100644
index 0000000..15ce5bc
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/YetAnotherConfigLib.java
@@ -0,0 +1,107 @@
+package dev.isxander.yacl3.api;
+
+import com.google.common.collect.ImmutableList;
+import dev.isxander.yacl3.config.ConfigInstance;
+import dev.isxander.yacl3.gui.YACLScreen;
+import dev.isxander.yacl3.impl.YetAnotherConfigLibImpl;
+import net.minecraft.client.gui.screens.Screen;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.function.Consumer;
+
+/**
+ * Main class of the mod.
+ * Contains all data and used to provide a {@link Screen}
+ */
+public interface YetAnotherConfigLib {
+ /**
+ * Title of the GUI. Only used for Minecraft narration.
+ */
+ Component title();
+
+ /**
+ * Gets all config categories.
+ */
+ ImmutableList<ConfigCategory> categories();
+
+ /**
+ * Ran when changes are saved. Can be used to save config to a file etc.
+ */
+ Runnable saveFunction();
+
+ /**
+ * Ran every time the YACL screen initialises. Can be paired with FAPI to add custom widgets.
+ */
+ Consumer<YACLScreen> initConsumer();
+
+ /**
+ * Generates a Screen to display based on this instance.
+ *
+ * @param parent parent screen to open once closed
+ */
+ Screen generateScreen(@Nullable Screen parent);
+
+ /**
+ * Creates a builder to construct YACL
+ */
+ static Builder createBuilder() {
+ return new YetAnotherConfigLibImpl.BuilderImpl();
+ }
+
+ /**
+ * Creates an instance using a {@link ConfigInstance} which autofills the save() builder method.
+ * This also takes an easy functional interface that provides defaults and config to help build YACL bindings.
+ */
+ static <T> YetAnotherConfigLib create(ConfigInstance<T> configInstance, ConfigBackedBuilder<T> builder) {
+ return builder.build(configInstance.getDefaults(), configInstance.getConfig(), createBuilder().save(configInstance::save)).build();
+ }
+
+ interface Builder {
+ /**
+ * Sets title of GUI for Minecraft narration
+ *
+ * @see YetAnotherConfigLib#title()
+ */
+ Builder title(@NotNull Component title);
+
+ /**
+ * Adds a new category.
+ * To create a category you need to use {@link ConfigCategory#createBuilder()}
+ *
+ * @see YetAnotherConfigLib#categories()
+ */
+ Builder category(@NotNull ConfigCategory category);
+
+ /**
+ * Adds multiple categories at once.
+ * To create a category you need to use {@link ConfigCategory#createBuilder()}
+ *
+ * @see YetAnotherConfigLib#categories()
+ */
+ Builder categories(@NotNull Collection<? extends ConfigCategory> categories);
+
+ /**
+ * Used to define a save function for when user clicks the Save Changes button
+ *
+ * @see YetAnotherConfigLib#saveFunction()
+ */
+ Builder save(@NotNull Runnable saveFunction);
+
+ /**
+ * Defines a consumer that is accepted every time the YACL screen initialises
+ *
+ * @see YetAnotherConfigLib#initConsumer()
+ */
+ Builder screenInit(@NotNull Consumer<YACLScreen> initConsumer);
+
+ YetAnotherConfigLib build();
+ }
+
+ @FunctionalInterface
+ interface ConfigBackedBuilder<T> {
+ YetAnotherConfigLib.Builder build(T defaults, T config, YetAnotherConfigLib.Builder builder);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/BooleanControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/BooleanControllerBuilder.java
new file mode 100644
index 0000000..88f9a77
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/BooleanControllerBuilder.java
@@ -0,0 +1,16 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.BooleanControllerBuilderImpl;
+
+public interface BooleanControllerBuilder extends ValueFormattableController<Boolean, BooleanControllerBuilder> {
+ BooleanControllerBuilder coloured(boolean coloured);
+
+ BooleanControllerBuilder onOffFormatter();
+ BooleanControllerBuilder yesNoFormatter();
+ BooleanControllerBuilder trueFalseFormatter();
+
+ static BooleanControllerBuilder create(Option<Boolean> option) {
+ return new BooleanControllerBuilderImpl(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/ColorControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/ColorControllerBuilder.java
new file mode 100644
index 0000000..8e442ff
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/ColorControllerBuilder.java
@@ -0,0 +1,14 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.ColorControllerBuilderImpl;
+
+import java.awt.Color;
+
+public interface ColorControllerBuilder extends ControllerBuilder<Color> {
+ ColorControllerBuilder allowAlpha(boolean allowAlpha);
+
+ static ColorControllerBuilder create(Option<Color> option) {
+ return new ColorControllerBuilderImpl(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/ControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/ControllerBuilder.java
new file mode 100644
index 0000000..0cd3a55
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/ControllerBuilder.java
@@ -0,0 +1,9 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Controller;
+import org.jetbrains.annotations.ApiStatus;
+
+public interface ControllerBuilder<T> {
+ @ApiStatus.Internal
+ Controller<T> build();
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/CyclingListControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/CyclingListControllerBuilder.java
new file mode 100644
index 0000000..8c9ea91
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/CyclingListControllerBuilder.java
@@ -0,0 +1,15 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.CyclingListControllerBuilderImpl;
+
+public interface CyclingListControllerBuilder<T> extends ValueFormattableController<T, CyclingListControllerBuilder<T>> {
+ @SuppressWarnings("unchecked")
+ CyclingListControllerBuilder<T> values(T... values);
+
+ CyclingListControllerBuilder<T> values(Iterable<? extends T> values);
+
+ static <T> CyclingListControllerBuilder<T> create(Option<T> option) {
+ return new CyclingListControllerBuilderImpl<>(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/DoubleFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/DoubleFieldControllerBuilder.java
new file mode 100644
index 0000000..db4af94
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/DoubleFieldControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.DoubleFieldControllerBuilderImpl;
+
+public interface DoubleFieldControllerBuilder extends NumberFieldControllerBuilder<Double, DoubleFieldControllerBuilder> {
+ static DoubleFieldControllerBuilder create(Option<Double> option) {
+ return new DoubleFieldControllerBuilderImpl(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/DoubleSliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/DoubleSliderControllerBuilder.java
new file mode 100644
index 0000000..7e4b6f9
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/DoubleSliderControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.DoubleSliderControllerBuilderImpl;
+
+public interface DoubleSliderControllerBuilder extends SliderControllerBuilder<Double, DoubleSliderControllerBuilder> {
+ static DoubleSliderControllerBuilder create(Option<Double> option) {
+ return new DoubleSliderControllerBuilderImpl(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/EnumControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/EnumControllerBuilder.java
new file mode 100644
index 0000000..decb8f9
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/EnumControllerBuilder.java
@@ -0,0 +1,12 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.EnumControllerBuilderImpl;
+
+public interface EnumControllerBuilder<T extends Enum<T>> extends ValueFormattableController<T, EnumControllerBuilder<T>> {
+ EnumControllerBuilder<T> enumClass(Class<T> enumClass);
+
+ static <T extends Enum<T>> EnumControllerBuilder<T> create(Option<T> option) {
+ return new EnumControllerBuilderImpl<>(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/FloatFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/FloatFieldControllerBuilder.java
new file mode 100644
index 0000000..de81837
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/FloatFieldControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.FloatFieldControllerBuilderImpl;
+
+public interface FloatFieldControllerBuilder extends NumberFieldControllerBuilder<Float, FloatFieldControllerBuilder> {
+ static FloatFieldControllerBuilder create(Option<Float> option) {
+ return new FloatFieldControllerBuilderImpl(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/FloatSliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/FloatSliderControllerBuilder.java
new file mode 100644
index 0000000..2a04dde
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/FloatSliderControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.FloatSliderControllerBuilderImpl;
+
+public interface FloatSliderControllerBuilder extends SliderControllerBuilder<Float, FloatSliderControllerBuilder> {
+ static FloatSliderControllerBuilder create(Option<Float> option) {
+ return new FloatSliderControllerBuilderImpl(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/IntegerFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/IntegerFieldControllerBuilder.java
new file mode 100644
index 0000000..1e31fac
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/IntegerFieldControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.IntegerFieldControllerBuilderImpl;
+
+public interface IntegerFieldControllerBuilder extends NumberFieldControllerBuilder<Integer, IntegerFieldControllerBuilder> {
+ static IntegerFieldControllerBuilder create(Option<Integer> option) {
+ return new IntegerFieldControllerBuilderImpl(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/IntegerSliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/IntegerSliderControllerBuilder.java
new file mode 100644
index 0000000..11e089a
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/IntegerSliderControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.IntegerSliderControllerBuilderImpl;
+
+public interface IntegerSliderControllerBuilder extends SliderControllerBuilder<Integer, IntegerSliderControllerBuilder> {
+ static IntegerSliderControllerBuilder create(Option<Integer> option) {
+ return new IntegerSliderControllerBuilderImpl(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/LongFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/LongFieldControllerBuilder.java
new file mode 100644
index 0000000..c53b464
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/LongFieldControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.LongFieldControllerBuilderImpl;
+
+public interface LongFieldControllerBuilder extends NumberFieldControllerBuilder<Long, LongFieldControllerBuilder> {
+ static LongFieldControllerBuilder create(Option<Long> option) {
+ return new LongFieldControllerBuilderImpl(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/LongSliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/LongSliderControllerBuilder.java
new file mode 100644
index 0000000..fc09423
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/LongSliderControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.LongSliderControllerBuilderImpl;
+
+public interface LongSliderControllerBuilder extends SliderControllerBuilder<Long, LongSliderControllerBuilder> {
+ static LongSliderControllerBuilder create(Option<Long> option) {
+ return new LongSliderControllerBuilderImpl(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/NumberFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/NumberFieldControllerBuilder.java
new file mode 100644
index 0000000..b5cfa1f
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/NumberFieldControllerBuilder.java
@@ -0,0 +1,7 @@
+package dev.isxander.yacl3.api.controller;
+
+public interface NumberFieldControllerBuilder<T extends Number, B extends NumberFieldControllerBuilder<T, B>> extends ValueFormattableController<T, B> {
+ B min(T min);
+ B max(T max);
+ B range(T min, T max);
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/SliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/SliderControllerBuilder.java
new file mode 100644
index 0000000..2fb3fec
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/SliderControllerBuilder.java
@@ -0,0 +1,6 @@
+package dev.isxander.yacl3.api.controller;
+
+public interface SliderControllerBuilder<T extends Number, B extends SliderControllerBuilder<T, B>> extends ValueFormattableController<T, B> {
+ B range(T min, T max);
+ B step(T step);
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/StringControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/StringControllerBuilder.java
new file mode 100644
index 0000000..5e2f8c6
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/StringControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.StringControllerBuilderImpl;
+
+public interface StringControllerBuilder extends ControllerBuilder<String> {
+ static StringControllerBuilder create(Option<String> option) {
+ return new StringControllerBuilderImpl(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/TickBoxControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/TickBoxControllerBuilder.java
new file mode 100644
index 0000000..71a2762
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/TickBoxControllerBuilder.java
@@ -0,0 +1,10 @@
+package dev.isxander.yacl3.api.controller;
+
+import dev.isxander.yacl3.api.Option;
+import dev.isxander.yacl3.impl.controller.TickBoxControllerBuilderImpl;
+
+public interface TickBoxControllerBuilder extends ControllerBuilder<Boolean> {
+ static TickBoxControllerBuilder create(Option<Boolean> option) {
+ return new TickBoxControllerBuilderImpl(option);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/ValueFormattableController.java b/common/src/main/java/dev/isxander/yacl3/api/controller/ValueFormattableController.java
new file mode 100644
index 0000000..af55e55
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/controller/ValueFormattableController.java
@@ -0,0 +1,9 @@
+package dev.isxander.yacl3.api.controller;
+
+import net.minecraft.network.chat.Component;
+
+import java.util.function.Function;
+
+public interface ValueFormattableController<T, B extends ValueFormattableController<T, B>> extends ControllerBuilder<T> {
+ B valueFormatter(Function<T, Component> formatter);
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/utils/Dimension.java b/common/src/main/java/dev/isxander/yacl3/api/utils/Dimension.java
new file mode 100644
index 0000000..ec09238
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/utils/Dimension.java
@@ -0,0 +1,33 @@
+package dev.isxander.yacl3.api.utils;
+
+import dev.isxander.yacl3.impl.utils.DimensionIntegerImpl;
+
+public interface Dimension<T extends Number> {
+ T x();
+ T y();
+
+ T width();
+ T height();
+
+ T xLimit();
+ T yLimit();
+
+ T centerX();
+ T centerY();
+
+ boolean isPointInside(T x, T y);
+
+ MutableDimension<T> clone();
+
+ Dimension<T> withX(T x);
+ Dimension<T> withY(T y);
+ Dimension<T> withWidth(T width);
+ Dimension<T> withHeight(T height);
+
+ Dimension<T> moved(T x, T y);
+ Dimension<T> expanded(T width, T height);
+
+ static MutableDimension<Integer> ofInt(int x, int y, int width, int height) {
+ return new DimensionIntegerImpl(x, y, width, height);
+ }
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/utils/MutableDimension.java b/common/src/main/java/dev/isxander/yacl3/api/utils/MutableDimension.java
new file mode 100644
index 0000000..f551232
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/utils/MutableDimension.java
@@ -0,0 +1,11 @@
+package dev.isxander.yacl3.api.utils;
+
+public interface MutableDimension<T extends Number> extends Dimension<T> {
+ MutableDimension<T> setX(T x);
+ MutableDimension<T> setY(T y);
+ MutableDimension<T> setWidth(T width);
+ MutableDimension<T> setHeight(T height);
+
+ MutableDimension<T> move(T x, T y);
+ MutableDimension<T> expand(T width, T height);
+}
diff --git a/common/src/main/java/dev/isxander/yacl3/api/utils/OptionUtils.java b/common/src/main/java/dev/isxander/yacl3/api/utils/OptionUtils.java
new file mode 100644
index 0000000..cf33f0f
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl3/api/utils/OptionUtils.java
@@ -0,0 +1,39 @@
+package dev.isxander.yacl3.api.utils;
+
+import dev.isxander.yacl3.api.*;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class OptionUtils {
+ /**
+ * Consumes all options, ignoring groups and categories.
+ * When consumer returns true, this function stops iterating.
+ */
+ public static void consumeOptions(YetAnotherConfigLib yacl, Function<Option<?>, Boolean> consumer) {
+ for (ConfigCategory category : yacl.categories()) {
+ for (OptionGroup group : category.groups()) {
+ if (group instanceof ListOption<?> list) {
+ if (consumer.apply(list)) return;
+ } else {
+ for (Option<?> option : group.options()) {
+ if (consumer.apply(option)) return;
+ }
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Consumes all options, ignoring groups and categories.
+ *
+ * @see OptionUtils#consumeOptions(YetAnotherConfigLib, Function)
+ */
+ public static void forEachOptions(YetAnotherConfigLib yacl, Consumer<Option<?>> consumer) {
+ consumeOptions(yacl, (opt) -> {
+ consumer.accept(opt);
+ return false;
+ });
+ }
+}