aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/dev/isxander/yacl
diff options
context:
space:
mode:
authorxander <xander@isxander.dev>2022-09-01 11:58:49 +0100
committerxander <xander@isxander.dev>2022-09-01 11:58:49 +0100
commit4d977cc9764ecf0073650f126700f6ff638fa06b (patch)
tree883e68bbd80874c048b3e34db59bf0aa926b489b /src/main/java/dev/isxander/yacl
parente63a3c989e3a899bdc81558dd2e4c5cc2c659bde (diff)
downloadYetAnotherConfigLib-4d977cc9764ecf0073650f126700f6ff638fa06b.tar.gz
YetAnotherConfigLib-4d977cc9764ecf0073650f126700f6ff638fa06b.tar.bz2
YetAnotherConfigLib-4d977cc9764ecf0073650f126700f6ff638fa06b.zip
javadoc!
added LongSliderController renamed Control -> Controller add minecraft simple option binding constructor
Diffstat (limited to 'src/main/java/dev/isxander/yacl')
-rw-r--r--src/main/java/dev/isxander/yacl/api/Binding.java31
-rw-r--r--src/main/java/dev/isxander/yacl/api/ButtonOption.java4
-rw-r--r--src/main/java/dev/isxander/yacl/api/ConfigCategory.java60
-rw-r--r--src/main/java/dev/isxander/yacl/api/Control.java14
-rw-r--r--src/main/java/dev/isxander/yacl/api/Controller.java30
-rw-r--r--src/main/java/dev/isxander/yacl/api/NameableEnum.java3
-rw-r--r--src/main/java/dev/isxander/yacl/api/Option.java93
-rw-r--r--src/main/java/dev/isxander/yacl/api/OptionGroup.java45
-rw-r--r--src/main/java/dev/isxander/yacl/api/YetAnotherConfigLib.java60
-rw-r--r--src/main/java/dev/isxander/yacl/api/utils/OptionUtils.java13
-rw-r--r--src/main/java/dev/isxander/yacl/gui/OptionListWidget.java8
-rw-r--r--src/main/java/dev/isxander/yacl/gui/YACLScreen.java4
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/ActionControl.java58
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/ActionController.java87
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java (renamed from src/main/java/dev/isxander/yacl/gui/controllers/ControlWidget.java)12
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/EnumController.java (renamed from src/main/java/dev/isxander/yacl/gui/controllers/EnumControl.java)49
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/TickBoxController.java (renamed from src/main/java/dev/isxander/yacl/gui/controllers/TickBoxControl.java)42
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/package-info.java11
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/DoubleSliderController.java (renamed from src/main/java/dev/isxander/yacl/gui/controllers/slider/DoubleSliderControl.java)55
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/FloatSliderController.java (renamed from src/main/java/dev/isxander/yacl/gui/controllers/slider/FloatSliderControl.java)55
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/ISliderControl.java29
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/ISliderController.java54
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderControl.java72
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderController.java111
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/LongSliderController.java111
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/SliderControllerElement.java (renamed from src/main/java/dev/isxander/yacl/gui/controllers/slider/SliderControlElement.java)16
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/package-info.java10
-rw-r--r--src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java16
-rw-r--r--src/main/java/dev/isxander/yacl/impl/OptionImpl.java16
-rw-r--r--src/main/java/dev/isxander/yacl/impl/utils/NumberFormatHelper.java38
-rw-r--r--src/main/java/dev/isxander/yacl/mixin/SimpleOptionAccessor.java11
31 files changed, 962 insertions, 256 deletions
diff --git a/src/main/java/dev/isxander/yacl/api/Binding.java b/src/main/java/dev/isxander/yacl/api/Binding.java
index 74120a0..d870d8c 100644
--- a/src/main/java/dev/isxander/yacl/api/Binding.java
+++ b/src/main/java/dev/isxander/yacl/api/Binding.java
@@ -1,10 +1,17 @@
package dev.isxander.yacl.api;
import dev.isxander.yacl.impl.GenericBindingImpl;
+import dev.isxander.yacl.mixin.SimpleOptionAccessor;
+import net.minecraft.client.option.SimpleOption;
+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);
@@ -12,7 +19,31 @@ public interface Binding<T> {
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> of(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 SimpleOption}
+ */
+ static <T> Binding<T> of(SimpleOption<T> minecraftOption) {
+ Validate.notNull(minecraftOption, "`minecraftOption` must not be null");
+
+ return new GenericBindingImpl<>(
+ ((SimpleOptionAccessor<T>) (Object) minecraftOption).getDefaultValue(),
+ minecraftOption::getValue,
+ minecraftOption::setValue
+ );
+ }
}
diff --git a/src/main/java/dev/isxander/yacl/api/ButtonOption.java b/src/main/java/dev/isxander/yacl/api/ButtonOption.java
index 08436b3..346bfa4 100644
--- a/src/main/java/dev/isxander/yacl/api/ButtonOption.java
+++ b/src/main/java/dev/isxander/yacl/api/ButtonOption.java
@@ -20,7 +20,7 @@ public interface ButtonOption extends Option<Runnable> {
class Builder {
private Text name;
private final List<Text> tooltipLines = new ArrayList<>();
- private Function<ButtonOption, Control<Runnable>> controlGetter;
+ private Function<ButtonOption, Controller<Runnable>> controlGetter;
private Runnable action;
private Builder() {
@@ -48,7 +48,7 @@ public interface ButtonOption extends Option<Runnable> {
return this;
}
- public Builder controller(@NotNull Function<ButtonOption, Control<Runnable>> control) {
+ public Builder controller(@NotNull Function<ButtonOption, Controller<Runnable>> control) {
Validate.notNull(control, "`control` cannot be null");
this.controlGetter = control;
diff --git a/src/main/java/dev/isxander/yacl/api/ConfigCategory.java b/src/main/java/dev/isxander/yacl/api/ConfigCategory.java
index 13552d6..b2bbf95 100644
--- a/src/main/java/dev/isxander/yacl/api/ConfigCategory.java
+++ b/src/main/java/dev/isxander/yacl/api/ConfigCategory.java
@@ -10,14 +10,29 @@ import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.function.Function;
+/**
+ * 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 Text name();
+ /**
+ * Gets every {@link OptionGroup} in this category.
+ */
@NotNull ImmutableList<OptionGroup> groups();
+ /**
+ * Creates a builder to construct a {@link ConfigCategory}
+ */
static Builder createBuilder() {
return new Builder();
}
@@ -32,6 +47,11 @@ public interface ConfigCategory {
}
+ /**
+ * Sets name of the category
+ *
+ * @see ConfigCategory#name()
+ */
public Builder name(@NotNull Text name) {
Validate.notNull(name, "`name` cannot be null");
@@ -39,6 +59,14 @@ public interface ConfigCategory {
return this;
}
+ /**
+ * 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()
+ */
public Builder option(@NotNull Option<?> option) {
Validate.notNull(option, "`option` must not be null");
@@ -46,6 +74,26 @@ public interface ConfigCategory {
return this;
}
+ /**
+ * 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()
+ */
+ public Builder options(@NotNull Collection<Option<?>> options) {
+ Validate.notEmpty(options, "`options` must not be empty");
+
+ this.rootOptions.addAll(options);
+ return this;
+ }
+
+ /**
+ * 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");
@@ -53,6 +101,18 @@ public interface ConfigCategory {
return this;
}
+ /**
+ * 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;
+ }
+
public ConfigCategory build() {
Validate.notNull(name, "`name` must not be null to build `ConfigCategory`");
Validate.notEmpty(rootOptions, "`at least one option must be added to build `ConfigCategory`");
diff --git a/src/main/java/dev/isxander/yacl/api/Control.java b/src/main/java/dev/isxander/yacl/api/Control.java
deleted file mode 100644
index 242b2c8..0000000
--- a/src/main/java/dev/isxander/yacl/api/Control.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package dev.isxander.yacl.api;
-
-import dev.isxander.yacl.api.utils.Dimension;
-import dev.isxander.yacl.gui.controllers.ControlWidget;
-import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.text.Text;
-
-public interface Control<T> {
- Option<T> option();
-
- Text formatValue();
-
- ControlWidget<?> provideWidget(Screen screen, Dimension<Integer> widgetDimension);
-}
diff --git a/src/main/java/dev/isxander/yacl/api/Controller.java b/src/main/java/dev/isxander/yacl/api/Controller.java
new file mode 100644
index 0000000..2c0a5e1
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/api/Controller.java
@@ -0,0 +1,30 @@
+package dev.isxander.yacl.api;
+
+import dev.isxander.yacl.api.utils.Dimension;
+import dev.isxander.yacl.gui.controllers.ControllerWidget;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * 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()}
+ */
+ Text formatValue();
+
+ /**
+ * Provides a widget to display
+ *
+ * @param screen parent screen
+ */
+ @ApiStatus.Internal
+ ControllerWidget<?> provideWidget(Screen screen, Dimension<Integer> widgetDimension);
+}
diff --git a/src/main/java/dev/isxander/yacl/api/NameableEnum.java b/src/main/java/dev/isxander/yacl/api/NameableEnum.java
index 12a58c3..9744ce9 100644
--- a/src/main/java/dev/isxander/yacl/api/NameableEnum.java
+++ b/src/main/java/dev/isxander/yacl/api/NameableEnum.java
@@ -2,6 +2,9 @@ package dev.isxander.yacl.api;
import net.minecraft.text.Text;
+/**
+ * Used for the default value formatter of {@link dev.isxander.yacl.gui.controllers.EnumController}
+ */
public interface NameableEnum {
Text getDisplayName();
}
diff --git a/src/main/java/dev/isxander/yacl/api/Option.java b/src/main/java/dev/isxander/yacl/api/Option.java
index 5a98d50..6598b80 100644
--- a/src/main/java/dev/isxander/yacl/api/Option.java
+++ b/src/main/java/dev/isxander/yacl/api/Option.java
@@ -5,7 +5,6 @@ import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@@ -14,27 +13,70 @@ import java.util.function.Function;
import java.util.function.Supplier;
public interface Option<T> {
+ /**
+ * Name of the option
+ */
@NotNull Text name();
- @Nullable Text tooltip();
-
- @NotNull Control<T> control();
-
+ /**
+ * Tooltip (or description) of the option.
+ * Rendered on hover.
+ */
+ @NotNull Text tooltip();
+
+ /**
+ * Widget provider for a type of option.
+ *
+ * @see dev.isxander.yacl.gui.controllers
+ */
+ @NotNull Controller<T> controller();
+
+ /**
+ * Binding for the option.
+ * Controls setting, getting and default value.
+ *
+ * @see Binding
+ */
@NotNull Binding<T> binding();
+ /**
+ * Checks if the pending value is not equal to the current set value
+ */
boolean changed();
- T pendingValue();
+ /**
+ * 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.
+ */
void applyValue();
+ /**
+ * Sets the pending value to the bound value.
+ */
void forgetPendingValue();
+ /**
+ * Sets the pending value to the default bound value.
+ */
void requestSetDefault();
- static <T> Builder<T> createBuilder(Class<T> clazz) {
+ /**
+ * Creates a builder to construct an {@link Option}
+ *
+ * @param <T> type of the option's value
+ * @param typeClass used to capture the type
+ */
+ static <T> Builder<T> createBuilder(Class<T> typeClass) {
return new Builder<>();
}
@@ -43,7 +85,7 @@ public interface Option<T> {
private final List<Text> tooltipLines = new ArrayList<>();
- private Function<Option<T>, Control<T>> controlGetter;
+ private Function<Option<T>, Controller<T>> controlGetter;
private Binding<T> binding;
@@ -51,6 +93,11 @@ public interface Option<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");
@@ -58,6 +105,13 @@ public interface Option<T> {
return this;
}
+ /**
+ * Sets the tooltip to be used by the option.
+ * 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");
@@ -65,13 +119,25 @@ public interface Option<T> {
return this;
}
- public Builder<T> controller(@NotNull Function<Option<T>, Control<T>> control) {
+ /**
+ * 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<Option<T>, Controller<T>> control) {
Validate.notNull(control, "`control` cannot be null");
this.controlGetter = control;
return this;
}
+ /**
+ * Sets the binding for the option.
+ * Used for default, getter and setter.
+ *
+ * @see Binding
+ */
public Builder<T> binding(@NotNull Binding<T> binding) {
Validate.notNull(binding, "`binding` cannot be null");
@@ -79,6 +145,15 @@ public interface Option<T> {
return this;
}
+ /**
+ * Sets the binding for the option.
+ * Shorthand of {@link Binding#of(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 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");
diff --git a/src/main/java/dev/isxander/yacl/api/OptionGroup.java b/src/main/java/dev/isxander/yacl/api/OptionGroup.java
index bedbc82..6a302c4 100644
--- a/src/main/java/dev/isxander/yacl/api/OptionGroup.java
+++ b/src/main/java/dev/isxander/yacl/api/OptionGroup.java
@@ -5,18 +5,37 @@ import dev.isxander.yacl.impl.OptionGroupImpl;
import net.minecraft.text.Text;
import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+/**
+ * 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.
+ */
Text name();
+ /**
+ * List of all options in the group
+ */
@NotNull ImmutableList<Option<?>> options();
+ /**
+ * 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 Builder();
}
@@ -29,6 +48,11 @@ public interface OptionGroup {
}
+ /**
+ * 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");
@@ -36,6 +60,12 @@ public interface OptionGroup {
return this;
}
+ /**
+ * Adds an option to group.
+ * To construct an option, use {@link Option#createBuilder(Class)}
+ *
+ * @see OptionGroup#options()
+ */
public Builder option(@NotNull Option<?> option) {
Validate.notNull(option, "`option` must not be null");
@@ -43,6 +73,19 @@ public interface OptionGroup {
return this;
}
+ /**
+ * Adds multiple options to group.
+ * To construct an option, use {@link Option#createBuilder(Class)}
+ *
+ * @see OptionGroup#options()
+ */
+ public Builder options(@NotNull Collection<Option<?>> options) {
+ Validate.notEmpty(options, "`options` must not be empty");
+
+ this.options.addAll(options);
+ return this;
+ }
+
public OptionGroup build() {
Validate.notEmpty(options, "`options` must not be empty to build `OptionGroup`");
diff --git a/src/main/java/dev/isxander/yacl/api/YetAnotherConfigLib.java b/src/main/java/dev/isxander/yacl/api/YetAnotherConfigLib.java
index a31a4fb..34ccc6a 100644
--- a/src/main/java/dev/isxander/yacl/api/YetAnotherConfigLib.java
+++ b/src/main/java/dev/isxander/yacl/api/YetAnotherConfigLib.java
@@ -10,21 +10,45 @@ 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.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.
+ */
Text 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 Builder();
}
@@ -39,6 +63,11 @@ public interface YetAnotherConfigLib {
}
+ /**
+ * Sets title of GUI for Minecraft narration
+ *
+ * @see YetAnotherConfigLib#title()
+ */
public Builder title(@NotNull Text title) {
Validate.notNull(title, "`title` cannot be null");
@@ -46,6 +75,12 @@ public interface YetAnotherConfigLib {
return this;
}
+ /**
+ * Adds a new category.
+ * To create a category you need to use {@link ConfigCategory#createBuilder()}
+ *
+ * @see YetAnotherConfigLib#categories()
+ */
public Builder category(@NotNull ConfigCategory category) {
Validate.notNull(category, "`category` cannot be null");
@@ -53,6 +88,24 @@ public interface YetAnotherConfigLib {
return this;
}
+ /**
+ * Adds multiple categories at once.
+ * To create a category you need to use {@link ConfigCategory#createBuilder()}
+ *
+ * @see YetAnotherConfigLib#categories()
+ */
+ public Builder categories(@NotNull Collection<ConfigCategory> categories) {
+ Validate.notEmpty(categories, "`categories` cannot be empty");
+
+ this.categories.addAll(categories);
+ return this;
+ }
+
+ /**
+ * 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");
@@ -60,6 +113,11 @@ public interface YetAnotherConfigLib {
return this;
}
+ /**
+ * 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");
diff --git a/src/main/java/dev/isxander/yacl/api/utils/OptionUtils.java b/src/main/java/dev/isxander/yacl/api/utils/OptionUtils.java
index ed51683..ab46b5b 100644
--- a/src/main/java/dev/isxander/yacl/api/utils/OptionUtils.java
+++ b/src/main/java/dev/isxander/yacl/api/utils/OptionUtils.java
@@ -9,20 +9,29 @@ 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()) {
for (Option<?> option : group.options()) {
- if (!consumer.apply(option)) return;
+ 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 true;
+ return false;
});
}
}
diff --git a/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java b/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java
index 6cb7090..e020065 100644
--- a/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java
+++ b/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java
@@ -4,7 +4,7 @@ import dev.isxander.yacl.api.ConfigCategory;
import dev.isxander.yacl.api.Option;
import dev.isxander.yacl.api.OptionGroup;
import dev.isxander.yacl.api.utils.Dimension;
-import dev.isxander.yacl.gui.controllers.ControlWidget;
+import dev.isxander.yacl.gui.controllers.ControllerWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.Element;
@@ -25,7 +25,7 @@ public class OptionListWidget extends ElementListWidget<OptionListWidget.Entry>
if (!group.isRoot())
addEntry(new GroupSeparatorEntry(group));
for (Option<?> option : group.options()) {
- addEntry(new OptionEntry(option.control().provideWidget(screen, null)));
+ addEntry(new OptionEntry(option.controller().provideWidget(screen, null)));
}
}
}
@@ -40,9 +40,9 @@ public class OptionListWidget extends ElementListWidget<OptionListWidget.Entry>
}
private static class OptionEntry extends Entry {
- private final ControlWidget<?> widget;
+ private final ControllerWidget<?> widget;
- public OptionEntry(ControlWidget<?> widget) {
+ public OptionEntry(ControllerWidget<?> widget) {
this.widget = widget;
}
diff --git a/src/main/java/dev/isxander/yacl/gui/YACLScreen.java b/src/main/java/dev/isxander/yacl/gui/YACLScreen.java
index 97cc8ed..aab047d 100644
--- a/src/main/java/dev/isxander/yacl/gui/YACLScreen.java
+++ b/src/main/java/dev/isxander/yacl/gui/YACLScreen.java
@@ -118,9 +118,9 @@ public class YACLScreen extends Screen {
OptionUtils.consumeOptions(config, (option) -> {
if (option.changed()) {
pendingChanges.set(true);
- return false;
+ return true;
}
- return true;
+ return false;
});
return pendingChanges.get();
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/ActionControl.java b/src/main/java/dev/isxander/yacl/gui/controllers/ActionControl.java
deleted file mode 100644
index 66e8e35..0000000
--- a/src/main/java/dev/isxander/yacl/gui/controllers/ActionControl.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package dev.isxander.yacl.gui.controllers;
-
-import dev.isxander.yacl.api.ButtonOption;
-import dev.isxander.yacl.api.Control;
-import dev.isxander.yacl.api.utils.Dimension;
-import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.text.Text;
-
-public class ActionControl implements Control<Runnable> {
- private final ButtonOption option;
- private final Text executeText;
-
- public ActionControl(ButtonOption option) {
- this(option, Text.translatable("yacl.control.action.execute"));
- }
-
- public ActionControl(ButtonOption option, Text executeText) {
- this.option = option;
- this.executeText = executeText;
-
- }
-
- @Override
- public ButtonOption option() {
- return option;
- }
-
- @Override
- public Text formatValue() {
- return executeText;
- }
-
- @Override
- public ControlWidget<ActionControl> provideWidget(Screen screen, Dimension<Integer> widgetDimension) {
- return new ActionControlElement(this, screen, widgetDimension);
- }
-
- public static class ActionControlElement extends ControlWidget<ActionControl> {
- public ActionControlElement(ActionControl control, Screen screen, Dimension<Integer> dim) {
- super(control, screen, dim);
- }
-
- @Override
- public boolean mouseClicked(double mouseX, double mouseY, int button) {
- if (isMouseOver(mouseX, mouseY)) {
- playDownSound();
- control.option().action().run();
- return true;
- }
- return false;
- }
-
- @Override
- protected int getHoveredControlWidth() {
- return getUnhoveredControlWidth();
- }
- }
-}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/ActionController.java b/src/main/java/dev/isxander/yacl/gui/controllers/ActionController.java
new file mode 100644
index 0000000..b632c5f
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/ActionController.java
@@ -0,0 +1,87 @@
+package dev.isxander.yacl.gui.controllers;
+
+import dev.isxander.yacl.api.ButtonOption;
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.utils.Dimension;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * Simple controller that simply runs the button action on press
+ * and renders a {@link} Text on the right.
+ */
+public class ActionController implements Controller<Runnable> {
+ public static final Text DEFAULT_TEXT = Text.translatable("yacl.control.action.execute");
+
+ private final ButtonOption option;
+ private final Text text;
+
+ /**
+ * Constructs an action controller
+ * with the default formatter of {@link ActionController#DEFAULT_TEXT}
+ *
+ * @param option bound option
+ */
+ public ActionController(ButtonOption option) {
+ this(option, DEFAULT_TEXT);
+ }
+
+ /**
+ * Constructs an action controller
+ *
+ * @param option bound option
+ * @param text text to display
+ */
+ public ActionController(ButtonOption option, Text text) {
+ this.option = option;
+ this.text = text;
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ButtonOption option() {
+ return option;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Text formatValue() {
+ return text;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ControllerWidget<ActionController> provideWidget(Screen screen, Dimension<Integer> widgetDimension) {
+ return new ActionControllerElement(this, screen, widgetDimension);
+ }
+
+ @ApiStatus.Internal
+ public static class ActionControllerElement extends ControllerWidget<ActionController> {
+ public ActionControllerElement(ActionController control, Screen screen, Dimension<Integer> dim) {
+ super(control, screen, dim);
+ }
+
+ @Override
+ public boolean mouseClicked(double mouseX, double mouseY, int button) {
+ if (isMouseOver(mouseX, mouseY)) {
+ playDownSound();
+ control.option().action().run();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected int getHoveredControlWidth() {
+ return getUnhoveredControlWidth();
+ }
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/ControlWidget.java b/src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java
index be05ba4..416ee42 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/ControlWidget.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java
@@ -1,6 +1,6 @@
package dev.isxander.yacl.gui.controllers;
-import dev.isxander.yacl.api.Control;
+import dev.isxander.yacl.api.Controller;
import dev.isxander.yacl.api.utils.Dimension;
import dev.isxander.yacl.gui.AbstractWidget;
import net.minecraft.client.gui.DrawableHelper;
@@ -12,9 +12,9 @@ import net.minecraft.text.Text;
import java.util.List;
-public abstract class ControlWidget<T extends Control<?>> extends AbstractWidget {
+public abstract class ControllerWidget<T extends Controller<?>> extends AbstractWidget {
protected final T control;
- protected final List<OrderedText> wrappedDescription;
+ protected final List<OrderedText> wrappedTooltip;
public Dimension<Integer> dim;
protected final Screen screen;
@@ -22,11 +22,11 @@ public abstract class ControlWidget<T extends Control<?>> extends AbstractWidget
protected boolean hovered = false;
protected float hoveredTicks = 0;
- public ControlWidget(T control, Screen screen, Dimension<Integer> dim) {
+ public ControllerWidget(T control, Screen screen, Dimension<Integer> dim) {
this.control = control;
this.dim = dim;
this.screen = screen;
- this.wrappedDescription = textRenderer.wrapLines(control.option().tooltip(), screen.width / 2);
+ this.wrappedTooltip = textRenderer.wrapLines(control.option().tooltip(), screen.width / 2);
}
@Override
@@ -63,7 +63,7 @@ public abstract class ControlWidget<T extends Control<?>> extends AbstractWidget
}
if (hoveredTicks > 40) {
- screen.renderOrderedTooltip(matrices, wrappedDescription, mouseX, mouseY);
+ screen.renderOrderedTooltip(matrices, wrappedTooltip, mouseX, mouseY);
}
}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/EnumControl.java b/src/main/java/dev/isxander/yacl/gui/controllers/EnumController.java
index d032937..d88401c 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/EnumControl.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/EnumController.java
@@ -1,20 +1,36 @@
package dev.isxander.yacl.gui.controllers;
-import dev.isxander.yacl.api.Control;
+import dev.isxander.yacl.api.Controller;
import dev.isxander.yacl.api.NameableEnum;
import dev.isxander.yacl.api.Option;
import dev.isxander.yacl.api.utils.Dimension;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
+import org.jetbrains.annotations.ApiStatus;
import java.util.function.Function;
-public class EnumControl<T extends Enum<T>> implements Control<T> {
+/**
+ * Simple controller type that displays the enum on the right.
+ * <p>
+ * Cycles forward with left click, cycles backward with right click or when shift is held
+ *
+ * @param <T> enum type
+ */
+public class EnumController<T extends Enum<T>> implements Controller<T> {
private final Option<T> option;
private final Function<T, Text> valueFormatter;
private final Class<T> enumClass;
- public EnumControl(Option<T> option, Class<T> enumClass) {
+ /**
+ * Constructs a cycling enum controller with a default value formatter.
+ * The default value formatter first searches if the
+ * enum is a {@link NameableEnum} else, just use {@link Enum#name()}
+ *
+ * @param option bound option
+ * @param enumClass class of enum
+ */
+ public EnumController(Option<T> option, Class<T> enumClass) {
this(option, enumClass, value -> {
if (value instanceof NameableEnum nameableEnum)
return nameableEnum.getDisplayName();
@@ -22,31 +38,48 @@ public class EnumControl<T extends Enum<T>> implements Control<T> {
});
}
- public EnumControl(Option<T> option, Class<T> enumClass, Function<T, Text> valueFormatter) {
+ /**
+ * Constructs a cycling enum controller.
+ *
+ * @param option bound option
+ * @param enumClass class of enum
+ * @param valueFormatter format the enum into any {@link Text}
+ */
+ public EnumController(Option<T> option, Class<T> enumClass, Function<T, Text> valueFormatter) {
this.option = option;
this.valueFormatter = valueFormatter;
this.enumClass = enumClass;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public Option<T> option() {
return option;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public Text formatValue() {
return valueFormatter.apply(option().pendingValue());
}
+ /**
+ * {@inheritDoc}
+ */
@Override
- public ControlWidget<EnumControl<T>> provideWidget(Screen screen, Dimension<Integer> widgetDimension) {
- return new EnumControlElement<>(this, screen, widgetDimension, enumClass.getEnumConstants());
+ public ControllerWidget<EnumController<T>> provideWidget(Screen screen, Dimension<Integer> widgetDimension) {
+ return new EnumControllerElement<>(this, screen, widgetDimension, enumClass.getEnumConstants());
}
- public static class EnumControlElement<T extends Enum<T>> extends ControlWidget<EnumControl<T>> {
+ @ApiStatus.Internal
+ public static class EnumControllerElement<T extends Enum<T>> extends ControllerWidget<EnumController<T>> {
private final T[] values;
- public EnumControlElement(EnumControl<T> control, Screen screen, Dimension<Integer> dim, T[] values) {
+ public EnumControllerElement(EnumController<T> control, Screen screen, Dimension<Integer> dim, T[] values) {
super(control, screen, dim);
this.values = values;
}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/TickBoxControl.java b/src/main/java/dev/isxander/yacl/gui/controllers/TickBoxController.java
index fb0e595..3bc5c22 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/TickBoxControl.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/TickBoxController.java
@@ -1,6 +1,6 @@
package dev.isxander.yacl.gui.controllers;
-import dev.isxander.yacl.api.Control;
+import dev.isxander.yacl.api.Controller;
import dev.isxander.yacl.api.Option;
import dev.isxander.yacl.api.utils.Dimension;
import net.minecraft.client.gui.DrawableHelper;
@@ -8,10 +8,14 @@ import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
+import org.jetbrains.annotations.ApiStatus;
import java.util.function.Function;
-public class TickBoxControl implements Control<Boolean> {
+/**
+ * On hover, this controller renders a tickbox, otherwise, formatted {@link Text}
+ */
+public class TickBoxController implements Controller<Boolean> {
public static final Function<Boolean, Text> ON_OFF_FORMATTER = (state) ->
state
@@ -31,33 +35,55 @@ public class TickBoxControl implements Control<Boolean> {
private final Option<Boolean> option;
private final Function<Boolean, Text> valueFormatter;
- public TickBoxControl(Option<Boolean> option) {
+ /**
+ * Constructs a tickbox controller
+ * with the default value formatter of {@link TickBoxController#ON_OFF_FORMATTER}
+ *
+ * @param option bound option
+ */
+ public TickBoxController(Option<Boolean> option) {
this(option, ON_OFF_FORMATTER);
}
- public TickBoxControl(Option<Boolean> option, Function<Boolean, Text> valueFormatter) {
+ /**
+ * Constructs a tickbox controller
+ *
+ * @param option bound option
+ * @param valueFormatter format value into any {@link Text}
+ */
+ public TickBoxController(Option<Boolean> option, Function<Boolean, Text> valueFormatter) {
this.option = option;
this.valueFormatter = valueFormatter;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public Option<Boolean> option() {
return option;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public Text formatValue() {
return valueFormatter.apply(option().pendingValue());
}
+ /**
+ * {@inheritDoc}
+ */
@Override
- public ControlWidget<TickBoxControl> provideWidget(Screen screen, Dimension<Integer> widgetDimension) {
- return new TickBoxControlElement(this, screen, widgetDimension);
+ public ControllerWidget<TickBoxController> provideWidget(Screen screen, Dimension<Integer> widgetDimension) {
+ return new TickBoxControllerElement(this, screen, widgetDimension);
}
- public static class TickBoxControlElement extends ControlWidget<TickBoxControl> {
- private TickBoxControlElement(TickBoxControl control, Screen screen, Dimension<Integer> dim) {
+ @ApiStatus.Internal
+ public static class TickBoxControllerElement extends ControllerWidget<TickBoxController> {
+ private TickBoxControllerElement(TickBoxController control, Screen screen, Dimension<Integer> dim) {
super(control, screen, dim);
}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/package-info.java b/src/main/java/dev/isxander/yacl/gui/controllers/package-info.java
new file mode 100644
index 0000000..fcfc8e5
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/package-info.java
@@ -0,0 +1,11 @@
+/**
+ * This package contains all {@link dev.isxander.yacl.api.Controller} implementations
+ *
+ * <ul>
+ * <li>For numbers: {@link dev.isxander.yacl.gui.controllers.slider}</li>
+ * <li>For booleans: {@link dev.isxander.yacl.gui.controllers.TickBoxController}</li>
+ * <li>For enums: {@link dev.isxander.yacl.gui.controllers.EnumController}</li>
+ * <li>For {@link dev.isxander.yacl.api.ButtonOption}: {@link dev.isxander.yacl.gui.controllers.ActionController}</li>
+ * </ul>
+ */
+package dev.isxander.yacl.gui.controllers;
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/slider/DoubleSliderControl.java b/src/main/java/dev/isxander/yacl/gui/controllers/slider/DoubleSliderController.java
index 5aaa6c4..8d74ceb 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/slider/DoubleSliderControl.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/slider/DoubleSliderController.java
@@ -6,7 +6,13 @@ import org.apache.commons.lang3.Validate;
import java.util.function.Function;
-public class DoubleSliderControl implements ISliderControl<Double> {
+/**
+ * {@link ISliderController} for doubles.
+ */
+public class DoubleSliderController implements ISliderController<Double> {
+ /**
+ * Formats doubles to two decimal places
+ */
public static final Function<Double, Text> DEFAULT_FORMATTER = value -> Text.of(String.format("%.2f", value));
private final Option<Double> option;
@@ -15,11 +21,29 @@ public class DoubleSliderControl implements ISliderControl<Double> {
private final Function<Double, Text> valueFormatter;
- public DoubleSliderControl(Option<Double> option, double min, double max, double interval) {
+ /**
+ * Constructs a {@link ISliderController} for doubles
+ * using the default value formatter {@link DoubleSliderController#DEFAULT_FORMATTER}.
+ *
+ * @param option bound option
+ * @param min minimum slider value
+ * @param max maximum slider value
+ * @param interval step size (or increments) for the slider
+ */
+ public DoubleSliderController(Option<Double> option, double min, double max, double interval) {
this(option, min, max, interval, DEFAULT_FORMATTER);
}
- public DoubleSliderControl(Option<Double> option, double min, double max, double interval, Function<Double, Text> valueFormatter) {
+ /**
+ * Constructs a {@link ISliderController} for doubles.
+ *
+ * @param option bound option
+ * @param min minimum slider value
+ * @param max maximum slider value
+ * @param interval step size (or increments) for the slider
+ * @param valueFormatter format the value into any {@link Text}
+ */
+ public DoubleSliderController(Option<Double> option, double min, double max, double interval, Function<Double, Text> valueFormatter) {
Validate.isTrue(max > min, "`max` cannot be smaller than `min`");
Validate.isTrue(interval > 0, "`interval` must be more than 0");
@@ -30,43 +54,60 @@ public class DoubleSliderControl implements ISliderControl<Double> {
this.valueFormatter = valueFormatter;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public Option<Double> option() {
return option;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public Text formatValue() {
return valueFormatter.apply(option().pendingValue());
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public double min() {
return min;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public double max() {
return max;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public double interval() {
return interval;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void setPendingValue(double value) {
option().requestSet(value);
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public double pendingValue() {
return option().pendingValue();
}
- @Override
- public Text getValueText(double value) {
- return valueFormatter.apply(value);
- }
}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/slider/FloatSliderControl.java b/src/main/java/dev/isxander/yacl/gui/controllers/slider/FloatSliderController.java
index d74ec33..6f4192b 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/slider/FloatSliderControl.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/slider/FloatSliderController.java
@@ -6,7 +6,13 @@ import org.apache.commons.lang3.Validate;
import java.util.function.Function;
-public class FloatSliderControl implements ISliderControl<Float> {
+/**
+ * {@link ISliderController} for floats.
+ */
+public class FloatSliderController implements ISliderController<Float> {
+ /**
+ * Formats floats to one decimal place
+ */
public static final Function<Float, Text> DEFAULT_FORMATTER = value -> Text.of(String.format("%.1f", value));
private final Option<Float> option;
@@ -15,11 +21,29 @@ public class FloatSliderControl implements ISliderControl<Float> {
private final Function<Float, Text> valueFormatter;
- public FloatSliderControl(Option<Float> option, float min, float max, float interval) {
+ /**
+ * Constructs a {@link ISliderController} for floats
+ * using the default value formatter {@link FloatSliderController#DEFAULT_FORMATTER}.
+ *
+ * @param option bound option
+ * @param min minimum slider value
+ * @param max maximum slider value
+ * @param interval step size (or increments) for the slider
+ */
+ public FloatSliderController(Option<Float> option, float min, float max, float interval) {
this(option, min, max, interval, DEFAULT_FORMATTER);
}
- public FloatSliderControl(Option<Float> option, float min, float max, float interval, Function<Float, Text> valueFormatter) {
+ /**
+ * Constructs a {@link ISliderController} for floats.
+ *
+ * @param option bound option
+ * @param min minimum slider value
+ * @param max maximum slider value
+ * @param interval step size (or increments) for the slider
+ * @param valueFormatter format the value into any {@link Text}
+ */
+ public FloatSliderController(Option<Float> option, float min, float max, float interval, Function<Float, Text> valueFormatter) {
Validate.isTrue(max > min, "`max` cannot be smaller than `min`");
Validate.isTrue(interval > 0, "`interval` must be more than 0");
@@ -30,43 +54,60 @@ public class FloatSliderControl implements ISliderControl<Float> {
this.valueFormatter = valueFormatter;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public Option<Float> option() {
return option;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public Text formatValue() {
return valueFormatter.apply(option().pendingValue());
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public double min() {
return min;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public double max() {
return max;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public double interval() {
return interval;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void setPendingValue(double value) {
option().requestSet((float) value);
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public double pendingValue() {
return option().pendingValue();
}
- @Override
- public Text getValueText(double value) {
- return valueFormatter.apply((float) value);
- }
}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/slider/ISliderControl.java b/src/main/java/dev/isxander/yacl/gui/controllers/slider/ISliderControl.java
deleted file mode 100644
index 8c1cdf8..0000000
--- a/src/main/java/dev/isxander/yacl/gui/controllers/slider/ISliderControl.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package dev.isxander.yacl.gui.controllers.slider;
-
-import dev.isxander.yacl.api.Control;
-import dev.isxander.yacl.api.utils.Dimension;
-import dev.isxander.yacl.gui.controllers.ControlWidget;
-import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.text.Text;
-
-public interface ISliderControl<T extends Number> extends Control<T> {
- double min();
-
- double max();
-
- double interval();
-
- default double range() {
- return max() - min();
- }
-
- void setPendingValue(double value);
- double pendingValue();
-
- Text getValueText(double value);
-
- @Override
- default ControlWidget<?> provideWidget(Screen screen, Dimension<Integer> widgetDimension) {
- return new SliderControlElement(this, screen, widgetDimension, min(), max(), interval());
- }
-}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/slider/ISliderController.java b/src/main/java/dev/isxander/yacl/gui/controllers/slider/ISliderController.java
new file mode 100644
index 0000000..0f6c2d5
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/slider/ISliderController.java
@@ -0,0 +1,54 @@
+package dev.isxander.yacl.gui.controllers.slider;
+
+import dev.isxander.yacl.api.Controller;
+import dev.isxander.yacl.api.utils.Dimension;
+import dev.isxander.yacl.gui.controllers.ControllerWidget;
+import net.minecraft.client.gui.screen.Screen;
+
+/**
+ * Simple custom slider implementation that shifts the current value across when shown.
+ * <p>
+ * For simplicity, {@link SliderControllerElement} works in doubles so each
+ * {@link ISliderController} must cast to double. This is to get around re-writing the element for every type.
+ */
+public interface ISliderController<T extends Number> extends Controller<T> {
+ /**
+ * Gets the minimum value for the slider
+ */
+ double min();
+
+ /**
+ * Gets the maximum value for the slider
+ */
+ double max();
+
+ /**
+ * Gets the interval (or step size) for the slider.
+ */
+ double interval();
+
+ /**
+ * Gets the range of the slider.
+ */
+ default double range() {
+ return max() - min();
+ }
+
+ /**
+ * Sets the {@link dev.isxander.yacl.api.Option}'s pending value
+ */
+ void setPendingValue(double value);
+
+ /**
+ * Gets the {@link dev.isxander.yacl.api.Option}'s pending value
+ */
+ double pendingValue();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default ControllerWidget<?> provideWidget(Screen screen, Dimension<Integer> widgetDimension) {
+ return new SliderControllerElement(this, screen, widgetDimension, min(), max(), interval());
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderControl.java b/src/main/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderControl.java
deleted file mode 100644
index 99c8137..0000000
--- a/src/main/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderControl.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package dev.isxander.yacl.gui.controllers.slider;
-
-import dev.isxander.yacl.api.Option;
-import net.minecraft.text.Text;
-import org.apache.commons.lang3.Validate;
-
-import java.util.function.Function;
-
-public class IntegerSliderControl implements ISliderControl<Integer> {
- public static final Function<Integer, Text> DEFAULT_FORMATTER = value -> Text.of(String.valueOf(value));
-
- private final Option<Integer> option;
-
- private final int min, max, interval;
-
- private final Function<Integer, Text> valueFormatter;
-
- public IntegerSliderControl(Option<Integer> option, int min, int max, int interval) {
- this(option, min, max, interval, DEFAULT_FORMATTER);
- }
-
- public IntegerSliderControl(Option<Integer> option, int min, int max, int interval, Function<Integer, Text> valueFormatter) {
- Validate.isTrue(max > min, "`max` cannot be smaller than `min`");
- Validate.isTrue(interval > 0, "`interval` must be more than 0");
-
- this.option = option;
- this.min = min;
- this.max = max;
- this.interval = interval;
- this.valueFormatter = valueFormatter;
- }
-
- @Override
- public Option<Integer> option() {
- return option;
- }
-
- @Override
- public Text formatValue() {
- return valueFormatter.apply(option().pendingValue());
- }
-
- @Override
- public double min() {
- return min;
- }
-
- @Override
- public double max() {
- return max;
- }
-
- @Override
- public double interval() {
- return interval;
- }
-
- @Override
- public void setPendingValue(double value) {
- option().requestSet((int) value);
- }
-
- @Override
- public double pendingValue() {
- return option().pendingValue();
- }
-
- @Override
- public Text getValueText(double value) {
- return valueFormatter.apply((int) value);
- }
-}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderController.java b/src/main/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderController.java
new file mode 100644
index 0000000..0d0d7b9
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderController.java
@@ -0,0 +1,111 @@
+package dev.isxander.yacl.gui.controllers.slider;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.utils.NumberFormatHelper;
+import net.minecraft.text.Text;
+import org.apache.commons.lang3.Validate;
+
+import java.util.function.Function;
+
+/**
+ * {@link ISliderController} for integers.
+ */
+public class IntegerSliderController implements ISliderController<Integer> {
+ public static final Function<Integer, Text> DEFAULT_FORMATTER = value -> Text.of(NumberFormatHelper.formatSmaller(value));
+
+ private final Option<Integer> option;
+
+ private final int min, max, interval;
+
+ private final Function<Integer, Text> valueFormatter;
+
+ /**
+ * Constructs a {@link ISliderController} for integers
+ * using the default value formatter {@link IntegerSliderController#DEFAULT_FORMATTER}.
+ *
+ * @param option bound option
+ * @param min minimum slider value
+ * @param max maximum slider value
+ * @param interval step size (or increments) for the slider
+ */
+ public IntegerSliderController(Option<Integer> option, int min, int max, int interval) {
+ this(option, min, max, interval, DEFAULT_FORMATTER);
+ }
+
+ /**
+ * Constructs a {@link ISliderController} for integers.
+ *
+ * @param option bound option
+ * @param min minimum slider value
+ * @param max maximum slider value
+ * @param interval step size (or increments) for the slider
+ * @param valueFormatter format the value into any {@link Text}
+ */
+ public IntegerSliderController(Option<Integer> option, int min, int max, int interval, Function<Integer, Text> valueFormatter) {
+ Validate.isTrue(max > min, "`max` cannot be smaller than `min`");
+ Validate.isTrue(interval > 0, "`interval` must be more than 0");
+
+ this.option = option;
+ this.min = min;
+ this.max = max;
+ this.interval = interval;
+ this.valueFormatter = valueFormatter;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Option<Integer> option() {
+ return option;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Text formatValue() {
+ return valueFormatter.apply(option().pendingValue());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double min() {
+ return min;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double max() {
+ return max;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double interval() {
+ return interval;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setPendingValue(double value) {
+ option().requestSet((int) value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double pendingValue() {
+ return option().pendingValue();
+ }
+
+}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/slider/LongSliderController.java b/src/main/java/dev/isxander/yacl/gui/controllers/slider/LongSliderController.java
new file mode 100644
index 0000000..d4c71d1
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/slider/LongSliderController.java
@@ -0,0 +1,111 @@
+package dev.isxander.yacl.gui.controllers.slider;
+
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.impl.utils.NumberFormatHelper;
+import net.minecraft.text.Text;
+import org.apache.commons.lang3.Validate;
+
+import java.util.function.Function;
+
+/**
+ * {@link ISliderController} for longs.
+ */
+public class LongSliderController implements ISliderController<Long> {
+ public static final Function<Long, Text> DEFAULT_FORMATTER = value -> Text.of(NumberFormatHelper.formatSmaller(value));
+
+ private final Option<Long> option;
+
+ private final long min, max, interval;
+
+ private final Function<Long, Text> valueFormatter;
+
+ /**
+ * Constructs a {@link ISliderController} for longs
+ * using the default value formatter {@link LongSliderController#DEFAULT_FORMATTER}.
+ *
+ * @param option bound option
+ * @param min minimum slider value
+ * @param max maximum slider value
+ * @param interval step size (or increments) for the slider
+ */
+ public LongSliderController(Option<Long> option, long min, long max, long interval) {
+ this(option, min, max, interval, DEFAULT_FORMATTER);
+ }
+
+ /**
+ * Constructs a {@link ISliderController} for longs.
+ *
+ * @param option bound option
+ * @param min minimum slider value
+ * @param max maximum slider value
+ * @param interval step size (or increments) for the slider
+ * @param valueFormatter format the value into any {@link Text}
+ */
+ public LongSliderController(Option<Long> option, long min, long max, long interval, Function<Long, Text> valueFormatter) {
+ Validate.isTrue(max > min, "`max` cannot be smaller than `min`");
+ Validate.isTrue(interval > 0, "`interval` must be more than 0");
+
+ this.option = option;
+ this.min = min;
+ this.max = max;
+ this.interval = interval;
+ this.valueFormatter = valueFormatter;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Option<Long> option() {
+ return option;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Text formatValue() {
+ return valueFormatter.apply(option().pendingValue());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double min() {
+ return min;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double max() {
+ return max;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double interval() {
+ return interval;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setPendingValue(double value) {
+ option().requestSet((long) value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double pendingValue() {
+ return option().pendingValue();
+ }
+
+}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/slider/SliderControlElement.java b/src/main/java/dev/isxander/yacl/gui/controllers/slider/SliderControllerElement.java
index b8dab71..139ef39 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/slider/SliderControlElement.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/slider/SliderControllerElement.java
@@ -1,14 +1,15 @@
package dev.isxander.yacl.gui.controllers.slider;
import dev.isxander.yacl.api.utils.Dimension;
-import dev.isxander.yacl.gui.controllers.ControlWidget;
+import dev.isxander.yacl.gui.controllers.ControllerWidget;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.text.Text;
import net.minecraft.util.math.MathHelper;
+import org.jetbrains.annotations.ApiStatus;
-public class SliderControlElement extends ControlWidget<ISliderControl<?>> {
+@ApiStatus.Internal
+public class SliderControllerElement extends ControllerWidget<ISliderController<?>> {
private final double min, max, interval;
private float interpolation;
@@ -17,7 +18,7 @@ public class SliderControlElement extends ControlWidget<ISliderControl<?>> {
private boolean mouseDown = false;
- public SliderControlElement(ISliderControl<?> option, Screen screen, Dimension<Integer> dim, double min, double max, double interval) {
+ public SliderControllerElement(ISliderController<?> option, Screen screen, Dimension<Integer> dim, double min, double max, double interval) {
super(option, screen, dim);
this.min = min;
this.max = max;
@@ -103,18 +104,13 @@ public class SliderControlElement extends ControlWidget<ISliderControl<?>> {
@Override
protected int getHoveredControlWidth() {
- int textWidth = textRenderer.getWidth(getValueText());
- return hovered ? sliderBounds.width() + textWidth + 6 + getThumbWidth() / 2 : textWidth ;
+ return sliderBounds.width() + getUnhoveredControlWidth() + 6 + getThumbWidth() / 2;
}
private void calculateInterpolation() {
interpolation = (float) ((control.pendingValue() - control.min()) * 1 / control.range());
}
- private Text getValueText() {
- return control.getValueText(control.pendingValue());
- }
-
private int getThumbX() {
return (int) (sliderBounds.x() + sliderBounds.width() * interpolation);
}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/slider/package-info.java b/src/main/java/dev/isxander/yacl/gui/controllers/slider/package-info.java
new file mode 100644
index 0000000..bff0d57
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/slider/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * This package contains implementations of sliders for different number types
+ * <ul>
+ * <li>For doubles: {@link dev.isxander.yacl.gui.controllers.slider.DoubleSliderController}</li>
+ * <li>For floats: {@link dev.isxander.yacl.gui.controllers.slider.FloatSliderController}</li>
+ * <li>For integers: {@link dev.isxander.yacl.gui.controllers.slider.IntegerSliderController}</li>
+ * <li>For longs: {@link dev.isxander.yacl.gui.controllers.slider.LongSliderController}</li>
+ * </ul>
+ */
+package dev.isxander.yacl.gui.controllers.slider;
diff --git a/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java b/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java
index 516a5ee..4104ff0 100644
--- a/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java
+++ b/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java
@@ -2,7 +2,7 @@ package dev.isxander.yacl.impl;
import dev.isxander.yacl.api.Binding;
import dev.isxander.yacl.api.ButtonOption;
-import dev.isxander.yacl.api.Control;
+import dev.isxander.yacl.api.Controller;
import net.minecraft.text.Text;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -15,19 +15,19 @@ public class ButtonOptionImpl implements ButtonOption {
private final Text name;
private final Text tooltip;
private final Runnable action;
- private final Control<Runnable> control;
+ private final Controller<Runnable> controller;
private final Binding<Runnable> binding;
public ButtonOptionImpl(
@NotNull Text name,
@Nullable Text tooltip,
@NotNull Runnable action,
- @NotNull Function<ButtonOption, Control<Runnable>> controlGetter
+ @NotNull Function<ButtonOption, Controller<Runnable>> controlGetter
) {
this.name = name;
this.tooltip = tooltip;
this.action = action;
- this.control = controlGetter.apply(this);
+ this.controller = controlGetter.apply(this);
this.binding = new EmptyBinderImpl();
}
@@ -37,7 +37,7 @@ public class ButtonOptionImpl implements ButtonOption {
}
@Override
- public @Nullable Text tooltip() {
+ public @NotNull Text tooltip() {
return tooltip;
}
@@ -47,8 +47,8 @@ public class ButtonOptionImpl implements ButtonOption {
}
@Override
- public @NotNull Control<Runnable> control() {
- return control;
+ public @NotNull Controller<Runnable> controller() {
+ return controller;
}
@Override
@@ -62,7 +62,7 @@ public class ButtonOptionImpl implements ButtonOption {
}
@Override
- public Runnable pendingValue() {
+ public @NotNull Runnable pendingValue() {
throw new UnsupportedOperationException();
}
diff --git a/src/main/java/dev/isxander/yacl/impl/OptionImpl.java b/src/main/java/dev/isxander/yacl/impl/OptionImpl.java
index a62726f..c61eaa1 100644
--- a/src/main/java/dev/isxander/yacl/impl/OptionImpl.java
+++ b/src/main/java/dev/isxander/yacl/impl/OptionImpl.java
@@ -1,7 +1,7 @@
package dev.isxander.yacl.impl;
import dev.isxander.yacl.api.Binding;
-import dev.isxander.yacl.api.Control;
+import dev.isxander.yacl.api.Controller;
import dev.isxander.yacl.api.Option;
import net.minecraft.text.Text;
import org.jetbrains.annotations.ApiStatus;
@@ -14,7 +14,7 @@ import java.util.function.Function;
public class OptionImpl<T> implements Option<T> {
private final Text name;
private final Text tooltip;
- private final Control<T> control;
+ private final Controller<T> controller;
private final Binding<T> binding;
private T pendingValue;
@@ -22,12 +22,12 @@ public class OptionImpl<T> implements Option<T> {
public OptionImpl(
@NotNull Text name,
@Nullable Text tooltip,
- @NotNull Function<Option<T>, Control<T>> controlGetter,
+ @NotNull Function<Option<T>, Controller<T>> controlGetter,
@NotNull Binding<T> binding
) {
this.name = name;
this.tooltip = tooltip;
- this.control = controlGetter.apply(this);
+ this.controller = controlGetter.apply(this);
this.binding = binding;
this.pendingValue = binding().getValue();
}
@@ -38,13 +38,13 @@ public class OptionImpl<T> implements Option<T> {
}
@Override
- public @Nullable Text tooltip() {
+ public @NotNull Text tooltip() {
return tooltip;
}
@Override
- public @NotNull Control<T> control() {
- return control;
+ public @NotNull Controller<T> controller() {
+ return controller;
}
@Override
@@ -58,7 +58,7 @@ public class OptionImpl<T> implements Option<T> {
}
@Override
- public T pendingValue() {
+ public @NotNull T pendingValue() {
return pendingValue;
}
diff --git a/src/main/java/dev/isxander/yacl/impl/utils/NumberFormatHelper.java b/src/main/java/dev/isxander/yacl/impl/utils/NumberFormatHelper.java
new file mode 100644
index 0000000..bbf44a1
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/impl/utils/NumberFormatHelper.java
@@ -0,0 +1,38 @@
+package dev.isxander.yacl.impl.utils;
+
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+@ApiStatus.Internal
+public class NumberFormatHelper {
+ private static final NavigableMap<Long, String> suffixes = new TreeMap<>();
+ static {
+ suffixes.put(1_000L, "K");
+ suffixes.put(1_000_000L, "M");
+ suffixes.put(1_000_000_000L, "B");
+ suffixes.put(1_000_000_000_000L, "T");
+ suffixes.put(1_000_000_000_000_000L, "P");
+ suffixes.put(1_000_000_000_000_000_000L, "E");
+ }
+
+ /**
+ * @author <a href="https://stackoverflow.com/a/30661479">assylias</a>
+ */
+ public static String formatSmaller(long value) {
+ // Long.MIN_VALUE == -Long.MIN_VALUE, so we need an adjustment here
+ if (value == Long.MIN_VALUE) return formatSmaller(Long.MIN_VALUE + 1);
+ if (value < 0) return "-" + formatSmaller(-value);
+ if (value < 1000) return Long.toString(value); //deal with easy case
+
+ Map.Entry<Long, String> e = suffixes.floorEntry(value);
+ Long divideBy = e.getKey();
+ String suffix = e.getValue();
+
+ long truncated = value / (divideBy / 10); //the number part of the output times 10
+ boolean hasDecimal = truncated < 100 && (truncated / 10d) != (truncated / 10);
+ return hasDecimal ? (truncated / 10d) + suffix : (truncated / 10) + suffix;
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl/mixin/SimpleOptionAccessor.java b/src/main/java/dev/isxander/yacl/mixin/SimpleOptionAccessor.java
new file mode 100644
index 0000000..9207225
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/mixin/SimpleOptionAccessor.java
@@ -0,0 +1,11 @@
+package dev.isxander.yacl.mixin;
+
+import net.minecraft.client.option.SimpleOption;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+@Mixin(SimpleOption.class)
+public interface SimpleOptionAccessor<T> {
+ @Accessor
+ T getDefaultValue();
+}