diff options
author | Kevin <92656833+kevinthegreat1@users.noreply.github.com> | 2023-11-18 04:07:39 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-18 09:07:39 +0000 |
commit | 442f48a6e28196910e92f460d3d677e3e47cbfc0 (patch) | |
tree | 3e34b77b7bf9707f8553a07abe629aab837a6130 | |
parent | 953645023c829735663493511390948bf69714f8 (diff) | |
download | YetAnotherConfigLib-442f48a6e28196910e92f460d3d677e3e47cbfc0.tar.gz YetAnotherConfigLib-442f48a6e28196910e92f460d3d677e3e47cbfc0.tar.bz2 YetAnotherConfigLib-442f48a6e28196910e92f460d3d677e3e47cbfc0.zip |
Add Enum Dropdown Controller (#117)
8 files changed, 174 insertions, 2 deletions
diff --git a/common/src/main/java/dev/isxander/yacl3/api/NameableEnum.java b/common/src/main/java/dev/isxander/yacl3/api/NameableEnum.java index 5a50207..425623f 100644 --- a/common/src/main/java/dev/isxander/yacl3/api/NameableEnum.java +++ b/common/src/main/java/dev/isxander/yacl3/api/NameableEnum.java @@ -3,7 +3,7 @@ package dev.isxander.yacl3.api; import net.minecraft.network.chat.Component; /** - * Used for the default value formatter of {@link dev.isxander.yacl3.gui.controllers.cycling.EnumController} + * Used for the default value formatter of {@link dev.isxander.yacl3.gui.controllers.cycling.EnumController} and {@link dev.isxander.yacl3.gui.controllers.dropdown.EnumDropdownController} */ public interface NameableEnum { Component getDisplayName(); diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/EnumDropdownControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/EnumDropdownControllerBuilder.java new file mode 100644 index 0000000..0814cc6 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/EnumDropdownControllerBuilder.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.EnumDropdownControllerBuilderImpl; + +public interface EnumDropdownControllerBuilder<E extends Enum<E>> extends ValueFormattableController<E, EnumDropdownControllerBuilder<E>> { + static <E extends Enum<E>> EnumDropdownControllerBuilder<E> create(Option<E> option) { + return new EnumDropdownControllerBuilderImpl<>(option); + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/AbstractDropdownController.java b/common/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/AbstractDropdownController.java index b4e4304..b913d15 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/AbstractDropdownController.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/AbstractDropdownController.java @@ -26,8 +26,12 @@ public abstract class AbstractDropdownController<T> implements IStringController this.allowAnyValue = allowAnyValue; } + protected AbstractDropdownController(Option<T> option, List<String> allowedValues) { + this(option, allowedValues, false, false); + } + protected AbstractDropdownController(Option<T> option) { - this(option, Collections.emptyList(), false, false); + this(option, Collections.emptyList()); } /** diff --git a/common/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/EnumDropdownController.java b/common/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/EnumDropdownController.java new file mode 100644 index 0000000..8e6e0e6 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/EnumDropdownController.java @@ -0,0 +1,92 @@ +package dev.isxander.yacl3.gui.controllers.dropdown; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.api.utils.Dimension; +import dev.isxander.yacl3.gui.AbstractWidget; +import dev.isxander.yacl3.gui.YACLScreen; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.stream.Stream; + +public class EnumDropdownController<E extends Enum<E>> extends AbstractDropdownController<E> { + /** + * The function used to convert enum constants to strings used for display, suggestion, and validation. Defaults to {@link Enum#toString}. + */ + protected final ValueFormatter<E> formatter; + + public EnumDropdownController(Option<E> option, ValueFormatter<E> formatter) { + super(option, Arrays.stream(option.pendingValue().getDeclaringClass().getEnumConstants()).map(formatter::format).map(Component::getString).toList()); + this.formatter = formatter; + } + + @Override + public String getString() { + return formatter.format(option().pendingValue()).getString(); + } + + @Override + public void setFromString(String value) { + option().requestSet(getEnumFromString(value)); + } + + /** + * Searches through enum constants for one whose {@link #formatter} result equals {@code value} + * + * @return The enum constant associated with the {@code value} or the pending value if none are found + * @implNote The return value of {@link #formatter} on each enum constant should be unique in order to ensure accuracy + */ + private E getEnumFromString(String value) { + value = value.toLowerCase(); + for (E constant : option().pendingValue().getDeclaringClass().getEnumConstants()) { + if (formatter.format(constant).getString().toLowerCase().equals(value)) return constant; + } + + return option().pendingValue(); + } + + @Override + public boolean isValueValid(String value) { + value = value.toLowerCase(); + for (String constant : getAllowedValues()) { + if (constant.equals(value)) return true; + } + + return false; + } + + @Override + protected String getValidValue(String value, int offset) { + return getValidEnumConstants(value) + .skip(offset) + .findFirst() + .orElseGet(this::getString); + } + + /** + * Filters and sorts through enum constants for those whose {@link #formatter} result equals {@code value} + * + * @return a sorted stream containing enum constants associated with the {@code value} + * @implNote The return value of {@link #formatter} on each enum constant should be unique in order to ensure accuracy + */ + @NotNull + protected Stream<String> getValidEnumConstants(String value) { + String valueLowerCase = value.toLowerCase(); + return getAllowedValues().stream() + .filter(constant -> constant.toLowerCase().contains(valueLowerCase)) + .sorted((s1, s2) -> { + String s1LowerCase = s1.toLowerCase(); + String s2LowerCase = s2.toLowerCase(); + if (s1LowerCase.startsWith(valueLowerCase) && !s2LowerCase.startsWith(valueLowerCase)) return -1; + if (!s1LowerCase.startsWith(valueLowerCase) && s2LowerCase.startsWith(valueLowerCase)) return 1; + return s1.compareTo(s2); + }); + } + + @Override + public AbstractWidget provideWidget(YACLScreen screen, Dimension<Integer> widgetDimension) { + return new EnumDropdownControllerElement<>(this, screen, widgetDimension); + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/EnumDropdownControllerElement.java b/common/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/EnumDropdownControllerElement.java new file mode 100644 index 0000000..2df6f6b --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/EnumDropdownControllerElement.java @@ -0,0 +1,25 @@ +package dev.isxander.yacl3.gui.controllers.dropdown; + +import dev.isxander.yacl3.api.utils.Dimension; +import dev.isxander.yacl3.gui.YACLScreen; + +import java.util.List; + +public class EnumDropdownControllerElement<E extends Enum<E>> extends AbstractDropdownControllerElement<E, String> { + private final EnumDropdownController<E> controller; + + public EnumDropdownControllerElement(EnumDropdownController<E> control, YACLScreen screen, Dimension<Integer> dim) { + super(control, screen, dim); + this.controller = control; + } + + @Override + public List<String> computeMatchingValues() { + return controller.getValidEnumConstants(inputField).toList(); + } + + @Override + public String getString(String object) { + return object; + } +} diff --git a/common/src/main/java/dev/isxander/yacl3/impl/controller/EnumDropdownControllerBuilderImpl.java b/common/src/main/java/dev/isxander/yacl3/impl/controller/EnumDropdownControllerBuilderImpl.java new file mode 100644 index 0000000..4ac063f --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/impl/controller/EnumDropdownControllerBuilderImpl.java @@ -0,0 +1,27 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.EnumDropdownControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.cycling.EnumController; +import dev.isxander.yacl3.gui.controllers.dropdown.EnumDropdownController; + +public class EnumDropdownControllerBuilderImpl<E extends Enum<E>> extends AbstractControllerBuilderImpl<E> implements EnumDropdownControllerBuilder<E> { + private ValueFormatter<E> formatter = EnumController.<E>getDefaultFormatter()::apply; + + public EnumDropdownControllerBuilderImpl(Option<E> option) { + super(option); + } + + @Override + public EnumDropdownControllerBuilder<E> formatValue(ValueFormatter<E> formatter) { + this.formatter = formatter; + return this; + } + + @Override + public Controller<E> build() { + return new EnumDropdownController<>(option, formatter); + } +} diff --git a/test-common/src/main/java/dev/isxander/yacl3/test/ConfigTest.java b/test-common/src/main/java/dev/isxander/yacl3/test/ConfigTest.java index a61a112..a8f49b0 100644 --- a/test-common/src/main/java/dev/isxander/yacl3/test/ConfigTest.java +++ b/test-common/src/main/java/dev/isxander/yacl3/test/ConfigTest.java @@ -4,6 +4,7 @@ import dev.isxander.yacl3.config.v2.api.ConfigClassHandler; import dev.isxander.yacl3.config.v2.api.SerialEntry; import dev.isxander.yacl3.config.v2.api.serializer.GsonConfigSerializerBuilder; import dev.isxander.yacl3.platform.YACLPlatform; +import net.minecraft.ChatFormatting; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; @@ -51,6 +52,8 @@ public class ConfigTest { public String stringSuggestions = ""; @SerialEntry public Item item = Items.OAK_LOG; + @SerialEntry + public ChatFormatting formattingOption = ChatFormatting.RED; @SerialEntry public List<String> stringList = List.of("This is quite cool.", "You can add multiple items!", "And it is integrated so well into Option groups!"); diff --git a/test-common/src/main/java/dev/isxander/yacl3/test/GuiTest.java b/test-common/src/main/java/dev/isxander/yacl3/test/GuiTest.java index a20312e..ec16e2e 100644 --- a/test-common/src/main/java/dev/isxander/yacl3/test/GuiTest.java +++ b/test-common/src/main/java/dev/isxander/yacl3/test/GuiTest.java @@ -14,6 +14,7 @@ import dev.isxander.yacl3.gui.controllers.string.number.DoubleFieldController; import dev.isxander.yacl3.gui.controllers.string.number.FloatFieldController; import dev.isxander.yacl3.gui.controllers.string.number.IntegerFieldController; import dev.isxander.yacl3.gui.controllers.string.number.LongFieldController; +import net.minecraft.ChatFormatting; import net.minecraft.Util; import net.minecraft.client.GraphicsStatus; import net.minecraft.client.Minecraft; @@ -24,6 +25,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.HoverEvent; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; +import org.apache.commons.lang3.StringUtils; import java.awt.Color; import java.nio.file.Path; @@ -268,6 +270,15 @@ public class GuiTest { ) .controller(ItemControllerBuilder::create) .build()) + .option(Option.<ChatFormatting>createBuilder() + .name(Component.literal("Enum Dropdown")) + .binding( + defaults.formattingOption, + () -> config.formattingOption, + (value) -> config.formattingOption = value + ) + .controller(option -> EnumDropdownControllerBuilder.create(option).formatValue(formatting -> Component.literal(StringUtils.capitalize(formatting.getName()).replaceAll("_", " ")))) + .build()) .build()) .group(OptionGroup.createBuilder() .name(Component.literal("Options that aren't really options")) |