diff options
author | isXander <isxander@users.noreply.github.com> | 2022-12-03 18:57:03 +0000 |
---|---|---|
committer | isXander <isxander@users.noreply.github.com> | 2022-12-03 18:57:03 +0000 |
commit | daf3168671bc9c0cd64693d7129cddfdb1a08bb2 (patch) | |
tree | a128054f311448d806da0838ec2a4cd4bdbc7241 /src | |
parent | b953bd7cc37dcd32da9da85baa5f1b83646d5abb (diff) | |
download | YetAnotherConfigLib-daf3168671bc9c0cd64693d7129cddfdb1a08bb2.tar.gz YetAnotherConfigLib-daf3168671bc9c0cd64693d7129cddfdb1a08bb2.tar.bz2 YetAnotherConfigLib-daf3168671bc9c0cd64693d7129cddfdb1a08bb2.zip |
number fields + StringControllerElement improvements
when highlighted text, arrow keys go to each side of the selection
switch to InputUtil instead of GLFW
bump yarn
Diffstat (limited to 'src')
21 files changed, 643 insertions, 63 deletions
diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/ActionController.java b/src/client/java/dev/isxander/yacl/gui/controllers/ActionController.java index b8e2cd1..7666dff 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/ActionController.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/ActionController.java @@ -5,8 +5,8 @@ import dev.isxander.yacl.api.Controller; import dev.isxander.yacl.api.utils.Dimension; import dev.isxander.yacl.gui.AbstractWidget; import dev.isxander.yacl.gui.YACLScreen; +import net.minecraft.client.util.InputUtil; import net.minecraft.text.Text; -import org.lwjgl.glfw.GLFW; import java.util.function.BiConsumer; @@ -94,7 +94,7 @@ public class ActionController implements Controller<BiConsumer<YACLScreen, Butto return false; } - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_SPACE || keyCode == GLFW.GLFW_KEY_KP_ENTER) { + if (keyCode == InputUtil.GLFW_KEY_ENTER || keyCode == InputUtil.GLFW_KEY_SPACE || keyCode == InputUtil.GLFW_KEY_KP_ENTER) { executeAction(); return true; } diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/BooleanController.java b/src/client/java/dev/isxander/yacl/gui/controllers/BooleanController.java index b696831..3a8e5c3 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/BooleanController.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/BooleanController.java @@ -5,6 +5,7 @@ import dev.isxander.yacl.api.Option; import dev.isxander.yacl.api.utils.Dimension; import dev.isxander.yacl.gui.AbstractWidget; import dev.isxander.yacl.gui.YACLScreen; +import net.minecraft.client.util.InputUtil; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.screen.ScreenTexts; import net.minecraft.text.Text; @@ -146,7 +147,7 @@ public class BooleanController implements Controller<Boolean> { return false; } - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_SPACE || keyCode == GLFW.GLFW_KEY_KP_ENTER) { + if (keyCode == InputUtil.GLFW_KEY_ENTER || keyCode == InputUtil.GLFW_KEY_SPACE || keyCode == InputUtil.GLFW_KEY_KP_ENTER) { toggleSetting(); return true; } diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/ColorController.java b/src/client/java/dev/isxander/yacl/gui/controllers/ColorController.java index a68475d..6853a03 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/ColorController.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/ColorController.java @@ -108,7 +108,7 @@ public class ColorController implements IStringController<Color> { private final List<Character> allowedChars; public ColorControllerElement(ColorController control, YACLScreen screen, Dimension<Integer> dim) { - super(control, screen, dim); + super(control, screen, dim, true); this.colorController = control; this.allowedChars = ImmutableList.of('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); } @@ -135,21 +135,22 @@ public class ColorController implements IStringController<Color> { if (caretPos == 0) return; - string = string.substring(0, Math.min(inputField.length() - caretPos, string.length())); + String trimmed = string.substring(0, Math.min(inputField.length() - caretPos, string.length())); - inputField.replace(caretPos, caretPos + string.length(), string); - caretPos += string.length(); - setSelectionLength(); - - updateControl(); + if (modifyInput(builder -> builder.replace(caretPos, caretPos + trimmed.length(), trimmed))) { + caretPos += trimmed.length(); + setSelectionLength(); + updateControl(); + } } @Override protected void doBackspace() { if (caretPos > 1) { - inputField.setCharAt(caretPos - 1, '0'); - caretPos--; - updateControl(); + if (modifyInput(builder -> builder.setCharAt(caretPos - 1, '0'))) { + caretPos--; + updateControl(); + } } } diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/TickBoxController.java b/src/client/java/dev/isxander/yacl/gui/controllers/TickBoxController.java index 340983d..b0ae449 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/TickBoxController.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/TickBoxController.java @@ -6,9 +6,9 @@ import dev.isxander.yacl.api.utils.Dimension; import dev.isxander.yacl.gui.AbstractWidget; import dev.isxander.yacl.gui.YACLScreen; import net.minecraft.client.gui.DrawableHelper; +import net.minecraft.client.util.InputUtil; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Text; -import org.lwjgl.glfw.GLFW; /** * This controller renders a tickbox @@ -109,7 +109,7 @@ public class TickBoxController implements Controller<Boolean> { return false; } - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_SPACE || keyCode == GLFW.GLFW_KEY_KP_ENTER) { + if (keyCode == InputUtil.GLFW_KEY_ENTER || keyCode == InputUtil.GLFW_KEY_SPACE || keyCode == InputUtil.GLFW_KEY_KP_ENTER) { toggleSetting(); return true; } diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/cycling/CyclingControllerElement.java b/src/client/java/dev/isxander/yacl/gui/controllers/cycling/CyclingControllerElement.java index ab0a9c3..246fbec 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/cycling/CyclingControllerElement.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/cycling/CyclingControllerElement.java @@ -4,7 +4,7 @@ import dev.isxander.yacl.api.utils.Dimension; import dev.isxander.yacl.gui.YACLScreen; import dev.isxander.yacl.gui.controllers.ControllerWidget; import net.minecraft.client.gui.screen.Screen; -import org.lwjgl.glfw.GLFW; +import net.minecraft.client.util.InputUtil; public class CyclingControllerElement extends ControllerWidget<ICyclingController<?>> { @@ -39,11 +39,11 @@ public class CyclingControllerElement extends ControllerWidget<ICyclingControlle return false; switch (keyCode) { - case GLFW.GLFW_KEY_LEFT, GLFW.GLFW_KEY_DOWN -> + case InputUtil.GLFW_KEY_LEFT, InputUtil.GLFW_KEY_DOWN -> cycleValue(-1); - case GLFW.GLFW_KEY_RIGHT, GLFW.GLFW_KEY_UP -> + case InputUtil.GLFW_KEY_RIGHT, InputUtil.GLFW_KEY_UP -> cycleValue(1); - case GLFW.GLFW_KEY_ENTER, GLFW.GLFW_KEY_SPACE, GLFW.GLFW_KEY_KP_ENTER -> + case InputUtil.GLFW_KEY_ENTER, InputUtil.GLFW_KEY_SPACE, InputUtil.GLFW_KEY_KP_ENTER -> cycleValue(Screen.hasControlDown() || Screen.hasShiftDown() ? -1 : 1); default -> { return false; diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/slider/DoubleSliderController.java b/src/client/java/dev/isxander/yacl/gui/controllers/slider/DoubleSliderController.java index b530e8c..54c7476 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/slider/DoubleSliderController.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/slider/DoubleSliderController.java @@ -13,7 +13,7 @@ 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)); + public static final Function<Double, Text> DEFAULT_FORMATTER = value -> Text.of(String.format("%,.2f", value).replaceAll("[\u00a0\u202F]", " ")); private final Option<Double> option; diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/slider/FloatSliderController.java b/src/client/java/dev/isxander/yacl/gui/controllers/slider/FloatSliderController.java index d7c203e..84ca9a2 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/slider/FloatSliderController.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/slider/FloatSliderController.java @@ -13,7 +13,7 @@ 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)); + public static final Function<Float, Text> DEFAULT_FORMATTER = value -> Text.of(String.format("%,.1f", value).replaceAll("[\u00a0\u202F]", " ")); private final Option<Float> option; diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderController.java b/src/client/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderController.java index a8bca7c..50ec9d2 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderController.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderController.java @@ -10,7 +10,7 @@ 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(String.format("%,d", value)); + public static final Function<Integer, Text> DEFAULT_FORMATTER = value -> Text.of(String.format("%,d", value).replaceAll("[\u00a0\u202F]", " ")); private final Option<Integer> option; diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/slider/LongSliderController.java b/src/client/java/dev/isxander/yacl/gui/controllers/slider/LongSliderController.java index 50559d5..3038402 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/slider/LongSliderController.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/slider/LongSliderController.java @@ -10,7 +10,7 @@ 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(String.format("%,d", value)); + public static final Function<Long, Text> DEFAULT_FORMATTER = value -> Text.of(String.format("%,d", value).replaceAll("[\u00a0\u202F]", " ")); private final Option<Long> option; diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/slider/SliderControllerElement.java b/src/client/java/dev/isxander/yacl/gui/controllers/slider/SliderControllerElement.java index 913cc00..c78e0eb 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/slider/SliderControllerElement.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/slider/SliderControllerElement.java @@ -5,9 +5,9 @@ import dev.isxander.yacl.gui.YACLScreen; 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.InputUtil; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.MathHelper; -import org.lwjgl.glfw.GLFW; public class SliderControllerElement extends ControllerWidget<ISliderController<?>> { private final double min, max, interval; @@ -104,8 +104,8 @@ public class SliderControllerElement extends ControllerWidget<ISliderController< return false; switch (keyCode) { - case GLFW.GLFW_KEY_LEFT, GLFW.GLFW_KEY_DOWN -> incrementValue(-1); - case GLFW.GLFW_KEY_RIGHT, GLFW.GLFW_KEY_UP -> incrementValue(1); + case InputUtil.GLFW_KEY_LEFT, InputUtil.GLFW_KEY_DOWN -> incrementValue(-1); + case InputUtil.GLFW_KEY_RIGHT, InputUtil.GLFW_KEY_UP -> incrementValue(1); default -> { return false; } diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/string/IStringController.java b/src/client/java/dev/isxander/yacl/gui/controllers/string/IStringController.java index 41843b8..553e278 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/string/IStringController.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/string/IStringController.java @@ -2,6 +2,9 @@ package dev.isxander.yacl.gui.controllers.string; import dev.isxander.yacl.api.Controller; import dev.isxander.yacl.api.Option; +import dev.isxander.yacl.api.utils.Dimension; +import dev.isxander.yacl.gui.AbstractWidget; +import dev.isxander.yacl.gui.YACLScreen; import net.minecraft.text.Text; /** @@ -29,4 +32,13 @@ public interface IStringController<T> extends Controller<T> { default Text formatValue() { return Text.of(getString()); } + + default boolean isInputValid(String input) { + return true; + } + + @Override + default AbstractWidget provideWidget(YACLScreen screen, Dimension<Integer> widgetDimension) { + return new StringControllerElement(this, screen, widgetDimension, true); + } } diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/string/StringController.java b/src/client/java/dev/isxander/yacl/gui/controllers/string/StringController.java index 0caaa93..3a07641 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/string/StringController.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/string/StringController.java @@ -37,9 +37,4 @@ public class StringController implements IStringController<String> { public void setFromString(String value) { option().requestSet(value); } - - @Override - public AbstractWidget provideWidget(YACLScreen screen, Dimension<Integer> widgetDimension) { - return new StringControllerElement(this, screen, widgetDimension); - } } diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/string/StringControllerElement.java b/src/client/java/dev/isxander/yacl/gui/controllers/string/StringControllerElement.java index 0c3b7c9..bce6906 100644 --- a/src/client/java/dev/isxander/yacl/gui/controllers/string/StringControllerElement.java +++ b/src/client/java/dev/isxander/yacl/gui/controllers/string/StringControllerElement.java @@ -5,13 +5,17 @@ import dev.isxander.yacl.gui.YACLScreen; 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.InputUtil; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Text; import net.minecraft.util.Formatting; -import org.lwjgl.glfw.GLFW; + +import java.util.function.Consumer; public class StringControllerElement extends ControllerWidget<IStringController<?>> { - protected StringBuilder inputField; + protected final boolean instantApply; + + protected String inputField; protected Dimension<Integer> inputFieldBounds; protected boolean inputFieldFocused; @@ -22,12 +26,14 @@ public class StringControllerElement extends ControllerWidget<IStringController< private final Text emptyText; - public StringControllerElement(IStringController<?> control, YACLScreen screen, Dimension<Integer> dim) { + public StringControllerElement(IStringController<?> control, YACLScreen screen, Dimension<Integer> dim, boolean instantApply) { super(control, screen, dim); - inputField = new StringBuilder(control.getString()); + this.instantApply = instantApply; + inputField = control.getString(); inputFieldFocused = false; selectionLength = 0; emptyText = Text.literal("Click to type...").formatted(Formatting.GRAY); + control.option().addListener((opt, val) -> inputField = control.getString()); setDimension(dim); } @@ -35,12 +41,14 @@ public class StringControllerElement extends ControllerWidget<IStringController< protected void drawHoveredControl(MatrixStack matrices, int mouseX, int mouseY, float delta) { ticks += delta; + String text = instantApply ? control.getString() : inputField; + DrawableHelper.fill(matrices, inputFieldBounds.x(), inputFieldBounds.yLimit(), inputFieldBounds.xLimit(), inputFieldBounds.yLimit() + 1, -1); DrawableHelper.fill(matrices, inputFieldBounds.x() + 1, inputFieldBounds.yLimit() + 1, inputFieldBounds.xLimit() + 1, inputFieldBounds.yLimit() + 2, 0xFF404040); if (inputFieldFocused || focused) { - int caretX = inputFieldBounds.x() + textRenderer.getWidth(control.getString().substring(0, caretPos)) - 1; - if (inputField.isEmpty()) + int caretX = inputFieldBounds.x() + textRenderer.getWidth(text.substring(0, caretPos)) - 1; + if (text.isEmpty()) caretX += inputFieldBounds.width() / 2; if (ticks % 20 <= 10) { @@ -48,7 +56,7 @@ public class StringControllerElement extends ControllerWidget<IStringController< } if (selectionLength != 0) { - int selectionX = inputFieldBounds.x() + textRenderer.getWidth(control.getString().substring(0, caretPos + selectionLength)); + int selectionX = inputFieldBounds.x() + textRenderer.getWidth(text.substring(0, caretPos + selectionLength)); DrawableHelper.fill(matrices, caretX, inputFieldBounds.y() - 1, selectionX, inputFieldBounds.yLimit(), 0x803030FF); } } @@ -83,11 +91,11 @@ public class StringControllerElement extends ControllerWidget<IStringController< return false; switch (keyCode) { - case GLFW.GLFW_KEY_ESCAPE -> { - inputFieldFocused = false; + case InputUtil.GLFW_KEY_ESCAPE, InputUtil.GLFW_KEY_ENTER -> { + unfocus(); return true; } - case GLFW.GLFW_KEY_LEFT -> { + case InputUtil.GLFW_KEY_LEFT -> { if (Screen.hasShiftDown()) { if (Screen.hasControlDown()) { int spaceChar = findSpaceIndex(true); @@ -98,14 +106,18 @@ public class StringControllerElement extends ControllerWidget<IStringController< selectionLength += 1; } } else { - if (caretPos > 0) - caretPos--; + if (caretPos > 0) { + if (selectionLength != 0) + caretPos += Math.min(selectionLength, 0); + else + caretPos--; + } selectionLength = 0; } return true; } - case GLFW.GLFW_KEY_RIGHT -> { + case InputUtil.GLFW_KEY_RIGHT -> { if (Screen.hasShiftDown()) { if (Screen.hasControlDown()) { int spaceChar = findSpaceIndex(false); @@ -116,18 +128,22 @@ public class StringControllerElement extends ControllerWidget<IStringController< selectionLength -= 1; } } else { - if (caretPos < inputField.length()) - caretPos++; + if (caretPos < inputField.length()) { + if (selectionLength != 0) + caretPos += Math.max(selectionLength, 0); + else + caretPos++; + } selectionLength = 0; } return true; } - case GLFW.GLFW_KEY_BACKSPACE -> { + case InputUtil.GLFW_KEY_BACKSPACE -> { doBackspace(); return true; } - case GLFW.GLFW_KEY_DELETE -> { + case InputUtil.GLFW_KEY_DELETE -> { doDelete(); return true; } @@ -172,36 +188,46 @@ public class StringControllerElement extends ControllerWidget<IStringController< if (selectionLength != 0) { write(""); } else if (caretPos > 0) { - inputField.deleteCharAt(caretPos - 1); - caretPos--; - updateControl(); + if (modifyInput(builder -> builder.deleteCharAt(caretPos - 1))) + caretPos--; } } protected void doDelete() { if (caretPos < inputField.length()) { - inputField.deleteCharAt(caretPos); - updateControl(); + modifyInput(builder -> builder.deleteCharAt(caretPos)); } } public void write(String string) { if (selectionLength == 0) { - string = textRenderer.trimToWidth(string, getMaxLength() - textRenderer.getWidth(inputField.toString())); + String trimmed = textRenderer.trimToWidth(string, getMaxLength() - textRenderer.getWidth(inputField)); - inputField.insert(caretPos, string); - caretPos += string.length(); + if (modifyInput(builder -> builder.insert(caretPos, trimmed))) { + caretPos += trimmed.length(); + } } else { int start = getSelectionStart(); int end = getSelectionEnd(); - string = textRenderer.trimToWidth(string, getMaxLength() - textRenderer.getWidth(inputField.toString()) + textRenderer.getWidth(inputField.substring(start, end))); + String trimmed = textRenderer.trimToWidth(string, getMaxLength() - textRenderer.getWidth(inputField) + textRenderer.getWidth(inputField.substring(start, end))); - inputField.replace(start, end, string); - caretPos = start + string.length(); - selectionLength = 0; + if (modifyInput(builder -> builder.replace(start, end, trimmed))) { + caretPos = start + trimmed.length(); + selectionLength = 0; + } } - updateControl(); + } + + public boolean modifyInput(Consumer<StringBuilder> consumer) { + StringBuilder temp = new StringBuilder(inputField); + consumer.accept(temp); + if (!control.isInputValid(temp.toString())) + return false; + inputField = temp.toString(); + if (instantApply) + updateControl(); + return true; } public int getMaxLength() { @@ -249,6 +275,7 @@ public class StringControllerElement extends ControllerWidget<IStringController< public void unfocus() { super.unfocus(); inputFieldFocused = false; + if (!instantApply) updateControl(); } @Override @@ -265,7 +292,7 @@ public class StringControllerElement extends ControllerWidget<IStringController< } protected void updateControl() { - control.setFromString(inputField.toString()); + control.setFromString(inputField); } @Override @@ -278,6 +305,6 @@ public class StringControllerElement extends ControllerWidget<IStringController< if (!inputFieldFocused && inputField.isEmpty()) return emptyText; - return super.getValueText(); + return instantApply || !inputFieldFocused ? control.formatValue() : Text.of(inputField); } } diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/string/number/DoubleFieldController.java b/src/client/java/dev/isxander/yacl/gui/controllers/string/number/DoubleFieldController.java new file mode 100644 index 0000000..1dbd7c0 --- /dev/null +++ b/src/client/java/dev/isxander/yacl/gui/controllers/string/number/DoubleFieldController.java @@ -0,0 +1,105 @@ +package dev.isxander.yacl.gui.controllers.string.number; + +import dev.isxander.yacl.api.Option; +import dev.isxander.yacl.gui.controllers.slider.DoubleSliderController; +import net.minecraft.text.Text; + +import java.math.BigDecimal; +import java.util.function.Function; + +/** + * {@inheritDoc} + */ +public class DoubleFieldController extends NumberFieldController<Double> { + private final double min, max; + + /** + * Constructs a double field controller + * + * @param option option to bind controller to + * @param min minimum allowed value (clamped on apply) + * @param max maximum allowed value (clamped on apply) + * @param formatter display text, not used whilst editing + */ + public DoubleFieldController(Option<Double> option, double min, double max, Function<Double, Text> formatter) { + super(option, formatter); + this.min = min; + this.max = max; + } + + /** + * Constructs a double field controller. + * Uses {@link DoubleSliderController#DEFAULT_FORMATTER} as display text, + * not used whilst editing. + * + * @param option option to bind controller to + * @param min minimum allowed value (clamped on apply) + * @param max maximum allowed value (clamped on apply) + */ + public DoubleFieldController(Option<Double> option, double min, double max) { + this(option, min, max, DoubleSliderController.DEFAULT_FORMATTER); + } + + /** + * Constructs a double field controller. + * Does not have a minimum or a maximum range. + * + * @param option option to bind controller to + * @param formatter display text, not used whilst editing + */ + public DoubleFieldController(Option<Double> option, Function<Double, Text> formatter) { + this(option, -Double.MAX_VALUE, Double.MAX_VALUE, formatter); + } + + /** + * Constructs a double field controller. + * Uses {@link DoubleSliderController#DEFAULT_FORMATTER} as display text, + * not used whilst editing. + * Does not have a minimum or a maximum range. + * + * @param option option to bind controller to + */ + public DoubleFieldController(Option<Double> option) { + this(option, -Double.MAX_VALUE, Double.MAX_VALUE, DoubleSliderController.DEFAULT_FORMATTER); + } + + /** + * {@inheritDoc} + */ + @Override + public double min() { + return this.min; + } + + /** + * {@inheritDoc} + */ + @Override + public double max() { + return this.max; + } + + /** + * {@inheritDoc} + */ + @Override + public String getString() { + return BigDecimal.valueOf(option().pendingValue()).stripTrailingZeros().toPlainString(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setPendingValue(double value) { + option().requestSet(value); + } + + /** + * {@inheritDoc} + */ + @Override + public double pendingValue() { + return option().pendingValue(); + } +} diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/string/number/FloatFieldController.java b/src/client/java/dev/isxander/yacl/gui/controllers/string/number/FloatFieldController.java new file mode 100644 index 0000000..4b34d7f --- /dev/null +++ b/src/client/java/dev/isxander/yacl/gui/controllers/string/number/FloatFieldController.java @@ -0,0 +1,105 @@ +package dev.isxander.yacl.gui.controllers.string.number; + +import dev.isxander.yacl.api.Option; +import dev.isxander.yacl.gui.controllers.slider.FloatSliderController; +import net.minecraft.text.Text; + +import java.math.BigDecimal; +import java.util.function.Function; + +/** + * {@inheritDoc} + */ +public class FloatFieldController extends NumberFieldController<Float> { + private final float min, max; + + /** + * Constructs a double field controller + * + * @param option option to bind controller to + * @param min minimum allowed value (clamped on apply) + * @param max maximum allowed value (clamped on apply) + * @param formatter display text, not used whilst editing + */ + public FloatFieldController(Option<Float> option, float min, float max, Function<Float, Text> formatter) { + super(option, formatter); + this.min = min; + this.max = max; + } + + /** + * Constructs a double field controller. + * Uses {@link FloatSliderController#DEFAULT_FORMATTER} as display text, + * not used whilst editing. + * + * @param option option to bind controller to + * @param min minimum allowed value (clamped on apply) + * @param max maximum allowed value (clamped on apply) + */ + public FloatFieldController(Option<Float> option, float min, float max) { + this(option, min, max, FloatSliderController.DEFAULT_FORMATTER); + } + + /** + * Constructs a double field controller. + * Does not have a minimum or a maximum range. + * + * @param option option to bind controller to + * @param formatter display text, not used whilst editing + */ + public FloatFieldController(Option<Float> option, Function<Float, Text> formatter) { + this(option, -Float.MAX_VALUE, Float.MAX_VALUE, formatter); + } + + /** + * Constructs a double field controller. + * Uses {@link FloatSliderController#DEFAULT_FORMATTER} as display text, + * not used whilst editing. + * Does not have a minimum or a maximum range. + * + * @param option option to bind controller to + */ + public FloatFieldController(Option<Float> option) { + this(option, -Float.MAX_VALUE, Float.MAX_VALUE, FloatSliderController.DEFAULT_FORMATTER); + } + + /** + * {@inheritDoc} + */ + @Override + public double min() { + return this.min; + } + + /** + * {@inheritDoc} + */ + @Override + public double max() { + return this.max; + } + + /** + * {@inheritDoc} + */ + @Override + public String getString() { + return BigDecimal.valueOf(option().pendingValue()).stripTrailingZeros().toPlainString(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setPendingValue(double value) { + option().requestSet((float) value); + } + + /** + * {@inheritDoc} + */ + @Override + public double pendingValue() { + return option().pendingValue(); + } +} diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/string/number/IntegerFieldController.java b/src/client/java/dev/isxander/yacl/gui/controllers/string/number/IntegerFieldController.java new file mode 100644 index 0000000..5f0121a --- /dev/null +++ b/src/client/java/dev/isxander/yacl/gui/controllers/string/number/IntegerFieldController.java @@ -0,0 +1,104 @@ +package dev.isxander.yacl.gui.controllers.string.number; + +import dev.isxander.yacl.api.Option; +import dev.isxander.yacl.gui.controllers.slider.IntegerSliderController; +import net.minecraft.text.Text; + +import java.util.function.Function; + +/** + * {@inheritDoc} + */ +public class IntegerFieldController extends NumberFieldController<Integer> { + private final int min, max; + + /** + * Constructs a double field controller + * + * @param option option to bind controller to + * @param min minimum allowed value (clamped on apply) + * @param max maximum allowed value (clamped on apply) + * @param formatter display text, not used whilst editing + */ + public IntegerFieldController(Option<Integer> option, int min, int max, Function<Integer, Text> formatter) { + super(option, formatter); + this.min = min; + this.max = max; + } + + /** + * Constructs a double field controller. + * Uses {@link IntegerSliderController#DEFAULT_FORMATTER} as display text, + * not used whilst editing. + * + * @param option option to bind controller to + * @param min minimum allowed value (clamped on apply) + * @param max maximum allowed value (clamped on apply) + */ + public IntegerFieldController(Option<Integer> option, int min, int max) { + this(option, min, max, IntegerSliderController.DEFAULT_FORMATTER); + } + + /** + * Constructs a double field controller. + * Does not have a minimum or a maximum range. + * + * @param option option to bind controller to + * @param formatter display text, not used whilst editing + */ + public IntegerFieldController(Option<Integer> option, Function<Integer, Text> formatter) { + this(option, -Integer.MAX_VALUE, Integer.MAX_VALUE, formatter); + } + + /** + * Constructs a double field controller. + * Uses {@link IntegerSliderController#DEFAULT_FORMATTER} as display text, + * not used whilst editing. + * Does not have a minimum or a maximum range. + * + * @param option option to bind controller to + */ + public IntegerFieldController(Option<Integer> option) { + this(option, -Integer.MAX_VALUE, Integer.MAX_VALUE, IntegerSliderController.DEFAULT_FORMATTER); + } + + /** + * {@inheritDoc} + */ + @Override + public double min() { + return this.min; + } + + /** + * {@inheritDoc} + */ + @Override + public double max() { + return this.max; + } + + /** + * {@inheritDoc} + */ + @Override + public String getString() { + return String.valueOf(option().pendingValue()); + } + + /** + * {@inheritDoc} + */ + @Override + public void setPendingValue(double value) { + option().requestSet((int) value); + } + + /** + * {@inheritDoc} + */ + @Override + public double pendingValue() { + return option().pendingValue(); + } +} diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/string/number/LongFieldController.java b/src/client/java/dev/isxander/yacl/gui/controllers/string/number/LongFieldController.java new file mode 100644 index 0000000..713d39f --- /dev/null +++ b/src/client/java/dev/isxander/yacl/gui/controllers/string/number/LongFieldController.java @@ -0,0 +1,104 @@ +package dev.isxander.yacl.gui.controllers.string.number; + +import dev.isxander.yacl.api.Option; +import dev.isxander.yacl.gui.controllers.slider.LongSliderController; +import net.minecraft.text.Text; + +import java.util.function.Function; + +/** + * {@inheritDoc} + */ +public class LongFieldController extends NumberFieldController<Long> { + private final long min, max; + + /** + * Constructs a double field controller + * + * @param option option to bind controller to + * @param min minimum allowed value (clamped on apply) + * @param max maximum allowed value (clamped on apply) + * @param formatter display text, not used whilst editing + */ + public LongFieldController(Option<Long> option, long min, long max, Function<Long, Text> formatter) { + super(option, formatter); + this.min = min; + this.max = max; + } + + /** + * Constructs a double field controller. + * Uses {@link LongSliderController#DEFAULT_FORMATTER} as display text, + * not used whilst editing. + * + * @param option option to bind controller to + * @param min minimum allowed value (clamped on apply) + * @param max maximum allowed value (clamped on apply) + */ + public LongFieldController(Option<Long> option, long min, long max) { + this(option, min, max, LongSliderController.DEFAULT_FORMATTER); + } + + /** + * Constructs a double field controller. + * Does not have a minimum or a maximum range. + * + * @param option option to bind controller to + * @param formatter display text, not used whilst editing + */ + public LongFieldController(Option<Long> option, Function<Long, Text> formatter) { + this(option, -Long.MAX_VALUE, Long.MAX_VALUE, formatter); + } + + /** + * Constructs a double field controller. + * Uses {@link LongSliderController#DEFAULT_FORMATTER} as display text, + * not used whilst editing. + * Does not have a minimum or a maximum range. + * + * @param option option to bind controller to + */ + public LongFieldController(Option<Long> option) { + this(option, -Long.MAX_VALUE, Long.MAX_VALUE, LongSliderController.DEFAULT_FORMATTER); + } + + /** + * {@inheritDoc} + */ + @Override + public double min() { + return this.min; + } + + /** + * {@inheritDoc} + */ + @Override + public double max() { + return this.max; + } + + /** + * {@inheritDoc} + */ + @Override + public String getString() { + return String.valueOf(option().pendingValue()); + } + + /** + * {@inheritDoc} + */ + @Override + public void setPendingValue(double value) { + option().requestSet((long) value); + } + + /** + * {@inheritDoc} + */ + @Override + public double pendingValue() { + return option().pendingValue(); + } +} diff --git a/src/client/java/dev/isxander/yacl/gui/controllers/string/number/NumberFieldController.java b/src/client/java/dev/isxander/yacl/gui/controllers/string/number/NumberFieldController.java new file mode 100644 index 0000000..bf0354a --- /dev/null +++ b/src/client/java/dev/isxander/yacl/gui/controllers/string/number/NumberFieldController.java @@ -0,0 +1,69 @@ +package dev.isxander.yacl.gui.controllers.string.number; + +import dev.isxander.yacl.api.Option; +import dev.isxander.yacl.api.utils.Dimension; +import dev.isxander.yacl.gui.AbstractWidget; +import dev.isxander.yacl.gui.YACLScreen; +import dev.isxander.yacl.gui.controllers.slider.ISliderController; +import dev.isxander.yacl.gui.controllers.string.IStringController; +import dev.isxander.yacl.gui.controllers.string.StringControllerElement; +import net.minecraft.text.Text; +import net.minecraft.util.math.MathHelper; + +import java.text.DecimalFormatSymbols; +import java.util.function.Function; + +/** + * Controller that allows you to enter in numbers using a text field. + * + * @param <T> number type + */ +public abstract class NumberFieldController<T extends Number> implements ISliderController<T>, IStringController<T> { + private final Option<T> option; + private final Function<T, Text> displayFormatter; + + public NumberFieldController(Option<T> option, Function<T, Text> displayFormatter) { + this.option = option; + this.displayFormatter = displayFormatter; + } + + @Override + public Option<T> option() { + return this.option; + } + + @Override + public void setFromString(String value) { + if (value.isEmpty() || value.equals(".") || value.equals("-")) value = "0"; + setPendingValue(MathHelper.clamp(Double.parseDouble(cleanupNumberString(value)), min(), max())); + } + + @Override + public double pendingValue() { + return option().pendingValue().doubleValue(); + } + + @Override + public boolean isInputValid(String input) { + return input.matches("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)|[.]||-"); + } + + @Override + public Text formatValue() { + return displayFormatter.apply(option().pendingValue()); + } + + @Override + public AbstractWidget provideWidget(YACLScreen screen, Dimension<Integer> widgetDimension) { + 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/src/client/java/dev/isxander/yacl/gui/controllers/string/number/package-info.java b/src/client/java/dev/isxander/yacl/gui/controllers/string/number/package-info.java new file mode 100644 index 0000000..86b9314 --- /dev/null +++ b/src/client/java/dev/isxander/yacl/gui/controllers/string/number/package-info.java @@ -0,0 +1,10 @@ +/** + * This package contains implementations of input fields for different number types + * <ul> + * <li>For doubles: {@link dev.isxander.yacl.gui.controllers.string.number.DoubleFieldController}</li> + * <li>For floats: {@link dev.isxander.yacl.gui.controllers.string.number.FloatFieldController}</li> + * <li>For integers: {@link dev.isxander.yacl.gui.controllers.string.number.IntegerFieldController}</li> + * <li>For longs: {@link dev.isxander.yacl.gui.controllers.string.number.LongFieldController}</li> + * </ul> + */ +package dev.isxander.yacl.gui.controllers.string.number; diff --git a/src/testmod/java/dev/isxander/yacl/test/GuiTest.java b/src/testmod/java/dev/isxander/yacl/test/GuiTest.java index 7377bc9..492a573 100644 --- a/src/testmod/java/dev/isxander/yacl/test/GuiTest.java +++ b/src/testmod/java/dev/isxander/yacl/test/GuiTest.java @@ -9,6 +9,10 @@ import dev.isxander.yacl.gui.controllers.slider.FloatSliderController; import dev.isxander.yacl.gui.controllers.slider.IntegerSliderController; import dev.isxander.yacl.gui.controllers.slider.LongSliderController; import dev.isxander.yacl.gui.controllers.string.StringController; +import dev.isxander.yacl.gui.controllers.string.number.DoubleFieldController; +import dev.isxander.yacl.gui.controllers.string.number.FloatFieldController; +import dev.isxander.yacl.gui.controllers.string.number.IntegerFieldController; +import dev.isxander.yacl.gui.controllers.string.number.LongFieldController; import dev.isxander.yacl.test.config.ConfigData; import dev.isxander.yacl.test.config.Entrypoint; import net.minecraft.client.MinecraftClient; @@ -158,6 +162,45 @@ public class GuiTest { .build()) .build()) .group(OptionGroup.createBuilder() + .name(Text.of("Number Fields")) + .option(Option.createBuilder(double.class) + .name(Text.of("Double Field")) + .binding( + defaults.doubleField, + () -> config.doubleField, + value -> config.doubleField = value + ) + .controller(DoubleFieldController::new) + .build()) + .option(Option.createBuilder(float.class) + .name(Text.of("Float Field")) + .binding( + defaults.floatField, + () -> config.floatField, + value -> config.floatField = value + ) + .controller(FloatFieldController::new) + .build()) + .option(Option.createBuilder(int.class) + .name(Text.of("Integer Field")) + .binding( + defaults.intField, + () -> config.intField, + value -> config.intField = value + ) + .controller(IntegerFieldController::new) + .build()) + .option(Option.createBuilder(long.class) + .name(Text.of("Long Field")) + .binding( + defaults.longField, + () -> config.longField, + value -> config.longField = value + ) + .controller(LongFieldController::new) + .build()) + .build()) + .group(OptionGroup.createBuilder() .name(Text.of("Enum Controllers")) .option(Option.createBuilder(ConfigData.Alphabet.class) .name(Text.of("Enum Cycler")) diff --git a/src/testmod/java/dev/isxander/yacl/test/config/ConfigData.java b/src/testmod/java/dev/isxander/yacl/test/config/ConfigData.java index 35e57dd..4eedd9f 100644 --- a/src/testmod/java/dev/isxander/yacl/test/config/ConfigData.java +++ b/src/testmod/java/dev/isxander/yacl/test/config/ConfigData.java @@ -14,6 +14,10 @@ public class ConfigData { @ConfigEntry public long longSlider = 0; @ConfigEntry public String textField = "Hello"; @ConfigEntry public Color colorOption = Color.red; + @ConfigEntry public double doubleField = 0.5; + @ConfigEntry public float floatField = 0.5f; + @ConfigEntry public int intField = 5; + @ConfigEntry public long longField = 5; @ConfigEntry public Alphabet enumOption = Alphabet.A; @ConfigEntry public boolean groupTestRoot = false; |