Reputation: 6911
I am trying to have a JavaFX Pane
(VBox
in my case, but I don't think it matters) that has a ContextMenu
that behaves correctly.
I have found these two questions: why Pane
s can't have ContextMenu
s and How to create ContextMenu
within a Pane
.
The problem I'm having with these two solutions (which are very similar) is that while the context menu correctly disappears if I click on the pane, it doesn't disappear if I click inside a control within that pane. The simplest way to observe this flaw is to create such a pane with a TextField
. Right-click on the pane to show the context menu, then click inside to TextField
to focus on it. While a proper context menu would disappear at this point, this "hacked-in" context menu (for lack of a better term) happily stays in it's place, possibly blocking the user's view of the text field they are trying to fill.
Now, I know I can add a change listener to the focused
property of each and every control on my pane, but that feels redundant. Is there a better way to make sure the context menu is hidden when a control in my pane is selected (or, more accurately - when the user click the mouse anywhere in the owning window outside the context menu)?
What I tried so far and doesn't work -
focused
property - it appears the pane isn't considered focused if one of it's children isfocused
property - it appears the context menu's focus isn't changed when clicking outside of it. Upvotes: 2
Views: 877
Reputation: 6911
Ok, so after some digging in the source code for JavaFX I have found this workaround (which is what "solves" this problem for normal controls). Simply add this line of code -
contextMenu.setImpl_showRelativeToWindow(true);
Now, I know using internal implementation methods is discouraged as they may disappear, but this is the only solution I have found. If anyone has a better solution I'll be glad to hear it, but I suspect this is simply a bug that should be filed (i.e. - there should be a way to use the showRelativeToWindow
mechanism when setting context menus on panes).
I guess a somewhat safer solution would be to have a throwaway control (not in the scene graph) on which to set the context menu, but I don't know if this would have any unwanted side effects:
Label throwaway = new Label(); // No special reason for using Label, could be any Control.
throwaway.setContextMenu(contextMenu); // note that this is the only place `throwaway` is used, it is never added to the scene graph
// or referenced again, but just setting the context menu on a control solves the problem.
Edit
After digging some more I have found this, and a solution in the discussion - you should call the show
method overload which takes a window, not a node! Not very clear, but it works:
myPane.setOnContextMenuRequested(event ->
contextMenu.show(myPane.getScene().getWindow(), event.getScreenX(), event.getScreenY())
);
Upvotes: 5