RFR: 8343956: Focus delegation API [v3]

Michael Strauß mstrauss at openjdk.org
Thu Jul 17 05:58:54 UTC 2025


On Mon, 14 Jul 2025 18:59:49 GMT, John Hendrikx <jhendrikx at openjdk.org> wrote:

>> Yeah, I'm sure traversal is solvable, I just wanted to spend some cycles figuring it out. For example, would you set focusTraversable on the date/time control AND it's delegates? My guess is just the delegates but this is the sort of thing one would want to prototype.
>> 
>> To be clear I'm not trying to solve traversal in this round. I just want to make sure we have mapped out the path forward so the API will accommodate traversal eventually.
>
> When I have a few moments, I'll see if I can create the date entry control, and see how it works with this solution.  I can then override Scene code to see if navigation is a quick fix or a huge issue.

> Currently we turn on input method processing at the platform level if and only if there's any control in the focus chain that is set up to receive input method events and respond to input method requests. That could be any focusOwner in any focused Scene or any one of the focusOwners' delegates. So I need to see the entire delegate chain.
> 
> If we don't expect the focus delegate to change while a focus scope node has focus then all I need is getFocusDelegate with no parameters. If we expect the focus delegate to change dynamically it should become a property.

The focus delegate can change while a focus scope has focus, but only due to a focus request (i.e. navigation or calling `Node.requestFocus()`). I'm reasonably certain that we don't need or want the delegate to be a property. As of now, the delegate chain is simply re-evaluated in response to a focus request.

However, there's currently no easy way for `Scene` to know when the delegate chain has changed.

> The existing traversal machinery is designed to update a Scene's focusOwner not which delegate is active within the current focusOwner.

Focus traversal is not aware of what a delegate is, nor should it be. This means that focus traversal _will_ update the delegate chain if you traverse into a node that hoists focus.

I think it's important to point out that the primary use case of focus delegation is the creation of black-box controls, but focus delegation is not the same level of abstraction as black-box controls. It's better to think of focus delegation as a basic building block, and black-box controls as a higher-level abstraction.

At the `Node` level (where controls are not a thing yet), it is more approriate to picture focus delegation as a kind of openly visible multi-level focus. In this model, focus traversal doesn't differentiate between nodes based on how they came into being (e.g. as part of a skin), it simply applies the traversal algorithm on the scene graph as it is, being completely unaware of the logical grouping that is established by controls.

> In the Date/Time control you give as an example how would the user move focus between the various delegates? What would be the most intuitive model for the user? Personally I prefer the one used in macOS Calendar where Tab is used to move between the month, day, and year (or hour and minute). But that creates a conflict with using Tab to move focus into and out of the Date/Time control as a whole.

I don't know what it would mean to move focus into and out of a control as a whole. You can only meaningfully move focus into a composite control if it has at least one sub-node that can be focused. If there is no such node, then you trivially can't use focus delegation. If, on the other hand, you have a focusable sub-node, then "moving into" the composite control just means focusing the sub-node. And "moving out" of the composite control means focusing the next logically focusable node in the scene graph. I don't see any conflict here.

> In any case the question is whether a monolithic control with multiple delegates is expected to roll its own internal traversal mechanism or if we need to extend the existing mechanism to include delegates. Having each control roll its own internal traversal would hide it from the accessibility frameworks. And if we expect the existing traversal keys (like Tab) to work internally we should extend the existing traversal machinery to work with delegates.

I haven't extensively tested this yet with custom composite controls, but I think the existing traversal logic will just work as it is.

-------------

PR Review Comment: https://git.openjdk.org/jfx/pull/1632#discussion_r2212343939


More information about the openjfx-dev mailing list