From db8ca2fd18c3fe1e67c882f3c78f28ad84f503c4 Mon Sep 17 00:00:00 2001 From: Worive <13164341+Worive@users.noreply.github.com> Date: Sun, 26 Jul 2026 11:26:14 +0200 Subject: [PATCH 1/3] Fix explicit percentages in bounded number fields --- .../cleanroommc/modularui/utils/MathUtils.java | 10 ++++++++++ .../widgets/textfield/TextFieldWidget.java | 16 +++++++++++----- .../modularui/utils/MathUtilsTest.java | 17 +++++++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 src/test/java/com/cleanroommc/modularui/utils/MathUtilsTest.java diff --git a/src/main/java/com/cleanroommc/modularui/utils/MathUtils.java b/src/main/java/com/cleanroommc/modularui/utils/MathUtils.java index 152dbc785..88b7c6e5d 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/MathUtils.java +++ b/src/main/java/com/cleanroommc/modularui/utils/MathUtils.java @@ -257,6 +257,16 @@ public static long percentOrSelf(double value, long maxValue) { return Math.round(value * maxValue); } + /** + * Resolves a parsed bounded value while preserving an explicit percentage in the source expression. + */ + public static long percentOrSelf(String expression, double value, long maxValue) { + if (expression != null && expression.indexOf('%') >= 0) { + return Math.round(value * maxValue); + } + return percentOrSelf(value, maxValue); + } + public static int castToIntSaturated(long l) { if (l >= Integer.MAX_VALUE) return Integer.MAX_VALUE; if (l <= Integer.MIN_VALUE) return Integer.MIN_VALUE; diff --git a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java index d86c528a5..c44086517 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java @@ -22,6 +22,7 @@ import java.text.ParseException; import java.text.ParsePosition; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.DoubleSupplier; import java.util.function.Function; @@ -245,6 +246,10 @@ public TextFieldWidget numberParser(INumberParser parser) { } public TextFieldWidget numbersDouble(DAM.UnaryDoubleOperator validator) { + return numbersDouble((input, num) -> validator.apply(num)); + } + + private TextFieldWidget numbersDouble(BiFunction validator) { this.numbers = true; return setValidator(val -> { double num; @@ -253,7 +258,7 @@ public TextFieldWidget numbersDouble(DAM.UnaryDoubleOperator validator) { } else { num = parse(val); } - return format.format(validator.apply(num)); + return format.format(validator.apply(val, num)); }); } @@ -289,17 +294,18 @@ public TextFieldWidget numbersLong(MathUtils.UnaryLongOperator validator) { * * @param validator allow further validation of the number * @param min optional lower limit - * @param max optional upper limit, if this is specified, then values that evaluate to a noninteger are multiplied by the max + * @param max optional upper limit, if this is specified, then explicit percentages and fractional values are + * multiplied by the max */ public TextFieldWidget numbersLong(MathUtils.UnaryLongOperator validator, @Nullable LongSupplier min, @Nullable LongSupplier max) { formatAsInteger(true); defaultWholeNumberScrollValues(); numberParser(MathUtils.PARSER_WHOLE_NUMBER); - return numbersDouble(d -> { + return numbersDouble((val, d) -> { long l; if (max != null) { long maxValue = max.getAsLong(); - l = MathUtils.percentOrSelf(d, maxValue); + l = MathUtils.percentOrSelf(val, d, maxValue); l = Math.min(validator.apply(l), maxValue); } else { l = validator.apply(Math.round(d)); @@ -307,7 +313,7 @@ public TextFieldWidget numbersLong(MathUtils.UnaryLongOperator validator, @Nulla if (min != null) { l = Math.max(l, min.getAsLong()); } - return l; + return (double) l; }); } diff --git a/src/test/java/com/cleanroommc/modularui/utils/MathUtilsTest.java b/src/test/java/com/cleanroommc/modularui/utils/MathUtilsTest.java new file mode 100644 index 000000000..3206f4c6a --- /dev/null +++ b/src/test/java/com/cleanroommc/modularui/utils/MathUtilsTest.java @@ -0,0 +1,17 @@ +package com.cleanroommc.modularui.utils; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MathUtilsTest { + + @Test + void testExplicitPercentageRelativeToMaximum() { + assertEquals(1, MathUtils.percentOrSelf("1", 1, 256)); + assertEquals(128, MathUtils.percentOrSelf("0.5", 0.5, 256)); + assertEquals(128, MathUtils.percentOrSelf("50%", 0.5, 256)); + assertEquals(256, MathUtils.percentOrSelf("100%", 1, 256)); + assertEquals(512, MathUtils.percentOrSelf("200%", 2, 256)); + } +} From 4235798c6d087e345c89ae1b45065a63d5f085b7 Mon Sep 17 00:00:00 2001 From: Worive <13164341+Worive@users.noreply.github.com> Date: Sun, 26 Jul 2026 11:43:47 +0200 Subject: [PATCH 2/3] Avoid boxing number field validation --- .../modularui/widgets/textfield/TextFieldWidget.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java index c44086517..ff15909d8 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java @@ -22,7 +22,6 @@ import java.text.ParseException; import java.text.ParsePosition; -import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.DoubleSupplier; import java.util.function.Function; @@ -249,7 +248,13 @@ public TextFieldWidget numbersDouble(DAM.UnaryDoubleOperator validator) { return numbersDouble((input, num) -> validator.apply(num)); } - private TextFieldWidget numbersDouble(BiFunction validator) { + @FunctionalInterface + private interface NumberValidator { + + double apply(String input, double value); + } + + private TextFieldWidget numbersDouble(NumberValidator validator) { this.numbers = true; return setValidator(val -> { double num; @@ -313,7 +318,7 @@ public TextFieldWidget numbersLong(MathUtils.UnaryLongOperator validator, @Nulla if (min != null) { l = Math.max(l, min.getAsLong()); } - return (double) l; + return l; }); } From 30a5c43343d79dcd12e0929bce7688537000a4cc Mon Sep 17 00:00:00 2001 From: Worive <13164341+Worive@users.noreply.github.com> Date: Sun, 26 Jul 2026 12:14:25 +0200 Subject: [PATCH 3/3] Mark as public --- .../modularui/widgets/textfield/TextFieldWidget.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java index ff15909d8..53c91d82b 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java @@ -249,12 +249,12 @@ public TextFieldWidget numbersDouble(DAM.UnaryDoubleOperator validator) { } @FunctionalInterface - private interface NumberValidator { + public interface NumberValidator { double apply(String input, double value); } - private TextFieldWidget numbersDouble(NumberValidator validator) { + public TextFieldWidget numbersDouble(NumberValidator validator) { this.numbers = true; return setValidator(val -> { double num;