I am trying to work around this bug in the jdk: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8088624
public class Blubb extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Button btn = new Button("Click");
btn.setTooltip(new Tooltip("Blubb"));
Scene scene = new Scene(new BorderPane(btn), 320, 240);
primaryStage.setScene(scene);
primaryStage.show();
Stage secondStage = new Stage();
secondStage.setScene(new Scene(new BorderPane(new Button("Click")), 320, 240));
//secondStage.initOwner(primaryStage);
secondStage.show();
}
}
If the button on the primary stage is hovered, it will come in front of the second stage. I found that calling initOwner()
on a Stage will eliminate this behavior.
Now my problem is following: I have multiple "popups" that have a common owner (the primary stage). Hovering over controls on the primary stage doesn't cause any unexpected behavior after the initOwner()
workaround. If you however hover over controls in a popup while another popup was in focus, the hovered popup will steal focus.
Is there a way I can work around this bug for not only the primary stage but also the popups?
UPDATE: turns out my workaround has undesired side-effects. Javadocs for Stage state following:
A stage will always be on top of its parent window.
So additionally, what would be a workaround that makes the popup not "always on top" and minimizable?
There is a way to get around it by overlaying StackPanes. Create your Scene
with a StackPane
so that you can add another StackPane
when the stage has lost its focus. The overlayed pane will prevent Tooltip
s or anything else happening on mouse-over while the pane is not in focus. You may also minimize any of your stages and they won't be always-on-top.
public class Blubb extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Button button_1 = new Button("Button #1");
button_1.setTooltip(new Tooltip("Blubb #1"));
StackPane primary = new StackPane(new BorderPane(button_1));
primaryStage.setScene(new Scene(primary, 320, 240));
addStageFocusListener(primaryStage, primary);
primaryStage.show();
Button button_2 = new Button("Button #2");
button_2.setTooltip(new Tooltip("Blubb #2"));
StackPane second = new StackPane(new BorderPane(button_2));
Stage secondStage = new Stage();
addStageFocusListener(secondStage, second);
secondStage.setScene(new Scene(second, 320, 240));
secondStage.show();
}
public void addStageFocusListener(Stage stage, StackPane stackPane) {
stage.focusedProperty().addListener(new ChangeListener<Boolean>(){
public final StackPane preventTooltip = new StackPane();
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(stage.isFocused()) {
if(stackPane.getChildren().contains(preventTooltip)) {
stackPane.getChildren().remove(preventTooltip);
}
} else {
stackPane.getChildren().add(preventTooltip);
}
}
});
}
}
You can try this:
public static final disableMouseEventOnUnfocus(final Stage stage)
{
if (stage == null
|| stage.getScene() == null
|| stage.getScene().getRoot() == null)
return;
else
{
stage.getScene().getRoot().mouseTransparentProperty().bind(stage.focusedProperty().not());
}
}
I didn't try it though, but if it works, this should be a good alternative. There is no need to restructure your layout, and you can leave all your layout in FXML, without specifying fx:id
for the tooltips.
You could try to unset the tooltip whenever the node's window loses focus. Such as below:
public class Blubb extends Application {
public static void main(String[] args) {
launch(args);
}
public static void installTooltip(Node n, Tooltip tp)
{
Window w = n.getScene().getWindow();
w.focusedProperty().addListener((val, before, after) -> {
if (after)
Tooltip.install(n, tp);
else
Tooltip.uninstall(n, tp);
});
if (w.isFocused())
Tooltip.install(n, tp);
else
Tooltip.uninstall(n, tp);
}
@Override
public void start(Stage primaryStage) throws Exception {
Tooltip tp = new Tooltip("Blubb");
Button btn = new Button("Click");
Scene scene = new Scene(new BorderPane(btn), 320, 240);
primaryStage.setScene(scene);
//primaryStage.show();
Stage secondStage = new Stage();
secondStage.setScene(new Scene(new BorderPane(new Button("Click")), 320, 240));
//secondStage.initOwner(primaryStage);
secondStage.show();
primaryStage.show();
installTooltip(btn, tp);
}
}
Of course, you would have to call installTooltip
after the node is added to the component.
I've come up with this alternative solution, as I've found it easier in my case to subclass Tooltip and apply a fix there. I just overload the show() method to only show if the owning window is focused. It's working like a charm for me...
public class FixedTooltip extends Tooltip {
public FixedTooltip(String string) {
super(string);
}
@Override
protected void show() {
Window owner = getOwnerWindow();
if (owner.isFocused())
super.show();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With