Search code examples
javajavafxtooltipstyling

JavaFX How Can I Center a Tooltip on a Node?


I'm currently working on a form that features validation upon changing focus from one node to another and wish to display a tooltip centered above a node containing an error if one exists. I'm using the tooltip's show(Node ownerNode, double anchorX, double anchorY) method signature to specify which node I would like to attach it to and where to position it.

I've tried the following code:

Tooltip tooltip = new Tooltip("Error"); 
tooltip.show(node, 0, 0);
double tooltipMiddle = tooltip.getWidth() / 2;
tooltip.hide(); 
double nodeMiddle = node.getWidth() / 2; 

//Gets the node's x and y position within the window
Bounds bounds = node.localToScene(node.getBoundsInLocal());  

//Tooltip's bottom-right corner is set to the anchor points, so I set x to
//the node's x coordinate plus half the width of the node plus half the width
//of the tooltip 
tooltip.show(node,
             bounds.getMinX() + nodeMiddle + tooltipMiddle, bounds.getMinY());

This has gotten me very close to the center, but it's still off. I've been all over the internet trying to find help, but I'm just not finding any, so I figured I'd ask here.
Any chance I could get some insight into why I'm not able to get this working how I'd like it?

Validation message screenshot


Solution

  • Code which I bring provides correct positioning of tooltip but is far from being perfect. It would take a lot of work to bring comprehensive solution (if you want we can discuss it). Going to the bottom I think you have a math problem and Tooltip class may not be the best option.

    import javafx.application.Application;
    import javafx.geometry.Bounds;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.Pane;
    import javafx.scene.layout.Region;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage;
    
    public class TooltipApp extends Application {
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage stage) throws Exception {
            TextField textField = new TextField();
            textField.setPrefWidth(100.);
    
            Button button = new Button("Tooltip");
    
            HBox hBox = new HBox(textField, button);
            hBox.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
    
            Label label = new Label("Empty!");
            label.setVisible(false);
            Pane tooltipPane = new Pane(label);
            tooltipPane.setMouseTransparent(true);
    
            StackPane stackPane = new StackPane(hBox, tooltipPane);
            stackPane.setPrefSize(600., 400.);
    
            Scene scene = new Scene(stackPane);
    
            stage.setScene(scene);
            stage.show();
    
            button.setOnMouseClicked(event -> {
                if (label.isVisible()) {
                    label.setVisible(false);
                } else {
                    Bounds sceneBounds = textField.localToScene(textField.getBoundsInLocal());
                    label.setLayoutX((sceneBounds.getMinX() + (sceneBounds.getWidth() / 2)) - (label.getWidth() / 2));
                    label.setLayoutY(sceneBounds.getMinY() - label.getHeight());
                    label.setVisible(true);
                }
            });
        }
    }