2021-08-31

Handling Infinity in JavaFX numerical TextField

I have a JavaFX TextField specialized to accept numbers, including scientific notation. It does pretty much everything I want. But, because it accepts scientific notation, it is easy for a user to enter a number beyond the range that can be represented by a double. When they do, the TextField displays "Infinity" (or "-Infinity"). When that happens the field can no longer be edited to correct the problem. The contents cannot be selected and deleted either. Tapping the "Escape" key does not return to the previous contents.

Here is an SSCCE, based closely on the answer by James_D to this question a few years ago.

import java.text.DecimalFormatSymbols;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.StringConverter;

public class NumericTextFieldDemo extends Application {

    char sep = new DecimalFormatSymbols().getDecimalSeparator();
    String negStarter = new StringBuilder("-").append(sep).toString();
    String posStarter = new StringBuilder("+").append(sep).toString();
    String patternStr = new StringBuilder()
            .append("[+|-]?(([1-9][0-9]*)|0)?(\\")
            .append(sep)
            .append("[0-9]*)?(([e|E][+|-]?[0-9]*)?)")
            .toString();
    Pattern validEditingState = Pattern.compile(patternStr);

    class NumericTextField extends TextField {

        UnaryOperator<TextFormatter.Change> filter = c -> {
            String text = c.getControlNewText();
            if (validEditingState.matcher(text).matches()) {
                return c;
            } else {
                return null;
            }
        };

        StringConverter<Double> converter = new StringConverter<Double>() {

            @Override
            public Double fromString(String s) {
                if (s.isEmpty() || "-".equals(s) || "+".equals(s)
                        || negStarter.equals(s) || posStarter.equals(s)) {
                    return 0.0;
                } else {
                    return Double.valueOf(s);
                }
            }

            @Override
            public String toString(Double d) {
                return d.toString();
            }
        };

        NumericTextField(double initValue) {
            TextFormatter<Double> textFormatter = new TextFormatter<>(converter, initValue, filter);
            textFormatter.valueProperty().addListener((ObservableValue<? extends Double> obs, Double oldValue, Double newValue) -> {
                System.out.println("User entered value: " + newValue);
            });
            setTextFormatter(textFormatter);
        }

        NumericTextField() {
            this(0.0);
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        NumericTextField ntf = new NumericTextField();
        // Setting the font seems to be required on macOS.
        ntf.setFont(new Font("Arial", 14));

        VBox root = new VBox(5, ntf);
        root.setAlignment(Pos.CENTER);
        primaryStage.setScene(new Scene(root, 250, 150));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Is there any way to catch the infinities and leave the TextField in a usable state? Is there some change that could be made to the class to prevent entering such numbers in the first place?



from Recent Questions - Stack Overflow https://ift.tt/2WE5wQ7
https://ift.tt/eA8V8J

No comments:

Post a Comment