I'm looking for the most concise way to deal with focus in an application which renders a map in a canvas component. You can pan the map location using arrow keys or ASWD keys. So far, I've been giving the canvas focus at startup and handling key pressed events via canvas.setOnKeyPressed().
This works fine, but I've always known that a problem was on the horizon when other components enter the picture. Once you interact with another component, it gains focus, and you're unable to scroll around the canvas map. I can prevent this from happening with some components like Hyperlinks or Buttons (I don't need tab-navigation) with something like this for those components:
sidePanel.getChildren().forEach(node -> node.setFocusTraversable(false));
But, when we get to things like TextArea or TextField, those do need to hold focus while they're being edited. And I'll need some way to return focus back (or at least unfocus those components) without being an annoyance to the user. (I don't want to have to click the canvas for it to regain focus.)
The options I see for returning focus back to the canvas after the user is done with those fields seem to be:
Add a key handler (ex. ESC or ENTER keypress) on EACH of these components which returns focus back to the canvas.
- Maybe not so concise, and a bit of a pain... also feels a bit fragile; if I miss something and the canvas loses focus, it would fail - I need a 100% reliable solution.
Extend each of these components and add similar code to return focus back to Canvas
- Even nastier and requires using custom components in Scene Builder, which is not ideal.
Add a global event handler on the Scene and transmit events to the controller which owns the canvas
- I believe an event filter would accomplish this - but on the other hand if the user is simply using arrow keys to move around a TextArea, I wouldn't want the Canvas map to move!
- To solve the above problem, possibly the global event handler could ignore ASWD and arrow keypresses if the focus is on certain types of components? Is this worth trying, or am I neglecting a problem this would create?
Are there any other simple options out there that I've missed - and what would you suggest as the best option here? I'd like an automatic solution that doesn't require remembering to add some workaround code every time a UI component is added.