Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spinner control value

I'm using Spinner from 8u40b17.

SpinnerValueFactory svf = new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 100);

Spinner sp = new Spinner();
sp.setValueFactory(svf);
sp.setEditable(true);
sp.setPrefWidth(80);

I noticed that when I enter some value from keyboard and I increase the upper value the expected number is not the next. Instead of this it's the next default value. How I can fix this?

For example: if I have 5 as default value and I enter 34, then press the upper arrow I expect to get 35 by actually get 6.

like image 917
Peter Penzov Avatar asked Dec 11 '14 22:12

Peter Penzov


2 Answers

I had the same problem with the spinner control. Your bug has been documented here: JDK-8094205

Here is the last comment:

Jonathan Giles added a comment - Dec, 15 2014 12:59 AM

Fixed locally in my repo, will push to the 8u60 repo this week once it opens. Now the text editor input is committed when increment / decrement are called (although the value is still not committed when focus is lost).

Unit tests:

javafx.scene.control.SpinnerTest.test_rt_39655_decrement()
javafx.scene.control.SpinnerTest.test_rt_39655_increment()

The changeset: http://hg.openjdk.java.net/openjfx/8u-dev/rt/rev/89ca7d3f699e

Here is my take on an Autocommit spinner. This one will auto commit anything that the factory will accept.

public class SpinnerAutoCommit<T> extends Spinner<T> {

    public SpinnerAutoCommit() {
        super();
        addListenerKeyChange();
    }

    public SpinnerAutoCommit(int min, int max, int initialValue) {
        super(min, max, initialValue);
        addListenerKeyChange();
    }

    public SpinnerAutoCommit(int min, int max, int initialValue, int amountToStepBy) {
        super(min, max, initialValue, amountToStepBy);
        addListenerKeyChange();
    }

    public SpinnerAutoCommit(double min, double max, double initialValue) {
        super(min, max, initialValue);
        addListenerKeyChange();
    }

    public SpinnerAutoCommit(double min, double max, double initialValue, double amountToStepBy) {
        super(min, max, initialValue, amountToStepBy);
        addListenerKeyChange();
    }

    public SpinnerAutoCommit(ObservableList<T> items) {
        super(items);
        addListenerKeyChange();
    }

    public SpinnerAutoCommit(SpinnerValueFactory<T> valueFactory) {
        super(valueFactory);
        addListenerKeyChange();
    }

    private void addListenerKeyChange() {
        getEditor().textProperty().addListener((observable, oldValue, newValue) -> {
            commitEditorText();
        });
    }

    private void commitEditorText() {
        if (!isEditable()) return;
        String text = getEditor().getText();
        SpinnerValueFactory<T> valueFactory = getValueFactory();
        if (valueFactory != null) {
            StringConverter<T> converter = valueFactory.getConverter();
            if (converter != null) {
                T value = converter.fromString(text);
                valueFactory.setValue(value);
            }
        }
    }
}
like image 134
Jonathan Fortin Avatar answered Dec 31 '22 20:12

Jonathan Fortin


By design, the changes in the textfield of the Spinner control are commited only when the user hits ENTER key, via action handler:

    getEditor().setOnAction(action -> {
        String text = getEditor().getText();
        SpinnerValueFactory<T> valueFactory = getValueFactory();
        if (valueFactory != null) {
            StringConverter<T> converter = valueFactory.getConverter();
            if (converter != null) {
                T value = converter.fromString(text);
                valueFactory.setValue(value);
            }
        }
    });

Note that if the typed value can't be converted, this will throw a NumberFormatException, keeping the wrong value in the textfield.

We can provide our own implementation, listening to other keys, like TAB key, via event filter, and at the same time, and in case of exception, restore the last valid value.

Something like this:

private final Spinner sp = new Spinner();

@Override
public void start(Stage primaryStage) {
    SpinnerValueFactory svf = new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 100);
    sp.setValueFactory(svf);
    sp.setEditable(true);
    sp.setPrefWidth(80);

    // Commit on TAB
    sp.addEventFilter(KeyEvent.ANY, e->{
        if (sp.isEditable() && e.getCode().equals(KeyCode.TAB)) {
            doCommit();
            e.consume();
        }
    });

    // Override Commit on ENTER
    sp.getEditor().setOnAction(e->{
        if(sp.isEditable()) {
            doCommit();
            e.consume();
        }
    });

    Scene scene = new Scene(new StackPane(sp), 300, 250);

    primaryStage.setScene(scene);
    primaryStage.show();        
}

/*
    Commit new value, checking conversion to integer, 
    restoring old valid value in case of exception
*/
private void doCommit(){
    String text = sp.getEditor().getText();
    SpinnerValueFactory<Integer> valueFactory = sp.getValueFactory();
    if (valueFactory != null) {
        StringConverter<Integer> converter = valueFactory.getConverter();
        if (converter != null) {
            try{
                Integer value = converter.fromString(text);
                valueFactory.setValue(value);
            } catch(NumberFormatException nfe){
                sp.getEditor().setText(converter.toString(valueFactory.getValue()));
            }
        }
    }
}
like image 42
José Pereda Avatar answered Dec 31 '22 20:12

José Pereda