Improvements for Focus Traversal code

John Hendrikx john.hendrikx at gmail.com
Tue May 16 11:54:57 UTC 2023


Hi list,

I've gone down another rabbit hole in JavaFX, this time related to focus 
traversal.

# Navigation keys are not handled universally for all controls

Some controls install custom behaviors, and some don't.  The custom 
behaviors often include direct handling of navigation keys 
(getFocusTraversalMappings).  The controls that don't (often controls 
that are not focus traversable by default, like Label) rely on the 
SceneEventDispatcher to handle navigation keys.

However, this leads to subtle differences; when placed inside another 
control that may be interested in the navigation keys (like ListView, 
TableView, ScrollPane) a Button will still respond to all navigation 
keys directly (and correctly), while a focus traversable Label will let 
these events bubble up, possibly to be consumed by its parent (ie. 
pressing DOWN may not focus another control inside the View, but instead 
scroll the view down as if the View had focus).  This is inconsistent.

==> As any Node can be made focus traversable, I think the handling of 
navigation keys should be done by Node itself; basically, after all 
event handlers have run for a Node, a final event handler should be 
called which checks for unconsumed navigation key events, and triggers 
navigation only then.  This would make all Nodes work consistently where 
navigation is concerned and would remove the need to add focus traversal 
key mappings in Behaviors.  Button would respond as it always has, while 
a focus traversable label would now work exactly as a Button would, even 
when nested in a View type parent.

# Translate Navigation Keys to Higher Level Events

When a key is determined to trigger navigation, we should translate this 
to a new type of event, for example NavigationEvent (or TraversalEvent). 
This Key event that triggered it is consumed, and this new event is 
fired.  This new event captures the intention to navigate somewhere 
(Navigate NEXT, PREVIOUS, UP, DOWN, LEFT, RIGHT) and participates in the 
usual filtering/bubbling phases.  This is far better than having a 
(potentially platform specific) key event with multiple possible 
meanings (sometimes it navigates, sometimes it scrolls) when one wants 
to deal with navigation issues.

This would solve a number of problems:

- Navigation events can all be handled in the same place, the Scene.
- One can trigger navigation in a nice programmatic way, instead of 
simulating a key press, which may be platform specific, one can send out 
a NavigationEvent#NEXT event type.
- Influencing the focus traversal mechanism is much easier, as these 
events can be listened to, filtered and consumed as needed. Contrast 
this with trying to do this with key events which may have different 
meanings depending on the state of the control receiving them.  Blocking 
navigation for example is a matter of consuming a type of Navigation 
event before it bubbles up to the Scene.
- It could allow for focus traversal that goes beyond Scenes (ie. focus 
another Scene, or a Swing control).  Navigation events not consumed by 
Scene could trigger other actions.  For NEXT/PREVIOUS navigation this 
may require a Scene setting that prevents wrapping around to the 
first/last control, and perhaps with the help of some further Navigation 
event variations (like nagivating to FIRST or LAST).
- A lot of the Accessor code to be able to reach the (private) traverse 
methods would not be needed anymore as the event system can reach the 
Scene code without needing accessors

--John





More information about the openjfx-dev mailing list