How to Individually Reference Each Thumb of a RangeSlider (ControlsFX)

1k views Asked by At

I'm trying to bind a label to be displayed above the lower and upper thumbs of RangeSlider.

The label's position should always remain above their respective thumb, no matter where the user slides it. Like so:

enter image description here

enter image description here

My approach is to attach listeners to each thumb so that I can set the label's to have appropriate X/Y coordinates every time a user slides. But when I run the following code I can't seem to get a reference to the individuals thumbs, via css selectors.

I followed this post, but this only uses one thumb and is therefore easy to reference. How do you properly use CSS selectors in my context, or what is a better approach if mine is flawed?

Controller

public class SliderDemoController implements Initializable {
    @FXML
    private RangeSlider range;
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Pane thumb = (Pane) range.lookup(".range-slider .low-thumb");
        System.out.println(thumb); // <-- Prints null
    }
}

Main

public class SliderDemoMain extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage initStage) throws IOException {
        FXMLLoader loader = new FXMLLoader(Drag.class.getClassLoader().getResource("sliderdemo.fxml"));
        Parent root = loader.load();
        Scene scene = new Scene(root);
        Stage primaryStage = new Stage();
        primaryStage.setTitle("Slider Demo");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

styleMain.css

.range-slider .range-bar {
    -fx-background-color: grey;
}

.range-slider .low-thumb {
     //....
}
.range-slider .high-thumb {
    //....
}

Top Line of sliderdemo.fxml

<HBox fx:id="menuBar" maxHeight="-Infinity" minHeight="-Infinity" prefHeight="79.0" stylesheets="@styleMain.css" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.propertydrop.SliderDemoController">
1

There are 1 answers

3
James_D On BEST ANSWER

For CSS lookups to work, you need CSS to have been applied, a layout pass to have been made, and the node to be part of a scene. (Note this in the post you referenced, the code in the answer carefully performs these steps before performing the lookup.)

This is difficult to achieve in a controller, because the initialize() method is typically invoked before the root node from the FXML is added to a scene. You can hack this by observing the sceneProperty() of the node, and responding when it is set to something non-null. (Yes, this is a bit of a hack...):

public class SliderDemoController implements Initializable {
    @FXML
    private RangeSlider range;
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        ChangeListener<Scene> initializer = new ChangeListener<Scene>() {
            @Override
            public void changed(ObservableValue<? extends Scene> obs, Scene oldScene, Scene newScene)  {
                if (newScene != null) {
                    range.applyCss();
                    range.getParent().layout();
                    Pane thumb = (Pane) range.lookup(".range-slider .low-thumb");
                    System.out.println(thumb); // <-- No longer null
                    range.sceneProperty().removeListener(this);
                }
            }
        };
        range.sceneProperty().addListener(initializer);
    }
}

Depending on your exact requirements, you might be able to get away with something a little less ugly, such as performing the lookup in an event handler or a listener on the slider's highValue or lowValue properties.