Spinner has +/- buttons which you can press and hold, but they are too slow.
Binding Spinner value to the distance of mouse vertical “drag” movement can provide much better experience.
How to implement this feature? Are there any controls with this behavior in JavaFX?
I.e.: In MsEdge you can click mouse wheel and autoscrolling will be enabled with the speed proportional to how far your cursor from start position. It’s not “dragging”, but something similar.
Drag spinner from its textfield:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class DraggableSpinnerApp extends Application {
@Override
public void start(Stage primaryStage) {
Spinner<Integer> spinner = new Spinner<>();
SpinnerValueFactory<Integer> valueFactory = new SpinnerValueFactory.IntegerSpinnerValueFactory(Integer.MIN_VALUE, Integer.MAX_VALUE, 0);
spinner.setValueFactory(valueFactory);
// There will be a little conflict between text selection and dragging
spinner.setEditable(true);
final double[] mouseAnchorY = {0d};
final double[] spinnerValOnStartDrag = {0d};
spinner.getEditor().setOnMousePressed(event -> {
// Capture the starting Y position and spinner value
mouseAnchorY[0] = event.getSceneY();
spinnerValOnStartDrag[0] = spinner.getValue();
});
// Mouse dragged event to calculate new value
spinner.getEditor().setOnMouseDragged(event -> {
double deltaY = mouseAnchorY[0] - event.getSceneY(); // Calculate the delta
// Observe values in console
System.out.printf("%s %s %s delta=%s%n",mouseAnchorY[0],event.getSceneY(),event.getEventType(),deltaY);
// For bigger initial values we want proportionally big delta factor
var valAbs = Math.abs(spinnerValOnStartDrag[0]);
var factor = String.valueOf(valAbs).length();
int newValue = (int) (spinnerValOnStartDrag[0]+deltaY*factor);
spinner.getValueFactory().setValue(newValue);
});
VBox vbox = new VBox(spinner);
Scene scene = new Scene(vbox, 400, 200);
primaryStage.setScene(scene);
primaryStage.setTitle("Draggable Spinner Example");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Maybe it would be better to bind this listener to buttons instead.