JavaFX tooltip event propagation quick tip

Using tooltips in different environments than JavaFx, like VLC Delphi or C# WPF, I’m used to behaviour that when user click outside of tooltip or tooltip owner then tooltip is hidden and also event is transmitted to original event target.
First part (hiding) working as expected but event propagation not. But JavaFX developers not forget about this and created opportunity to push event further.
All you have to do is to change property using method setConsumeAutoHidingEvents to false:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Specifies whether the event, which caused the Popup to hide, should be
* consumed. Having the event consumed prevents it from triggering some
* additional UI response in the Popup's owner window.
* @defaultValue true
* @since JavaFX 2.2
*/
private BooleanProperty consumeAutoHidingEvents =
new SimpleBooleanProperty(this, "consumeAutoHidingEvents",
true);
public final void setConsumeAutoHidingEvents(boolean value) {
consumeAutoHidingEvents.set(value);
}

As you can see consumeAutoHidingEvents property default value is true, so event is not propagated. This code is from PopupWindow class and Tooltip class inherits this property.
In so far as it may be appropriate for popupWindow, it is not good solution for tooltip, where do you not expect to click two times, first one only to hide tooltip and second time to make action on some another control.

JavaFX tooltip styleable parent bug

Few days ago I found problem with tooltip styling.
I wanted to show tooltip on demand (when TextField get focus) and I also wanted to style this tooltip using custom css like this:

1
2
3
4
5
6
7
8
9
.label-with-tooltip .tooltip {
-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 10, 0, 0, 0);
-fx-font-weight: bold;
-fx-padding: 5;
-fx-border-width:1;
-fx-background-color: #FBEFEF;
-fx-text-fill: #cc0033;
-fx-border-color:#cc0033;
}

I got result that when I hovered mouse on TextField then my style was applied correctly but when I show tooltip on focus event using tooltip.show(...) then tooltip didn’t get my style.

This is due to getStyleableParent method of Tooltip class with is responsible for style “propagation”. In Tooltip class getStyleableParent looks like this:

1
2
3
public Styleable getStyleableParent() {
return BEHAVIOR.hoveredNode;
}

BEHAVIOR is static class responsible for properly showing and hiding tooltip when mouse is over node. hoveredNode property is set in mouse move event handler on the node that have installed tooltip supported by BEHAVIOR class. Therefore, when you use show method to manually show tooltip then BEHAVIOR.hoveredNode property is not set and also you don’t have chance to change it, because BEHAVIOR is private class.

I found solution by overriding getStyleableParent and returning ownerNode which is set when you show tooltip using method of Tooltip class:

1
2
3
public void show(Node ownerNode, double anchorX, double anchorY) {
...
}

Here is example application where you see how it works:

JavaFX tooltip fix example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public class Main extends Application {
public static class TooltipFix extends Tooltip {
public TooltipFix(String text) {
super(text);
}
@Override
public Styleable getStyleableParent() {
Styleable styleableParent = super.getStyleableParent();
// if default behavior is not returning styleable parent
// then return owner node
if (styleableParent != null) {
return styleableParent;
} else {
return getOwnerNode();
}
}
}
@Override
public void start(Stage primaryStage) throws Exception{
GridPane gridPane = new GridPane();
Parent root = gridPane;
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
primaryStage.getScene().getStylesheets().clear();
primaryStage.getScene().getStylesheets().add(getClass().getResource("style.css").toExternalForm());
Label lblTooltipBug = new Label("Tooltip bugged");
lblTooltipBug.getStyleClass().add("label-with-tooltip");
// original tooltip
Tooltip tooltipBug = new Tooltip("I'm broken");
lblTooltipBug.setTooltip(tooltipBug);
gridPane.add(lblTooltipBug, 0, 0);
Label lblTooltipFix = new Label("Tooltip fixed");
lblTooltipFix.getStyleClass().add("label-with-tooltip");
gridPane.add(lblTooltipFix, 0, 1);
// fixed tooltip
TooltipFix tooltipFix = new TooltipFix("I'm repaired");
lblTooltipFix.setTooltip(tooltipFix);
Button btnShowTooltip = new Button("Show tooltips");
btnShowTooltip.setOnAction(event -> {
showTooltip(lblTooltipBug);
showTooltip(lblTooltipFix);
});
gridPane.add(btnShowTooltip, 0, 2);
gridPane.setVgap(20);
gridPane.setAlignment(Pos.CENTER);
}
public static void showTooltip(Label label) {
if (label != null && label.getTooltip() != null) {
// offset
Point2D point = label.localToScene(100, 0);
label.getTooltip().setAutoHide(true);
label.getTooltip().show(label, point.getX()
+ label.getScene().getX() + label.getScene().getWindow().getX(), point.getY()
+ label.getScene().getY() + label.getScene().getWindow().getY());
}
}
public static void main(String[] args) {
launch(args);
}
}

Welcome on my blog

Hi, my name is Lukas Sliwinski, I’m developer for about 10 years and I live in Poland.

On a daily basis I work in Delphi language creating Windows desktop applications (yes, it can be still powerful :)) in company that is a specialized manufacturer and supplier of comprehensive systems mainly for large and medium-sized enterprises.
In my work I also sometimes create software using other languages, mostly Java, C#, Javascript and Objective-C.

I like learn new technologies and languages. Mostly Scala language, Play! framework, node.js and JavaFX.

Within these mentioned areas you will be able to hopefully learn something new here.

Best regards,
Lukas.