0

I want to show tooltip while clicking one button.

My Application Pic

Relative position between this button and tooltip looks like:

Relative Position

I calculate the coordinates of Point B by:

B.y = (Button.minY + Button.maxY) / 2 - Tooltip.height / 2

I found that the value obtained by tooltip.getHeight() is larger than the actual height of the displayed tooltip.

tooltip.getHeight() seems to be the height of the Window, but what I actually want is the height of the displayed tip.

enter image description here

Following is the simple code, you can use it by calling showTooltip(Button) method:

public void showTooltip(Button btn) {
    btn.setOnAction(event -> {
        Tooltip tooltip = new Tooltip();
        tooltip.setText("TestTooltip");
        tooltip.setAutoHide(true);
        tooltip.setOnShown(windowEvent -> {
            Point2D anchor = anchor(btn, tooltip);
            tooltip.setX(anchor.getX());
            tooltip.setY(anchor.getY());
        });
        tooltip.show(btn.getScene().getWindow());
    });
}

private static Point2D anchor(final Node node, final Tooltip tooltip) {
    final Bounds bounds = node.localToScreen(node.getBoundsInLocal());
    final double tipHeight = tooltip.getHeight();
    final int xOffset = 2;

    double x = bounds.getMaxX() + xOffset;
    double y = (bounds.getMinY() + bounds.getMaxY()) / 2 - tipHeight / 2;
    // Not Correct
    System.out.println(tipHeight);

    return new Point2D(Math.max(x, 0), Math.max(y, 0));
}

Run Code Result

4
  • 2
    Tooltip inherits from Window, so perhaps you can check the height of its Scene. Commented May 28 at 12:07
  • 1
    Can you create a complete minimal reproducible example that demonstrates the issue? There are many possible reasons this might not work correctly (for example getting the height of the tooltip before layout has been calculated on it). Commented May 28 at 13:06
  • 2
    I found that Popup is better component to achieve what I want. It does not cause this problem. Here is the relevant information. Commented May 28 at 17:56
  • 2
    Likely an xy problem. Commented May 28 at 20:58

1 Answer 1

4

There's two things causing the misalignment:

  1. The default anchor location of a tooltip is CONTENT_TOP_LEFT.

  2. The default application user-agent stylesheet (modena.css) gives tooltips a drop shadow with a non-zero y-offset.

Neither of those two points are documented, as far as I can tell.

To properly center the tooltip you need to set the anchor location to WINDOW_TOP_LEFT and you need to account for the y-offset of the drop shadow. Here's a proof-of-concept:

import java.util.function.Function;
import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.PopupWindow.AnchorLocation;
import javafx.stage.Stage;

public class Main extends Application {

  @Override
  public void start(Stage primaryStage) {
    var button = new Button("Test");

    var tooltip = new Tooltip("Test button's tooltip.");
    tooltip.setAnchorLocation(AnchorLocation.WINDOW_TOP_LEFT);
    tooltip.setOnShown(
        _ -> {
          var bounds = button.localToScreen(button.getBoundsInLocal());
          tooltip.setAnchorX(bounds.getMaxX() + 5);
          // '3' accounts for the y-offset of the tooltip's drop shadow
          tooltip.setAnchorY(bounds.getCenterY() - tooltip.getHeight() / 2 + 3);
        });
    button.setTooltip(tooltip);

    // these lines only exist to help see the alignment
    var topLine = createLine(Color.RED, button, Bounds::getMinY);
    var centerLine = createLine(Color.GREEN, button, Bounds::getCenterY);
    var bottomLine = createLine(Color.RED, button, Bounds::getMaxY);

    var root = new StackPane(topLine, centerLine, bottomLine, button);
    var scene = new Scene(root, 300, 150);
    primaryStage.setScene(scene);
    primaryStage.show();
  }

  private Line createLine(Color stroke, Button button, Function<Bounds, Double> mapper) {
    var line = new Line();
    line.endXProperty().bind(button.sceneProperty().map(Scene::getWidth));
    line.startYProperty().bind(button.boundsInParentProperty().map(mapper));
    line.endYProperty().bind(line.startYProperty());
    line.setStroke(stroke);
    line.setStrokeWidth(2);
    line.setManaged(false);
    return line;
  }
}

Output:

Screenshot of running proof-of-concept.

This code adds 3 to the y-anchor of the tooltip to account for the y-offset of the drop shadow. The value of 3 was taken from modena.css. It may be different across different versions of JavaFX. I tested on JavaFX 24.0.1. A more robust approach would probably be to get the effect from the tooltip. Note it's the label within the tooltip that actually has the drop shadow effect. Of course, there's no guarantee the effect will remain a drop shadow in future versions of JavaFX.

You could also customize the styling of the tooltip. That way you know exactly what you need to account for. For instance, you could remove the effect entirely.

Or you could use your own Popup or PopupControl. That might make more sense semantically. Plus, Tooltip comes with functionality you may not need.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.