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..53c91d82b 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/textfield/TextFieldWidget.java @@ -245,6 +245,16 @@ public TextFieldWidget numberParser(INumberParser parser) { } public TextFieldWidget numbersDouble(DAM.UnaryDoubleOperator validator) { + return numbersDouble((input, num) -> validator.apply(num)); + } + + @FunctionalInterface + public interface NumberValidator { + + double apply(String input, double value); + } + + public TextFieldWidget numbersDouble(NumberValidator validator) { this.numbers = true; return setValidator(val -> { double num; @@ -253,7 +263,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 +299,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)); 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)); + } +}