Add reusable bounded integer inputs for JavaFX editors

This commit is contained in:
2026-11-07 16:10:54 +01:00
parent 188a6f38a5
commit bb90b2c060
2 changed files with 65 additions and 0 deletions
@@ -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;
}
}