From d163b9128d760e53e34fd6c08dbf782fa3d50c51 Mon Sep 17 00:00:00 2001 From: isXander Date: Sun, 27 Nov 2022 18:17:36 +0000 Subject: split sourcesets --- .../gui/controllers/string/IStringController.java | 32 +++ .../gui/controllers/string/StringController.java | 45 ++++ .../string/StringControllerElement.java | 283 +++++++++++++++++++++ 3 files changed, 360 insertions(+) create mode 100644 src/client/java/dev/isxander/yacl/gui/controllers/string/IStringController.java create mode 100644 src/client/java/dev/isxander/yacl/gui/controllers/string/StringController.java create mode 100644 src/client/java/dev/isxander/yacl/gui/controllers/string/StringControllerElement.java (limited to 'src/client/java/dev/isxander/yacl/gui/controllers/string') 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 new file mode 100644 index 0000000..41843b8 --- /dev/null +++ b/src/client/java/dev/isxander/yacl/gui/controllers/string/IStringController.java @@ -0,0 +1,32 @@ +package dev.isxander.yacl.gui.controllers.string; + +import dev.isxander.yacl.api.Controller; +import dev.isxander.yacl.api.Option; +import net.minecraft.text.Text; + +/** + * A controller that can be any type but can input and output a string. + */ +public interface IStringController extends Controller { + /** + * Gets the option's pending value as a string. + * + * @see Option#pendingValue() + */ + String getString(); + + /** + * Sets the option's pending value from a string. + * + * @see Option#requestSet(Object) + */ + void setFromString(String value); + + /** + * {@inheritDoc} + */ + @Override + default Text formatValue() { + return Text.of(getString()); + } +} 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 new file mode 100644 index 0000000..0caaa93 --- /dev/null +++ b/src/client/java/dev/isxander/yacl/gui/controllers/string/StringController.java @@ -0,0 +1,45 @@ +package dev.isxander.yacl.gui.controllers.string; + +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; + +/** + * A custom text field implementation for strings. + */ +public class StringController implements IStringController { + private final Option option; + + /** + * Constructs a string controller + * + * @param option bound option + */ + public StringController(Option option) { + this.option = option; + } + + /** + * {@inheritDoc} + */ + @Override + public Option option() { + return option; + } + + @Override + public String getString() { + return option().pendingValue(); + } + + @Override + public void setFromString(String value) { + option().requestSet(value); + } + + @Override + public AbstractWidget provideWidget(YACLScreen screen, Dimension 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 new file mode 100644 index 0000000..0c3b7c9 --- /dev/null +++ b/src/client/java/dev/isxander/yacl/gui/controllers/string/StringControllerElement.java @@ -0,0 +1,283 @@ +package dev.isxander.yacl.gui.controllers.string; + +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.DrawableHelper; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.lwjgl.glfw.GLFW; + +public class StringControllerElement extends ControllerWidget> { + protected StringBuilder inputField; + protected Dimension inputFieldBounds; + protected boolean inputFieldFocused; + + protected int caretPos; + protected int selectionLength; + + protected float ticks; + + private final Text emptyText; + + public StringControllerElement(IStringController control, YACLScreen screen, Dimension dim) { + super(control, screen, dim); + inputField = new StringBuilder(control.getString()); + inputFieldFocused = false; + selectionLength = 0; + emptyText = Text.literal("Click to type...").formatted(Formatting.GRAY); + setDimension(dim); + } + + @Override + protected void drawHoveredControl(MatrixStack matrices, int mouseX, int mouseY, float delta) { + ticks += delta; + + 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()) + caretX += inputFieldBounds.width() / 2; + + if (ticks % 20 <= 10) { + DrawableHelper.fill(matrices, caretX, inputFieldBounds.y(), caretX + 1, inputFieldBounds.yLimit(), -1); + } + + if (selectionLength != 0) { + int selectionX = inputFieldBounds.x() + textRenderer.getWidth(control.getString().substring(0, caretPos + selectionLength)); + DrawableHelper.fill(matrices, caretX, inputFieldBounds.y() - 1, selectionX, inputFieldBounds.yLimit(), 0x803030FF); + } + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (isAvailable() && inputFieldBounds.isPointInside((int) mouseX, (int) mouseY)) { + if (!inputFieldFocused) { + inputFieldFocused = true; + caretPos = getDefaultCarotPos(); + } else { + int textWidth = (int) mouseX - inputFieldBounds.x(); + caretPos = textRenderer.trimToWidth(control.getString(), textWidth).length(); + selectionLength = 0; + } + return true; + } else { + inputFieldFocused = false; + } + + return false; + } + + protected int getDefaultCarotPos() { + return inputField.length(); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (!inputFieldFocused) + return false; + + switch (keyCode) { + case GLFW.GLFW_KEY_ESCAPE -> { + inputFieldFocused = false; + return true; + } + case GLFW.GLFW_KEY_LEFT -> { + if (Screen.hasShiftDown()) { + if (Screen.hasControlDown()) { + int spaceChar = findSpaceIndex(true); + selectionLength += caretPos - spaceChar; + caretPos = spaceChar; + } else if (caretPos > 0) { + caretPos--; + selectionLength += 1; + } + } else { + if (caretPos > 0) + caretPos--; + selectionLength = 0; + } + + return true; + } + case GLFW.GLFW_KEY_RIGHT -> { + if (Screen.hasShiftDown()) { + if (Screen.hasControlDown()) { + int spaceChar = findSpaceIndex(false); + selectionLength -= spaceChar - caretPos; + caretPos = spaceChar; + } else if (caretPos < inputField.length()) { + caretPos++; + selectionLength -= 1; + } + } else { + if (caretPos < inputField.length()) + caretPos++; + selectionLength = 0; + } + + return true; + } + case GLFW.GLFW_KEY_BACKSPACE -> { + doBackspace(); + return true; + } + case GLFW.GLFW_KEY_DELETE -> { + doDelete(); + return true; + } + } + + if (canUseShortcuts()) { + if (Screen.isPaste(keyCode)) { + this.write(client.keyboard.getClipboard()); + return true; + } else if (Screen.isCopy(keyCode) && selectionLength != 0) { + client.keyboard.setClipboard(getSelection()); + return true; + } else if (Screen.isCut(keyCode) && selectionLength != 0) { + client.keyboard.setClipboard(getSelection()); + this.write(""); + return true; + } else if (Screen.isSelectAll(keyCode)) { + caretPos = inputField.length(); + selectionLength = -caretPos; + return true; + } + } + + return false; + } + + @Override + public boolean charTyped(char chr, int modifiers) { + if (!inputFieldFocused) + return false; + + write(Character.toString(chr)); + + return true; + } + + protected boolean canUseShortcuts() { + return true; + } + + protected void doBackspace() { + if (selectionLength != 0) { + write(""); + } else if (caretPos > 0) { + inputField.deleteCharAt(caretPos - 1); + caretPos--; + updateControl(); + } + } + + protected void doDelete() { + if (caretPos < inputField.length()) { + inputField.deleteCharAt(caretPos); + updateControl(); + } + } + + public void write(String string) { + if (selectionLength == 0) { + string = textRenderer.trimToWidth(string, getMaxLength() - textRenderer.getWidth(inputField.toString())); + + inputField.insert(caretPos, string); + caretPos += string.length(); + } else { + int start = getSelectionStart(); + int end = getSelectionEnd(); + + string = textRenderer.trimToWidth(string, getMaxLength() - textRenderer.getWidth(inputField.toString()) + textRenderer.getWidth(inputField.substring(start, end))); + + inputField.replace(start, end, string); + caretPos = start + string.length(); + selectionLength = 0; + } + updateControl(); + } + + public int getMaxLength() { + return getDimension().width() / 8 * 7; + } + + public int getSelectionStart() { + return Math.min(caretPos, caretPos + selectionLength); + } + + public int getSelectionEnd() { + return Math.max(caretPos, caretPos + selectionLength); + } + + protected String getSelection() { + return inputField.substring(getSelectionStart(), getSelectionEnd()); + } + + protected int findSpaceIndex(boolean reverse) { + int i; + int fromIndex = caretPos; + if (reverse) { + if (caretPos > 0) + fromIndex -= 1; + i = this.inputField.lastIndexOf(" ", fromIndex); + + if (i == -1) i = 0; + } else { + if (caretPos < inputField.length()) + fromIndex += 1; + i = this.inputField.indexOf(" ", fromIndex); + + if (i == -1) i = inputField.length(); + } + + return i; + } + + @Override + public boolean changeFocus(boolean lookForwards) { + return inputFieldFocused = super.changeFocus(lookForwards); + } + + @Override + public void unfocus() { + super.unfocus(); + inputFieldFocused = false; + } + + @Override + public void setDimension(Dimension dim) { + super.setDimension(dim); + + int width = Math.max(6, textRenderer.getWidth(getValueText())); + inputFieldBounds = Dimension.ofInt(dim.xLimit() - getXPadding() - width, dim.centerY() - textRenderer.fontHeight / 2, width, textRenderer.fontHeight); + } + + @Override + public boolean isHovered() { + return super.isHovered() || inputFieldFocused; + } + + protected void updateControl() { + control.setFromString(inputField.toString()); + } + + @Override + protected int getHoveredControlWidth() { + return getUnhoveredControlWidth(); + } + + @Override + protected Text getValueText() { + if (!inputFieldFocused && inputField.isEmpty()) + return emptyText; + + return super.getValueText(); + } +} -- cgit From daf3168671bc9c0cd64693d7129cddfdb1a08bb2 Mon Sep 17 00:00:00 2001 From: isXander Date: Sat, 3 Dec 2022 18:57:03 +0000 Subject: number fields + StringControllerElement improvements when highlighted text, arrow keys go to each side of the selection switch to InputUtil instead of GLFW bump yarn --- .../gui/controllers/string/IStringController.java | 12 +++ .../gui/controllers/string/StringController.java | 5 - .../string/StringControllerElement.java | 91 +++++++++++------- .../string/number/DoubleFieldController.java | 105 +++++++++++++++++++++ .../string/number/FloatFieldController.java | 105 +++++++++++++++++++++ .../string/number/IntegerFieldController.java | 104 ++++++++++++++++++++ .../string/number/LongFieldController.java | 104 ++++++++++++++++++++ .../string/number/NumberFieldController.java | 69 ++++++++++++++ .../controllers/string/number/package-info.java | 10 ++ 9 files changed, 568 insertions(+), 37 deletions(-) create mode 100644 src/client/java/dev/isxander/yacl/gui/controllers/string/number/DoubleFieldController.java create mode 100644 src/client/java/dev/isxander/yacl/gui/controllers/string/number/FloatFieldController.java create mode 100644 src/client/java/dev/isxander/yacl/gui/controllers/string/number/IntegerFieldController.java create mode 100644 src/client/java/dev/isxander/yacl/gui/controllers/string/number/LongFieldController.java create mode 100644 src/client/java/dev/isxander/yacl/gui/controllers/string/number/NumberFieldController.java create mode 100644 src/client/java/dev/isxander/yacl/gui/controllers/string/number/package-info.java (limited to 'src/client/java/dev/isxander/yacl/gui/controllers/string') 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 extends Controller { default Text formatValue() { return Text.of(getString()); } + + default boolean isInputValid(String input) { + return true; + } + + @Override + default AbstractWidget provideWidget(YACLScreen screen, Dimension 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 { public void setFromString(String value) { option().requestSet(value); } - - @Override - public AbstractWidget provideWidget(YACLScreen screen, Dimension 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> { - protected StringBuilder inputField; + protected final boolean instantApply; + + protected String inputField; protected Dimension inputFieldBounds; protected boolean inputFieldFocused; @@ -22,12 +26,14 @@ public class StringControllerElement extends ControllerWidget control, YACLScreen screen, Dimension dim) { + public StringControllerElement(IStringController control, YACLScreen screen, Dimension 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 { - 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 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 { + 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 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 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 { + 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 option, double min, double max, Function 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 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 option, Function 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 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 { + 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 option, float min, float max, Function 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 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 option, Function 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 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 { + 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 option, int min, int max, Function 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 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 option, Function 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 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 { + 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 option, long min, long max, Function 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 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 option, Function 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 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 number type + */ +public abstract class NumberFieldController implements ISliderController, IStringController { + private final Option option; + private final Function displayFormatter; + + public NumberFieldController(Option option, Function displayFormatter) { + this.option = option; + this.displayFormatter = displayFormatter; + } + + @Override + public Option 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 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 + *
    + *
  • For doubles: {@link dev.isxander.yacl.gui.controllers.string.number.DoubleFieldController}
  • + *
  • For floats: {@link dev.isxander.yacl.gui.controllers.string.number.FloatFieldController}
  • + *
  • For integers: {@link dev.isxander.yacl.gui.controllers.string.number.IntegerFieldController}
  • + *
  • For longs: {@link dev.isxander.yacl.gui.controllers.string.number.LongFieldController}
  • + *
+ */ +package dev.isxander.yacl.gui.controllers.string.number; -- cgit From 3f93f5cb0b30268ad76b5fe59c00c6af7286686a Mon Sep 17 00:00:00 2001 From: isXander Date: Tue, 6 Dec 2022 22:08:07 +0000 Subject: minor string controller element improvements Better carot positioning when clicking in text fields Fix left arrow not working in color controller rc3 bump LQF --- .../string/StringControllerElement.java | 28 +++++++++++++++++----- .../string/number/DoubleFieldController.java | 2 +- .../string/number/FloatFieldController.java | 2 +- .../string/number/IntegerFieldController.java | 5 ++++ .../string/number/LongFieldController.java | 5 ++++ 5 files changed, 34 insertions(+), 8 deletions(-) (limited to 'src/client/java/dev/isxander/yacl/gui/controllers/string') 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 bce6906..47a4c96 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 @@ -41,7 +41,7 @@ public class StringControllerElement extends ControllerWidget textX) { // if more than half way past the characters select in front of that char + caretPos = pos; + break; + } else if (pos == inputField.length() - 1) { + // if we have reached the end and no matches, it must be the second half of the char so the last position + caretPos = pos + 1; + } + currentWidth += charLength; + } + selectionLength = 0; } return true; 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 index 1dbd7c0..8933df3 100644 --- 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 @@ -84,7 +84,7 @@ public class DoubleFieldController extends NumberFieldController { */ @Override public String getString() { - return BigDecimal.valueOf(option().pendingValue()).stripTrailingZeros().toPlainString(); + return String.valueOf(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 index 4b34d7f..b1eb3a2 100644 --- 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 @@ -84,7 +84,7 @@ public class FloatFieldController extends NumberFieldController { */ @Override public String getString() { - return BigDecimal.valueOf(option().pendingValue()).stripTrailingZeros().toPlainString(); + return String.valueOf(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 index 5f0121a..50eecec 100644 --- 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 @@ -62,6 +62,11 @@ public class IntegerFieldController extends NumberFieldController { this(option, -Integer.MAX_VALUE, Integer.MAX_VALUE, IntegerSliderController.DEFAULT_FORMATTER); } + @Override + public boolean isInputValid(String input) { + return input.matches("\\d+|-|"); + } + /** * {@inheritDoc} */ 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 index 713d39f..516de74 100644 --- 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 @@ -62,6 +62,11 @@ public class LongFieldController extends NumberFieldController { this(option, -Long.MAX_VALUE, Long.MAX_VALUE, LongSliderController.DEFAULT_FORMATTER); } + @Override + public boolean isInputValid(String input) { + return input.matches("\\d+|-|"); + } + /** * {@inheritDoc} */ -- cgit