From ad3e7955295d114ecbdaf07eb51ba26f0dc95d96 Mon Sep 17 00:00:00 2001 From: isXander Date: Tue, 15 Aug 2023 12:03:27 +0100 Subject: Complete the controllers (except number fields) --- .../yacl3/config/v2/api/OptionFactory.java | 15 ---- .../yacl3/config/v2/api/ReadOnlyFieldAccess.java | 2 + .../yacl3/config/v2/api/SimpleOptionFactory.java | 87 -------------------- .../yacl3/config/v2/api/autogen/ColorRGBA.java | 12 +++ .../yacl3/config/v2/api/autogen/EnumCycler.java | 16 ++++ .../yacl3/config/v2/api/autogen/ListGroup.java | 30 +++++++ .../yacl3/config/v2/api/autogen/LongSlider.java | 16 ++++ .../yacl3/config/v2/api/autogen/OptionFactory.java | 15 ++++ .../config/v2/api/autogen/SimpleOptionFactory.java | 87 ++++++++++++++++++++ .../yacl3/config/v2/api/autogen/StringField.java | 11 +++ .../config/v2/impl/ConfigClassHandlerImpl.java | 1 + .../yacl3/config/v2/impl/OptionStorageImpl.java | 37 --------- .../config/v2/impl/ReflectionFieldAccess.java | 5 ++ .../yacl3/config/v2/impl/autogen/BooleanImpl.java | 2 +- .../config/v2/impl/autogen/ColorRGBAImpl.java | 19 +++++ .../config/v2/impl/autogen/DoubleSliderImpl.java | 2 +- .../config/v2/impl/autogen/EnumCyclerImpl.java | 42 ++++++++++ .../config/v2/impl/autogen/FloatSliderImpl.java | 2 +- .../config/v2/impl/autogen/IntSliderImpl.java | 2 +- .../yacl3/config/v2/impl/autogen/LabelImpl.java | 2 +- .../config/v2/impl/autogen/ListGroupImpl.java | 95 ++++++++++++++++++++++ .../config/v2/impl/autogen/LongSliderImpl.java | 28 +++++++ .../config/v2/impl/autogen/MasterTickBoxImpl.java | 2 +- .../v2/impl/autogen/OptionFactoryRegistry.java | 8 +- .../config/v2/impl/autogen/OptionStorageImpl.java | 37 +++++++++ .../config/v2/impl/autogen/StringFieldImpl.java | 16 ++++ .../yacl3/config/v2/impl/autogen/TickBoxImpl.java | 2 +- .../v2/impl/autogen/YACLAutoGenException.java | 11 +++ 28 files changed, 456 insertions(+), 148 deletions(-) delete mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/api/OptionFactory.java delete mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/api/SimpleOptionFactory.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/ColorRGBA.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/EnumCycler.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/ListGroup.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/LongSlider.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/OptionFactory.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/SimpleOptionFactory.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/StringField.java delete mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/impl/OptionStorageImpl.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/ColorRGBAImpl.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/EnumCyclerImpl.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/ListGroupImpl.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/LongSliderImpl.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/OptionStorageImpl.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/StringFieldImpl.java create mode 100644 common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/YACLAutoGenException.java (limited to 'common/src/main/java') diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/api/OptionFactory.java b/common/src/main/java/dev/isxander/yacl3/config/v2/api/OptionFactory.java deleted file mode 100644 index 9d53e79..0000000 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/api/OptionFactory.java +++ /dev/null @@ -1,15 +0,0 @@ -package dev.isxander.yacl3.config.v2.api; - -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; -import dev.isxander.yacl3.config.v2.impl.autogen.OptionFactoryRegistry; - -import java.lang.annotation.Annotation; - -public interface OptionFactory { - Option createOption(A annotation, ConfigField field, OptionStorage storage); - - static void register(Class annotationClass, OptionFactory factory) { - OptionFactoryRegistry.registerOptionFactory(annotationClass, factory); - } -} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/api/ReadOnlyFieldAccess.java b/common/src/main/java/dev/isxander/yacl3/config/v2/api/ReadOnlyFieldAccess.java index 5b71e58..1146e68 100644 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/api/ReadOnlyFieldAccess.java +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/api/ReadOnlyFieldAccess.java @@ -8,4 +8,6 @@ public interface ReadOnlyFieldAccess { String name(); Type type(); + + Class typeClass(); } diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/api/SimpleOptionFactory.java b/common/src/main/java/dev/isxander/yacl3/config/v2/api/SimpleOptionFactory.java deleted file mode 100644 index 67b1d87..0000000 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/api/SimpleOptionFactory.java +++ /dev/null @@ -1,87 +0,0 @@ -package dev.isxander.yacl3.config.v2.api; - -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.api.OptionDescription; -import dev.isxander.yacl3.api.OptionFlag; -import dev.isxander.yacl3.api.controller.ControllerBuilder; -import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; -import dev.isxander.yacl3.config.v2.impl.FieldBackedBinding; -import net.minecraft.client.Minecraft; -import net.minecraft.locale.Language; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.Nullable; - -import java.lang.annotation.Annotation; -import java.util.Set; - -public abstract class SimpleOptionFactory implements OptionFactory { - @Override - public Option createOption(A annotation, ConfigField field, OptionStorage storage) { - Option option = Option.createBuilder() - .name(this.name(annotation, field, storage)) - .description(v -> this.description(v, annotation, field, storage).build()) - .binding(new FieldBackedBinding<>(field.access(), field.defaultAccess())) - .controller(opt -> this.createController(annotation, field, storage, opt)) - .available(this.available(annotation, field, storage)) - .flags(this.flags(annotation, field, storage)) - .listener((opt, v) -> this.listener(annotation, field, storage, opt, v)) - .build(); - - postInit(annotation, field, storage, option); - return option; - } - - protected abstract ControllerBuilder createController(A annotation, ConfigField field, OptionStorage storage, Option option); - - protected MutableComponent name(A annotation, ConfigField field, OptionStorage storage) { - return Component.translatable(this.getTranslationKey(field, null)); - } - - protected OptionDescription.Builder description(T value, A annotation, ConfigField field, OptionStorage storage) { - OptionDescription.Builder builder = OptionDescription.createBuilder(); - - String key = this.getTranslationKey(field, "desc"); - if (Language.getInstance().has(key)) { - builder.text(Component.translatable(key)); - } else { - key += "."; - int i = 0; - while (Language.getInstance().has(key + i++)) { - builder.text(Component.translatable(key + i)); - } - } - - String imagePath = "textures/yacl3/" + field.parent().id().getPath() + "/" + field.access().name() + ".webp"; - imagePath = imagePath.toLowerCase().replaceAll("[^a-z0-9/._:-]", "_"); - ResourceLocation imageLocation = new ResourceLocation(field.parent().id().getNamespace(), imagePath); - if (Minecraft.getInstance().getResourceManager().getResource(imageLocation).isPresent()) { - builder.webpImage(imageLocation); - } - - return builder; - } - - protected boolean available(A annotation, ConfigField field, OptionStorage storage) { - return true; - } - - protected Set flags(A annotation, ConfigField field, OptionStorage storage) { - return Set.of(); - } - - protected void listener(A annotation, ConfigField field, OptionStorage storage, Option option, T value) { - - } - - protected void postInit(A annotation, ConfigField field, OptionStorage storage, Option option) { - - } - - protected String getTranslationKey(ConfigField field, @Nullable String suffix) { - String key = "yacl3.config.%s.%s".formatted(field.parent().id().toString(), field.access().name()); - if (suffix != null) key += "." + suffix; - return key; - } -} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/ColorRGBA.java b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/ColorRGBA.java new file mode 100644 index 0000000..dc5b8ff --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/ColorRGBA.java @@ -0,0 +1,12 @@ +package dev.isxander.yacl3.config.v2.api.autogen; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface ColorRGBA { + boolean allowAlpha() default false; +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/EnumCycler.java b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/EnumCycler.java new file mode 100644 index 0000000..5545c13 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/EnumCycler.java @@ -0,0 +1,16 @@ +package dev.isxander.yacl3.config.v2.api.autogen; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface EnumCycler { + int[] allowedOrdinals() default {}; + + interface CyclableEnum> { + T[] allowedValues(); + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/ListGroup.java b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/ListGroup.java new file mode 100644 index 0000000..0853ba4 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/ListGroup.java @@ -0,0 +1,30 @@ +package dev.isxander.yacl3.config.v2.api.autogen; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.ControllerBuilder; +import dev.isxander.yacl3.config.v2.api.ConfigField; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface ListGroup { + Class> valueFactory(); + + Class> controllerFactory(); + + int maxEntries() default 0; + int minEntries() default 0; + + interface ValueFactory { + T provideNewValue(); + } + + interface ControllerFactory { + ControllerBuilder createController(ListGroup annotation, ConfigField> field, OptionStorage storage, Option option); + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/LongSlider.java b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/LongSlider.java new file mode 100644 index 0000000..c77b3d3 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/LongSlider.java @@ -0,0 +1,16 @@ +package dev.isxander.yacl3.config.v2.api.autogen; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface LongSlider { + long min(); + + long max(); + + long step(); +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/OptionFactory.java b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/OptionFactory.java new file mode 100644 index 0000000..2e3a537 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/OptionFactory.java @@ -0,0 +1,15 @@ +package dev.isxander.yacl3.config.v2.api.autogen; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.config.v2.api.ConfigField; +import dev.isxander.yacl3.config.v2.impl.autogen.OptionFactoryRegistry; + +import java.lang.annotation.Annotation; + +public interface OptionFactory { + Option createOption(A annotation, ConfigField field, OptionStorage storage); + + static void register(Class annotationClass, OptionFactory factory) { + OptionFactoryRegistry.registerOptionFactory(annotationClass, factory); + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/SimpleOptionFactory.java b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/SimpleOptionFactory.java new file mode 100644 index 0000000..c532f29 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/SimpleOptionFactory.java @@ -0,0 +1,87 @@ +package dev.isxander.yacl3.config.v2.api.autogen; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.OptionDescription; +import dev.isxander.yacl3.api.OptionFlag; +import dev.isxander.yacl3.api.controller.ControllerBuilder; +import dev.isxander.yacl3.config.v2.api.ConfigField; +import dev.isxander.yacl3.config.v2.impl.FieldBackedBinding; +import net.minecraft.client.Minecraft; +import net.minecraft.locale.Language; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; + +import java.lang.annotation.Annotation; +import java.util.Set; + +public abstract class SimpleOptionFactory implements OptionFactory { + @Override + public Option createOption(A annotation, ConfigField field, OptionStorage storage) { + Option option = Option.createBuilder() + .name(this.name(annotation, field, storage)) + .description(v -> this.description(v, annotation, field, storage).build()) + .binding(new FieldBackedBinding<>(field.access(), field.defaultAccess())) + .controller(opt -> this.createController(annotation, field, storage, opt)) + .available(this.available(annotation, field, storage)) + .flags(this.flags(annotation, field, storage)) + .listener((opt, v) -> this.listener(annotation, field, storage, opt, v)) + .build(); + + postInit(annotation, field, storage, option); + return option; + } + + protected abstract ControllerBuilder createController(A annotation, ConfigField field, OptionStorage storage, Option option); + + protected MutableComponent name(A annotation, ConfigField field, OptionStorage storage) { + return Component.translatable(this.getTranslationKey(field, null)); + } + + protected OptionDescription.Builder description(T value, A annotation, ConfigField field, OptionStorage storage) { + OptionDescription.Builder builder = OptionDescription.createBuilder(); + + String key = this.getTranslationKey(field, "desc"); + if (Language.getInstance().has(key)) { + builder.text(Component.translatable(key)); + } else { + key += "."; + int i = 0; + while (Language.getInstance().has(key + i++)) { + builder.text(Component.translatable(key + i)); + } + } + + String imagePath = "textures/yacl3/" + field.parent().id().getPath() + "/" + field.access().name() + ".webp"; + imagePath = imagePath.toLowerCase().replaceAll("[^a-z0-9/._:-]", "_"); + ResourceLocation imageLocation = new ResourceLocation(field.parent().id().getNamespace(), imagePath); + if (Minecraft.getInstance().getResourceManager().getResource(imageLocation).isPresent()) { + builder.webpImage(imageLocation); + } + + return builder; + } + + protected boolean available(A annotation, ConfigField field, OptionStorage storage) { + return true; + } + + protected Set flags(A annotation, ConfigField field, OptionStorage storage) { + return Set.of(); + } + + protected void listener(A annotation, ConfigField field, OptionStorage storage, Option option, T value) { + + } + + protected void postInit(A annotation, ConfigField field, OptionStorage storage, Option option) { + + } + + protected String getTranslationKey(ConfigField field, @Nullable String suffix) { + String key = "yacl3.config.%s.%s".formatted(field.parent().id().toString(), field.access().name()); + if (suffix != null) key += "." + suffix; + return key; + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/StringField.java b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/StringField.java new file mode 100644 index 0000000..c30a75e --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/api/autogen/StringField.java @@ -0,0 +1,11 @@ +package dev.isxander.yacl3.config.v2.api.autogen; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface StringField { +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/ConfigClassHandlerImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/ConfigClassHandlerImpl.java index b9274e9..7c0c2a9 100644 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/ConfigClassHandlerImpl.java +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/ConfigClassHandlerImpl.java @@ -5,6 +5,7 @@ import dev.isxander.yacl3.config.v2.api.*; import dev.isxander.yacl3.config.v2.api.autogen.AutoGen; import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; import dev.isxander.yacl3.config.v2.impl.autogen.OptionFactoryRegistry; +import dev.isxander.yacl3.config.v2.impl.autogen.OptionStorageImpl; import dev.isxander.yacl3.platform.YACLPlatform; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/OptionStorageImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/OptionStorageImpl.java deleted file mode 100644 index 3c75a11..0000000 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/OptionStorageImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -package dev.isxander.yacl3.config.v2.impl; - -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; - -public class OptionStorageImpl implements OptionStorage { - private final Map> storage = new HashMap<>(); - private final Map>> scheduledOperations = new HashMap<>(); - - @Override - public @Nullable Option getOption(String fieldName) { - return storage.get(fieldName); - } - - @Override - public void scheduleOptionOperation(String fieldName, Consumer> optionConsumer) { - if (storage.containsKey(fieldName)) { - optionConsumer.accept(storage.get(fieldName)); - } else { - scheduledOperations.merge(fieldName, optionConsumer, Consumer::andThen); - } - } - - public void putOption(String fieldName, Option option) { - storage.put(fieldName, option); - - Consumer> consumer = scheduledOperations.remove(fieldName); - if (consumer != null) { - consumer.accept(option); - } - } -} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/ReflectionFieldAccess.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/ReflectionFieldAccess.java index 114137f..7bba083 100644 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/ReflectionFieldAccess.java +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/ReflectionFieldAccess.java @@ -33,4 +33,9 @@ public record ReflectionFieldAccess(Field field, Object instance) implements public Type type() { return field.getGenericType(); } + + @Override + public Class typeClass() { + return (Class) field.getType(); + } } diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/BooleanImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/BooleanImpl.java index 0a24cf5..74eab90 100644 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/BooleanImpl.java +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/BooleanImpl.java @@ -4,7 +4,7 @@ import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.BooleanControllerBuilder; import dev.isxander.yacl3.api.controller.ControllerBuilder; import dev.isxander.yacl3.config.v2.api.ConfigField; -import dev.isxander.yacl3.config.v2.api.SimpleOptionFactory; +import dev.isxander.yacl3.config.v2.api.autogen.SimpleOptionFactory; import dev.isxander.yacl3.config.v2.api.autogen.Boolean; import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; import net.minecraft.network.chat.Component; diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/ColorRGBAImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/ColorRGBAImpl.java new file mode 100644 index 0000000..4bbeca1 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/ColorRGBAImpl.java @@ -0,0 +1,19 @@ +package dev.isxander.yacl3.config.v2.impl.autogen; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.ColorControllerBuilder; +import dev.isxander.yacl3.api.controller.ControllerBuilder; +import dev.isxander.yacl3.config.v2.api.ConfigField; +import dev.isxander.yacl3.config.v2.api.autogen.SimpleOptionFactory; +import dev.isxander.yacl3.config.v2.api.autogen.ColorRGBA; +import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; + +import java.awt.Color; + +public class ColorRGBAImpl extends SimpleOptionFactory { + @Override + protected ControllerBuilder createController(ColorRGBA annotation, ConfigField field, OptionStorage storage, Option option) { + return ColorControllerBuilder.create(option) + .allowAlpha(annotation.allowAlpha()); + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/DoubleSliderImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/DoubleSliderImpl.java index 87db158..1a397bd 100644 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/DoubleSliderImpl.java +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/DoubleSliderImpl.java @@ -4,7 +4,7 @@ import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.ControllerBuilder; import dev.isxander.yacl3.api.controller.DoubleSliderControllerBuilder; import dev.isxander.yacl3.config.v2.api.ConfigField; -import dev.isxander.yacl3.config.v2.api.SimpleOptionFactory; +import dev.isxander.yacl3.config.v2.api.autogen.SimpleOptionFactory; import dev.isxander.yacl3.config.v2.api.autogen.DoubleSlider; import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; import net.minecraft.locale.Language; diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/EnumCyclerImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/EnumCyclerImpl.java new file mode 100644 index 0000000..e42ab3f --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/EnumCyclerImpl.java @@ -0,0 +1,42 @@ +package dev.isxander.yacl3.config.v2.impl.autogen; + +import dev.isxander.yacl3.api.NameableEnum; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.ControllerBuilder; +import dev.isxander.yacl3.api.controller.CyclingListControllerBuilder; +import dev.isxander.yacl3.config.v2.api.ConfigField; +import dev.isxander.yacl3.config.v2.api.autogen.SimpleOptionFactory; +import dev.isxander.yacl3.config.v2.api.autogen.EnumCycler; +import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; +import net.minecraft.network.chat.Component; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.IntStream; + +public class EnumCyclerImpl extends SimpleOptionFactory> { + @Override + protected ControllerBuilder> createController(EnumCycler annotation, ConfigField> field, OptionStorage storage, Option> option) { + List> values; + + if (option.pendingValue() instanceof EnumCycler.CyclableEnum cyclableEnum) { + values = Arrays.asList(cyclableEnum.allowedValues()); + } else { + Enum[] constants = field.access().typeClass().getEnumConstants(); + values = IntStream.range(0, constants.length) + .filter(ordinal -> annotation.allowedOrdinals().length == 0 || Arrays.stream(annotation.allowedOrdinals()).noneMatch(allowed -> allowed == ordinal)) + .mapToObj(ordinal -> constants[ordinal]) + .toList(); + } + + // EnumController doesn't support filtering + var builder = CyclingListControllerBuilder.create(option) + .values(values); + if (NameableEnum.class.isAssignableFrom(field.access().typeClass())) { + builder.valueFormatter(v -> ((NameableEnum) v).getDisplayName()); + } else { + builder.valueFormatter(v -> Component.translatable(getTranslationKey(field, "fmt." + v.name().toLowerCase()))); + } + return builder; + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/FloatSliderImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/FloatSliderImpl.java index b856a7a..7a098e1 100644 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/FloatSliderImpl.java +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/FloatSliderImpl.java @@ -4,7 +4,7 @@ import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.ControllerBuilder; import dev.isxander.yacl3.api.controller.FloatSliderControllerBuilder; import dev.isxander.yacl3.config.v2.api.ConfigField; -import dev.isxander.yacl3.config.v2.api.SimpleOptionFactory; +import dev.isxander.yacl3.config.v2.api.autogen.SimpleOptionFactory; import dev.isxander.yacl3.config.v2.api.autogen.FloatSlider; import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; import net.minecraft.locale.Language; diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/IntSliderImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/IntSliderImpl.java index c03d370..5f6e88e 100644 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/IntSliderImpl.java +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/IntSliderImpl.java @@ -4,7 +4,7 @@ import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.ControllerBuilder; import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder; import dev.isxander.yacl3.config.v2.api.ConfigField; -import dev.isxander.yacl3.config.v2.api.SimpleOptionFactory; +import dev.isxander.yacl3.config.v2.api.autogen.SimpleOptionFactory; import dev.isxander.yacl3.config.v2.api.autogen.IntSlider; import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; import net.minecraft.locale.Language; diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/LabelImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/LabelImpl.java index c36c8b7..6585652 100644 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/LabelImpl.java +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/LabelImpl.java @@ -3,7 +3,7 @@ package dev.isxander.yacl3.config.v2.impl.autogen; import dev.isxander.yacl3.api.LabelOption; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.config.v2.api.ConfigField; -import dev.isxander.yacl3.config.v2.api.OptionFactory; +import dev.isxander.yacl3.config.v2.api.autogen.OptionFactory; import dev.isxander.yacl3.config.v2.api.autogen.Label; import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; import net.minecraft.network.chat.Component; diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/ListGroupImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/ListGroupImpl.java new file mode 100644 index 0000000..c94be2e --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/ListGroupImpl.java @@ -0,0 +1,95 @@ +package dev.isxander.yacl3.config.v2.impl.autogen; + +import dev.isxander.yacl3.api.ListOption; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.OptionDescription; +import dev.isxander.yacl3.config.v2.api.ConfigField; +import dev.isxander.yacl3.config.v2.api.autogen.ListGroup; +import dev.isxander.yacl3.config.v2.api.autogen.OptionFactory; +import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; +import dev.isxander.yacl3.config.v2.impl.FieldBackedBinding; +import net.minecraft.client.Minecraft; +import net.minecraft.locale.Language; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +public class ListGroupImpl implements OptionFactory> { + @Override + public Option> createOption(ListGroup annotation, ConfigField> field, OptionStorage storage) { + ListGroup.ValueFactory valueFactory = createValueFactory((Class>) annotation.valueFactory()); + ListGroup.ControllerFactory controllerFactory = createControllerFactory((Class>) annotation.controllerFactory()); + + return ListOption.createBuilder() + .name(Component.translatable(this.getTranslationKey(field, null))) + .description(this.description(field)) + .initial(valueFactory::provideNewValue) + .controller(opt -> controllerFactory.createController(annotation, field, storage, opt)) + .binding(new FieldBackedBinding<>(field.access(), field.defaultAccess())) + .build(); + } + + private OptionDescription description(ConfigField> field) { + OptionDescription.Builder builder = OptionDescription.createBuilder(); + + String key = this.getTranslationKey(field, "desc"); + if (Language.getInstance().has(key)) { + builder.text(Component.translatable(key)); + } else { + key += "."; + int i = 0; + while (Language.getInstance().has(key + i++)) { + builder.text(Component.translatable(key + i)); + } + } + + String imagePath = "textures/yacl3/" + field.parent().id().getPath() + "/" + field.access().name() + ".webp"; + imagePath = imagePath.toLowerCase().replaceAll("[^a-z0-9/._:-]", "_"); + ResourceLocation imageLocation = new ResourceLocation(field.parent().id().getNamespace(), imagePath); + if (Minecraft.getInstance().getResourceManager().getResource(imageLocation).isPresent()) { + builder.webpImage(imageLocation); + } + + return builder.build(); + } + + private ListGroup.ValueFactory createValueFactory(Class> clazz) { + Constructor> constructor; + try { + constructor = clazz.getConstructor(); + } catch (NoSuchMethodException e) { + throw new YACLAutoGenException("Could not find no-args constructor for `valueFactory` on '%s' for @ListGroup field.".formatted(clazz.getName()), e); + } + + try { + return constructor.newInstance(); + } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { + throw new YACLAutoGenException("Couldn't invoke no-args constructor for `valueFactory` on '%s' for @ListGroup field.".formatted(clazz.getName()), e); + } + } + + private ListGroup.ControllerFactory createControllerFactory(Class> clazz) { + Constructor> constructor; + try { + constructor = clazz.getConstructor(); + } catch (NoSuchMethodException e) { + throw new YACLAutoGenException("Could not find no-args constructor on `controllerFactory`, '%s' for @ListGroup field.".formatted(clazz.getName()), e); + } + + try { + return constructor.newInstance(); + } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { + throw new YACLAutoGenException("Couldn't invoke no-args constructor on `controllerFactory`, '%s' for @ListGroup field.".formatted(clazz.getName()), e); + } + } + + private String getTranslationKey(ConfigField> field, @Nullable String suffix) { + String key = "yacl3.config.%s.%s".formatted(field.parent().id().toString(), field.access().name()); + if (suffix != null) key += "." + suffix; + return key; + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/LongSliderImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/LongSliderImpl.java new file mode 100644 index 0000000..5850315 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/LongSliderImpl.java @@ -0,0 +1,28 @@ +package dev.isxander.yacl3.config.v2.impl.autogen; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.ControllerBuilder; +import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder; +import dev.isxander.yacl3.api.controller.LongSliderControllerBuilder; +import dev.isxander.yacl3.config.v2.api.ConfigField; +import dev.isxander.yacl3.config.v2.api.autogen.IntSlider; +import dev.isxander.yacl3.config.v2.api.autogen.LongSlider; +import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; +import dev.isxander.yacl3.config.v2.api.autogen.SimpleOptionFactory; +import net.minecraft.locale.Language; +import net.minecraft.network.chat.Component; + +public class LongSliderImpl extends SimpleOptionFactory { + @Override + protected ControllerBuilder createController(LongSlider annotation, ConfigField field, OptionStorage storage, Option option) { + return LongSliderControllerBuilder.create(option) + .valueFormatter(v -> { + String key = getTranslationKey(field, "fmt." + v); + if (Language.getInstance().has(key)) + return Component.translatable(key); + return Component.literal(Long.toString(v)); + }) + .range(annotation.min(), annotation.max()) + .step(annotation.step()); + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/MasterTickBoxImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/MasterTickBoxImpl.java index fa2b7b5..367b4d0 100644 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/MasterTickBoxImpl.java +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/MasterTickBoxImpl.java @@ -4,7 +4,7 @@ import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.ControllerBuilder; import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder; import dev.isxander.yacl3.config.v2.api.ConfigField; -import dev.isxander.yacl3.config.v2.api.SimpleOptionFactory; +import dev.isxander.yacl3.config.v2.api.autogen.SimpleOptionFactory; import dev.isxander.yacl3.config.v2.api.autogen.MasterTickBox; import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/OptionFactoryRegistry.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/OptionFactoryRegistry.java index 1762f2d..28d8bb3 100644 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/OptionFactoryRegistry.java +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/OptionFactoryRegistry.java @@ -2,10 +2,9 @@ package dev.isxander.yacl3.config.v2.impl.autogen; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.config.v2.api.ConfigField; -import dev.isxander.yacl3.config.v2.api.OptionFactory; +import dev.isxander.yacl3.config.v2.api.autogen.OptionFactory; import dev.isxander.yacl3.config.v2.api.autogen.*; import dev.isxander.yacl3.config.v2.api.autogen.Boolean; -import dev.isxander.yacl3.config.v2.impl.autogen.*; import dev.isxander.yacl3.impl.utils.YACLConstants; import java.lang.annotation.Annotation; @@ -22,9 +21,14 @@ public class OptionFactoryRegistry { registerOptionFactory(TickBox.class, new TickBoxImpl()); registerOptionFactory(Boolean.class, new BooleanImpl()); registerOptionFactory(IntSlider.class, new IntSliderImpl()); + registerOptionFactory(LongSlider.class, new LongSliderImpl()); registerOptionFactory(FloatSlider.class, new FloatSliderImpl()); registerOptionFactory(DoubleSlider.class, new DoubleSliderImpl()); + registerOptionFactory(EnumCycler.class, new EnumCyclerImpl()); + registerOptionFactory(StringField.class, new StringFieldImpl()); + registerOptionFactory(ColorRGBA.class, new ColorRGBAImpl()); registerOptionFactory(Label.class, new LabelImpl()); + registerOptionFactory(ListGroup.class, new ListGroupImpl<>()); registerOptionFactory(MasterTickBox.class, new MasterTickBoxImpl()); } diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/OptionStorageImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/OptionStorageImpl.java new file mode 100644 index 0000000..80147d8 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/OptionStorageImpl.java @@ -0,0 +1,37 @@ +package dev.isxander.yacl3.config.v2.impl.autogen; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +public class OptionStorageImpl implements OptionStorage { + private final Map> storage = new HashMap<>(); + private final Map>> scheduledOperations = new HashMap<>(); + + @Override + public @Nullable Option getOption(String fieldName) { + return storage.get(fieldName); + } + + @Override + public void scheduleOptionOperation(String fieldName, Consumer> optionConsumer) { + if (storage.containsKey(fieldName)) { + optionConsumer.accept(storage.get(fieldName)); + } else { + scheduledOperations.merge(fieldName, optionConsumer, Consumer::andThen); + } + } + + public void putOption(String fieldName, Option option) { + storage.put(fieldName, option); + + Consumer> consumer = scheduledOperations.remove(fieldName); + if (consumer != null) { + consumer.accept(option); + } + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/StringFieldImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/StringFieldImpl.java new file mode 100644 index 0000000..7d6c993 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/StringFieldImpl.java @@ -0,0 +1,16 @@ +package dev.isxander.yacl3.config.v2.impl.autogen; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.ControllerBuilder; +import dev.isxander.yacl3.api.controller.StringControllerBuilder; +import dev.isxander.yacl3.config.v2.api.ConfigField; +import dev.isxander.yacl3.config.v2.api.autogen.SimpleOptionFactory; +import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; +import dev.isxander.yacl3.config.v2.api.autogen.StringField; + +public class StringFieldImpl extends SimpleOptionFactory { + @Override + protected ControllerBuilder createController(StringField annotation, ConfigField field, OptionStorage storage, Option option) { + return StringControllerBuilder.create(option); + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/TickBoxImpl.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/TickBoxImpl.java index 202534e..72ef046 100644 --- a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/TickBoxImpl.java +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/TickBoxImpl.java @@ -4,7 +4,7 @@ import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.ControllerBuilder; import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder; import dev.isxander.yacl3.config.v2.api.ConfigField; -import dev.isxander.yacl3.config.v2.api.SimpleOptionFactory; +import dev.isxander.yacl3.config.v2.api.autogen.SimpleOptionFactory; import dev.isxander.yacl3.config.v2.api.autogen.OptionStorage; import dev.isxander.yacl3.config.v2.api.autogen.TickBox; diff --git a/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/YACLAutoGenException.java b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/YACLAutoGenException.java new file mode 100644 index 0000000..68b375d --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/config/v2/impl/autogen/YACLAutoGenException.java @@ -0,0 +1,11 @@ +package dev.isxander.yacl3.config.v2.impl.autogen; + +public class YACLAutoGenException extends RuntimeException { + public YACLAutoGenException(String message) { + super(message); + } + + public YACLAutoGenException(String message, Throwable e) { + super(message, e); + } +} -- cgit