diff options
9 files changed, 105 insertions, 64 deletions
diff --git a/common/src/main/java/dev/isxander/yacl3/gui/controllers/BooleanController.java b/common/src/main/java/dev/isxander/yacl3/gui/controllers/BooleanController.java index 6d2c2b3..cbd6ba5 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/controllers/BooleanController.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/controllers/BooleanController.java @@ -11,6 +11,7 @@ import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; import java.util.function.Function; @@ -72,6 +73,7 @@ public class BooleanController implements Controller<Boolean> { this.coloured = coloured; } + @ApiStatus.Internal public static BooleanController createInternal(Option<Boolean> option, ValueFormatter<Boolean> formatter, boolean coloured) { return new BooleanController(option, formatter::format, coloured); } diff --git a/common/src/main/java/dev/isxander/yacl3/gui/controllers/cycling/CyclingListController.java b/common/src/main/java/dev/isxander/yacl3/gui/controllers/cycling/CyclingListController.java index 28bc623..3fce3cf 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/controllers/cycling/CyclingListController.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/controllers/cycling/CyclingListController.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.ValueFormatter; import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; import java.util.function.Function; @@ -38,6 +39,7 @@ public class CyclingListController<T> implements ICyclingController<T> { this.values = ImmutableList.copyOf(values); } + @ApiStatus.Internal public static <T> CyclingListController<T> createInternal(Option<T> option, Iterable<? extends T> values, ValueFormatter<T> formatter) { return new CyclingListController<>(option, values, formatter::format); } diff --git a/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/StringControllerElement.java b/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/StringControllerElement.java index 383e188..689d8e2 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/StringControllerElement.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/StringControllerElement.java @@ -21,12 +21,14 @@ public class StringControllerElement extends ControllerWidget<IStringController< protected boolean inputFieldFocused; protected int caretPos; + protected int previousCaretPos; protected int selectionLength; protected int renderOffset; protected UndoRedoHelper undoRedoHelper; protected float ticks; + protected float caretTicks; private final Component emptyText; @@ -69,18 +71,22 @@ public class StringControllerElement extends ControllerWidget<IStringController< if (caretPos > text.length()) caretPos = text.length(); - int caretX = textX + textRenderer.width(text.substring(0, caretPos)) - 1; + int caretX = textX + textRenderer.width(text.substring(0, caretPos)); if (text.isEmpty()) caretX = inputFieldBounds.x() + inputFieldBounds.width() / 2; - if (ticks % 20 <= 10) { - graphics.fill(caretX, inputFieldBounds.y(), caretX + 1, inputFieldBounds.yLimit(), -1); - } - if (selectionLength != 0) { int selectionX = textX + textRenderer.width(text.substring(0, caretPos + selectionLength)); - graphics.fill(caretX, inputFieldBounds.y() - 1, selectionX, inputFieldBounds.yLimit(), 0x803030FF); + graphics.fill(caretX, inputFieldBounds.y() - 2, selectionX, inputFieldBounds.yLimit() - 1, 0x803030FF); } + + if(caretPos != previousCaretPos) { + previousCaretPos = caretPos; + caretTicks = 0; + } + + if ((caretTicks += delta) % 20 <= 10) + graphics.fill(caretX, inputFieldBounds.y() - 2, caretX + 1, inputFieldBounds.yLimit() - 1, -1); } } graphics.disableScissor(); @@ -119,7 +125,7 @@ public class StringControllerElement extends ControllerWidget<IStringController< return true; } else { - inputFieldFocused = false; + unfocus(); } return false; @@ -152,10 +158,13 @@ public class StringControllerElement extends ControllerWidget<IStringController< checkRenderOffset(); } else { if (caretPos > 0) { - if (selectionLength != 0) - caretPos += Math.min(selectionLength, 0); - else - caretPos--; + if (Screen.hasControlDown()) { + caretPos = findSpaceIndex(true); + } else { + if (selectionLength != 0) { + caretPos += Math.min(selectionLength, 0); + } else caretPos--; + } } checkRenderOffset(); selectionLength = 0; @@ -176,10 +185,13 @@ public class StringControllerElement extends ControllerWidget<IStringController< checkRenderOffset(); } else { if (caretPos < inputField.length()) { - if (selectionLength != 0) - caretPos += Math.max(selectionLength, 0); - else - caretPos++; + if (Screen.hasControlDown()) { + caretPos = findSpaceIndex(false); + } else { + if (selectionLength != 0) { + caretPos += Math.max(selectionLength, 0); + } else caretPos++; + } checkRenderOffset(); } selectionLength = 0; @@ -195,6 +207,25 @@ public class StringControllerElement extends ControllerWidget<IStringController< doDelete(); return true; } + case InputConstants.KEY_END -> { + if (Screen.hasShiftDown()) { + selectionLength -= inputField.length() - caretPos; + } else selectionLength = 0; + caretPos = inputField.length(); + checkRenderOffset(); + return true; + } + case InputConstants.KEY_HOME -> { + if (Screen.hasShiftDown()) { + selectionLength += caretPos; + caretPos = 0; + } else { + caretPos = 0; + selectionLength = 0; + } + checkRenderOffset(); + return true; + } // case InputConstants.KEY_Z -> { // if (Screen.hasControlDown()) { // UndoRedoHelper.FieldState updated = Screen.hasShiftDown() ? undoRedoHelper.redo() : undoRedoHelper.undo(); @@ -262,7 +293,7 @@ public class StringControllerElement extends ControllerWidget<IStringController< } int textX = getDimension().xLimit() - textRenderer.width(inputField) - getXPadding(); - int caretX = textX + textRenderer.width(inputField.substring(0, caretPos)) - 1; + int caretX = textX + textRenderer.width(inputField.substring(0, caretPos)); int minX = getDimension().xLimit() - getXPadding() - getUnshiftedLength(); int maxX = minX + getUnshiftedLength(); @@ -371,16 +402,14 @@ public class StringControllerElement extends ControllerWidget<IStringController< int fromIndex = caretPos; if (reverse) { if (caretPos > 0) - fromIndex -= 1; - i = this.inputField.lastIndexOf(" ", fromIndex); - - if (i == -1) i = 0; + fromIndex -= 2; + i = this.inputField.lastIndexOf(" ", fromIndex) + 1; } else { if (caretPos < inputField.length()) fromIndex += 1; - i = this.inputField.indexOf(" ", fromIndex); + i = this.inputField.indexOf(" ", fromIndex) + 1; - if (i == -1) i = inputField.length(); + if (i == 0) i = inputField.length(); } return i; diff --git a/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/DoubleFieldController.java b/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/DoubleFieldController.java index 8445839..1fe3e41 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/DoubleFieldController.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/DoubleFieldController.java @@ -4,6 +4,7 @@ import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.ValueFormatter; import dev.isxander.yacl3.gui.controllers.slider.DoubleSliderController; import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; import java.util.function.Function; @@ -63,6 +64,7 @@ public class DoubleFieldController extends NumberFieldController<Double> { this(option, -Double.MAX_VALUE, Double.MAX_VALUE, DoubleSliderController.DEFAULT_FORMATTER); } + @ApiStatus.Internal public static DoubleFieldController createInternal(Option<Double> option, double min, double max, ValueFormatter<Double> formatter) { return new DoubleFieldController(option, min, max, formatter::format); } @@ -88,7 +90,7 @@ public class DoubleFieldController extends NumberFieldController<Double> { */ @Override public String getString() { - return String.valueOf(option().pendingValue()); + return NUMBER_FORMAT.format(option().pendingValue()); } /** diff --git a/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/FloatFieldController.java b/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/FloatFieldController.java index 78281c8..8c81b49 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/FloatFieldController.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/FloatFieldController.java @@ -4,6 +4,7 @@ import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.ValueFormatter; import dev.isxander.yacl3.gui.controllers.slider.FloatSliderController; import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; import java.util.function.Function; @@ -63,6 +64,7 @@ public class FloatFieldController extends NumberFieldController<Float> { this(option, -Float.MAX_VALUE, Float.MAX_VALUE, FloatSliderController.DEFAULT_FORMATTER); } + @ApiStatus.Internal public static FloatFieldController createInternal(Option<Float> option, float min, float max, ValueFormatter<Float> formatter) { return new FloatFieldController(option, min, max, formatter::format); } @@ -88,7 +90,7 @@ public class FloatFieldController extends NumberFieldController<Float> { */ @Override public String getString() { - return String.valueOf(option().pendingValue()); + return NUMBER_FORMAT.format(option().pendingValue()); } /** diff --git a/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/IntegerFieldController.java b/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/IntegerFieldController.java index 476da9c..6286978 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/IntegerFieldController.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/IntegerFieldController.java @@ -4,6 +4,7 @@ import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.ValueFormatter; import dev.isxander.yacl3.gui.controllers.slider.IntegerSliderController; import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; import java.util.function.Function; @@ -63,15 +64,11 @@ public class IntegerFieldController extends NumberFieldController<Integer> { this(option, -Integer.MAX_VALUE, Integer.MAX_VALUE, IntegerSliderController.DEFAULT_FORMATTER); } + @ApiStatus.Internal public static IntegerFieldController createInternal(Option<Integer> option, int min, int max, ValueFormatter<Integer> formatter) { return new IntegerFieldController(option, min, max, formatter::format); } - @Override - public boolean isInputValid(String input) { - return input.matches("(?:-?\\d+|)"); - } - /** * {@inheritDoc} */ @@ -93,7 +90,7 @@ public class IntegerFieldController extends NumberFieldController<Integer> { */ @Override public String getString() { - return String.valueOf(option().pendingValue()); + return NUMBER_FORMAT.format(option().pendingValue()); } /** diff --git a/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/LongFieldController.java b/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/LongFieldController.java index d6f4f19..906a2b5 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/LongFieldController.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/LongFieldController.java @@ -4,6 +4,7 @@ import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.ValueFormatter; import dev.isxander.yacl3.gui.controllers.slider.LongSliderController; import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; import java.util.function.Function; @@ -63,15 +64,11 @@ public class LongFieldController extends NumberFieldController<Long> { this(option, -Long.MAX_VALUE, Long.MAX_VALUE, LongSliderController.DEFAULT_FORMATTER); } + @ApiStatus.Internal public static LongFieldController createInternal(Option<Long> option, long min, long max, ValueFormatter<Long> formatter) { return new LongFieldController(option, min, max, formatter::format); } - @Override - public boolean isInputValid(String input) { - return input.matches("(?:-?\\d+|)"); - } - /** * {@inheritDoc} */ @@ -93,7 +90,7 @@ public class LongFieldController extends NumberFieldController<Long> { */ @Override public String getString() { - return String.valueOf(option().pendingValue()); + return NUMBER_FORMAT.format(option().pendingValue()); } /** diff --git a/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/NumberFieldController.java b/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/NumberFieldController.java index b164ea3..3c06876 100644 --- a/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/NumberFieldController.java +++ b/common/src/main/java/dev/isxander/yacl3/gui/controllers/string/number/NumberFieldController.java @@ -8,10 +8,14 @@ import dev.isxander.yacl3.gui.YACLScreen; import dev.isxander.yacl3.gui.controllers.slider.ISliderController; import dev.isxander.yacl3.gui.controllers.string.IStringController; import dev.isxander.yacl3.gui.controllers.string.StringControllerElement; +import dev.isxander.yacl3.impl.utils.YACLConstants; import net.minecraft.network.chat.Component; import net.minecraft.util.Mth; import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.text.ParseException; +import java.text.ParsePosition; import java.util.function.Function; /** @@ -20,6 +24,10 @@ import java.util.function.Function; * @param <T> number type */ public abstract class NumberFieldController<T extends Number> implements ISliderController<T>, IStringController<T> { + + protected static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance(); + private static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = DecimalFormatSymbols.getInstance(); + private final Option<T> option; private final ValueFormatter<T> displayFormatter; @@ -35,8 +43,11 @@ public abstract class NumberFieldController<T extends Number> implements ISlider @Override public void setFromString(String value) { - if (value.isEmpty() || value.equals(".") || value.equals("-")) value = "0"; - setPendingValue(Mth.clamp(Double.parseDouble(cleanupNumberString(value)), min(), max())); + try { + setPendingValue(Mth.clamp(NUMBER_FORMAT.parse(value).doubleValue(), min(), max())); + } catch (ParseException ignore) { + YACLConstants.LOGGER.warn("Failed to parse number: {}", value); + } } @Override @@ -46,7 +57,10 @@ public abstract class NumberFieldController<T extends Number> implements ISlider @Override public boolean isInputValid(String input) { - return input.matches("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)|[.]||-"); + input = input.replace(DECIMAL_FORMAT_SYMBOLS.getGroupingSeparator() + "", ""); + ParsePosition parsePosition = new ParsePosition(0); + NUMBER_FORMAT.parse(input, parsePosition); + return parsePosition.getIndex() == input.length(); } @Override @@ -59,10 +73,6 @@ public abstract class NumberFieldController<T extends Number> implements ISlider return new StringControllerElement(this, screen, widgetDimension, false); } - protected String cleanupNumberString(String number) { - return number.replace(String.valueOf(DecimalFormatSymbols.getInstance().getGroupingSeparator()), ""); - } - @Override public double interval() { return -1; 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 31942be..3b5c122 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 @@ -105,7 +105,7 @@ public class GuiTest { .name(Component.literal("Custom Boolean Toggle")) .description(val -> OptionDescription.createBuilder() .text(Component.literal("You can customize controllers like so! YACL is truly infinitely customizable! This tooltip is long in order to demonstrate the cool, smooth scrolling of these descriptions. Did you know, they are also super clickable?! I know, cool right, YACL 3.x really is amazing.")) - .image(Path.of("D:\\Xander\\Downloads\\_MG_0860-Enhanced-NR.png"), new ResourceLocation("yacl", "f.webp")) + .image(Path.of("D:\\Xander\\Downloads\\_MG_0860-Enhanced-NR.png"), new ResourceLocation("yacl", "f.webp")) // TODO: Add img file to git? .build()) .binding( defaults.customBooleanToggle, @@ -113,7 +113,7 @@ public class GuiTest { (value) -> config.customBooleanToggle = value ) .controller(opt -> BooleanControllerBuilder.create(opt) - .valueFormatter(state -> state ? Component.literal("Amazing") : Component.literal("Not Amazing")) + .formatValue(state -> state ? Component.literal("Amazing") : Component.literal("Not Amazing")) .coloured(true)) .listener((opt, val) -> booleanOption.get().setAvailable(val)) .build()) @@ -130,7 +130,7 @@ public class GuiTest { .build()) .group(OptionGroup.createBuilder() .name(Component.literal("Slider Controllers")) - .option(Option.createBuilder(int.class) + .option(Option.<Integer>createBuilder() .name(Component.literal("Int Slider")) .instant(true) .binding( @@ -141,7 +141,7 @@ public class GuiTest { ) .customController(opt -> new IntegerSliderController(opt, 0, 3, 1)) .build()) - .option(Option.createBuilder(double.class) + .option(Option.<Double>createBuilder() .name(Component.literal("Double Slider")) .binding( defaults.doubleSlider, @@ -150,7 +150,7 @@ public class GuiTest { ) .customController(opt -> new DoubleSliderController(opt, 0, 3, 0.05)) .build()) - .option(Option.createBuilder(float.class) + .option(Option.<Float>createBuilder() .name(Component.literal("Float Slider")) .binding( defaults.floatSlider, @@ -159,7 +159,7 @@ public class GuiTest { ) .customController(opt -> new FloatSliderController(opt, 0, 3, 0.1f)) .build()) - .option(Option.createBuilder(long.class) + .option(Option.<Long>createBuilder() .name(Component.literal("Long Slider")) .binding( defaults.longSlider, @@ -171,7 +171,7 @@ public class GuiTest { .build()) .group(OptionGroup.createBuilder() .name(Component.literal("Input Field Controllers")) - .option(Option.createBuilder(String.class) + .option(Option.<String>createBuilder() .name(Component.literal("Component Option")) .binding( defaults.textField, @@ -180,7 +180,7 @@ public class GuiTest { ) .customController(StringController::new) .build()) - .option(Option.createBuilder(Color.class) + .option(Option.<Color>createBuilder() .name(Component.literal("Color Option")) .binding( defaults.colorOption, @@ -192,7 +192,7 @@ public class GuiTest { .build()) .group(OptionGroup.createBuilder() .name(Component.literal("Number Fields")) - .option(Option.createBuilder(double.class) + .option(Option.<Double>createBuilder() .name(Component.literal("Double Field")) .binding( defaults.doubleField, @@ -201,7 +201,7 @@ public class GuiTest { ) .customController(DoubleFieldController::new) .build()) - .option(Option.createBuilder(float.class) + .option(Option.<Float>createBuilder() .name(Component.literal("Float Field")) .binding( defaults.floatField, @@ -210,7 +210,7 @@ public class GuiTest { ) .customController(FloatFieldController::new) .build()) - .option(Option.createBuilder(int.class) + .option(Option.<Integer>createBuilder() .name(Component.literal("Integer Field")) .binding( defaults.intField, @@ -219,7 +219,7 @@ public class GuiTest { ) .customController(IntegerFieldController::new) .build()) - .option(Option.createBuilder(long.class) + .option(Option.<Long>createBuilder() .name(Component.literal("Long Field")) .binding( defaults.longField, @@ -231,7 +231,7 @@ public class GuiTest { .build()) .group(OptionGroup.createBuilder() .name(Component.literal("Enum Controllers")) - .option(Option.createBuilder(ConfigTest.Alphabet.class) + .option(Option.<ConfigTest.Alphabet>createBuilder() .name(Component.literal("Enum Cycler")) .binding( defaults.enumOption, @@ -294,7 +294,7 @@ public class GuiTest { .group(OptionGroup.createBuilder() .name(Component.literal("Minecraft Bindings")) .description(OptionDescription.of(Component.literal("YACL can also bind Minecraft options!"))) - .option(Option.createBuilder(boolean.class) + .option(Option.<Boolean>createBuilder() .name(Component.literal("Minecraft AutoJump")) .description(OptionDescription.of(Component.literal("You can even bind minecraft options!"))) .binding(Binding.minecraft(Minecraft.getInstance().options.autoJump())) @@ -309,7 +309,7 @@ public class GuiTest { .build()) .category(ConfigCategory.createBuilder() .name(Component.literal("List Test")) - .group(ListOption.createBuilder(String.class) + .group(ListOption.<String>createBuilder() .name(Component.literal("String List")) .binding( defaults.stringList, @@ -334,7 +334,7 @@ public class GuiTest { .initial(0) .available(false) .build()) - .group(ListOption.createBuilder(Component.class) + .group(ListOption.<Component>createBuilder() .name(Component.literal("Useless Label List")) .binding(Binding.immutable(List.of(Component.literal("It's quite impressive that literally every single controller works, without problem.")))) .customController(LabelController::new) @@ -343,7 +343,7 @@ public class GuiTest { .build()) .category(ConfigCategory.createBuilder() .name(Component.literal("Group Test")) - .option(Option.createBuilder(boolean.class) + .option(Option.<Boolean>createBuilder() .name(Component.literal("Root Test")) .binding( defaults.groupTestRoot, @@ -354,7 +354,7 @@ public class GuiTest { .build()) .group(OptionGroup.createBuilder() .name(Component.literal("First Group")) - .option(Option.createBuilder(boolean.class) + .option(Option.<Boolean>createBuilder() .name(Component.literal("First Group Test 1")) .binding( defaults.groupTestFirstGroup, @@ -363,7 +363,7 @@ public class GuiTest { ) .customController(TickBoxController::new) .build()) - .option(Option.createBuilder(boolean.class) + .option(Option.<Boolean>createBuilder() .name(Component.literal("First Group Test 2")) .binding( defaults.groupTestFirstGroup2, @@ -375,7 +375,7 @@ public class GuiTest { .build()) .group(OptionGroup.createBuilder() .name(Component.empty()) - .option(Option.createBuilder(boolean.class) + .option(Option.<Boolean>createBuilder() .name(Component.literal("Second Group Test")) .binding( defaults.groupTestSecondGroup, @@ -437,7 +437,7 @@ public class GuiTest { .group(OptionGroup.createBuilder() .name(Component.literal("Name of the group")) .description(OptionDescription.of(Component.literal("This Component will appear when you hover over the name or focus on the collapse button with Tab."))) - .option(Option.createBuilder(boolean.class) + .option(Option.<Boolean>createBuilder() .name(Component.literal("Boolean Option")) .description(OptionDescription.of(Component.literal("This Component will appear as a tooltip when you hover over the option."))) .binding(true, () -> myBooleanOption, newVal -> myBooleanOption = newVal) |