Add reusable bounded integer inputs for JavaFX editors
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
# Bounded integer inputs
|
||||||
|
|
||||||
|
`de.neitzel.fx.control.IntegerSpinners.nonNegative(min, max, value)` creates an
|
||||||
|
editable JavaFX spinner with inclusive nonnegative bounds. Construct and use it
|
||||||
|
on the JavaFX application thread.
|
||||||
|
|
||||||
|
Every valid typed number immediately updates the value property, so a form can
|
||||||
|
save its model without waiting for Enter or a focus change. Non-digit input and
|
||||||
|
values above the maximum are rejected. Empty input and intermediate numbers
|
||||||
|
below the minimum retain the last committed value and revert when focus leaves
|
||||||
|
the editor. Increment/decrement use the normal JavaFX spinner behavior.
|
||||||
|
|
||||||
|
The factory rejects negative/inverted bounds and out-of-range initial values.
|
||||||
|
Applications own their field labels, units, and domain-specific bounds.
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package de.neitzel.fx.control;
|
||||||
|
|
||||||
|
import javafx.scene.control.Spinner;
|
||||||
|
import javafx.scene.control.TextFormatter;
|
||||||
|
|
||||||
|
/** Factories for bounded integer inputs that commit valid typing immediately. */
|
||||||
|
public final class IntegerSpinners {
|
||||||
|
private IntegerSpinners() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a nonnegative bounded input. Valid typing immediately updates its value;
|
||||||
|
* incomplete values below the minimum revert to the committed value on focus loss.
|
||||||
|
*
|
||||||
|
* @param min inclusive nonnegative minimum
|
||||||
|
* @param max inclusive maximum
|
||||||
|
* @param value initial value
|
||||||
|
* @return an editable spinner
|
||||||
|
*/
|
||||||
|
public static Spinner<Integer> nonNegative(int min, int max, int value) {
|
||||||
|
if (min < 0 || max < min || value < min || value > max) {
|
||||||
|
throw new IllegalArgumentException("Invalid nonnegative spinner bounds or value");
|
||||||
|
}
|
||||||
|
Spinner<Integer> spinner = new Spinner<>(min, max, value);
|
||||||
|
spinner.setEditable(true);
|
||||||
|
spinner.getEditor().setTextFormatter(new TextFormatter<String>(change -> {
|
||||||
|
String text = change.getControlNewText();
|
||||||
|
if (!text.matches("\\d{0,10}")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!text.isEmpty() && Long.parseLong(text) > max) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return change;
|
||||||
|
}));
|
||||||
|
spinner.getEditor().textProperty().addListener((observable, old, text) -> {
|
||||||
|
if (!text.isEmpty()) {
|
||||||
|
int parsed = Integer.parseInt(text);
|
||||||
|
if (parsed >= min) {
|
||||||
|
spinner.getValueFactory().setValue(parsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
spinner.getEditor().focusedProperty().addListener((observable, old, focused) -> {
|
||||||
|
if (!focused) {
|
||||||
|
spinner.getEditor().setText(spinner.getValue().toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return spinner;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user