Focus delegation API

John Hendrikx john.hendrikx at gmail.com
Sun Dec 8 18:38:56 UTC 2024


On 06/12/2024 20:16, Andy Goryachev wrote:
>
> Returning to this discussion.
>
>  
>
> Consider the following scenario: a compound control such as ComboBox,
> which has an editable TextField control as a part of its skin.  It
> might be expected that the ComboBox shows the focused border instead
> of its TextField even when the latter has the input focus.
>
>  
>
> When the user presses and releases a key, the key events are first
> dispatched to the input focus owner which is the TextField.  This part
> is correct.
>
No, this is incorrect.  The focus owner is the ComboBox, not some inner
component of it, and so events are dispatched to the ComboBox.  The
focused border is shown around the ComboBox, which is exactly what is
expected.  As far as the user is concerned, ComboBox is a fully
implemented native control without substructure; to make the
implementation of such a control a bit easier (and less repetitive),
JavaFX has elected to allow Controls to have children that can handle
some of the tasks of a complicated control.  These children however are
an implementation detail(as they must be as Skins provide them, and they
are not mandated to follow specifics.  It also would have been perfectly
acceptable to have created a ComboBox control that does not rely on any
other controls and replicates all TextField functionality natively.
>
>  
>
> The issue we seem to be struggling with is that, unlike the case when
> TextField is used as a top level control, now it is a part of a
> ComboBox.  Which means it should not handle some keys/events, instead
> delegating them to the top level control - there should be a single
> "controller" (in MVC parlance), instead of two fighting each other. 
> This statement applies to built-in controls as well as the custom
> controls.
>
I think you have this upside down.

The single controller is the ComboBox.  Events arrive there first.  It
encapsulates all its other functionality, including the TextField (if
there is one at all, perhaps it is all "custom" drawn).  The ComboBox,
fully in control, can decide that it wants some events handled by its
children if that is convenient.  This is done by internally forwarding
the event at a child (you can even fire the event --directly-- at the
child component, there is no hierarchy requirement here).

The opposite is also true; all events an inner control may generate will
have to pass scrutiny by the ComboBox, or the ComboBox is free to
configure its children in such a way that they won't generate events
(ie. by capturing triggering events before they reach the control, by
replacing the event dispatcher or setting event handlers directly).

Michael's proposal streamlines all of this, providing one solid
implementation as it is easy to make mistakes in the event handling, or
even use hacky work-arounds, like subclassing, refiring events at new
targets, fake focuses...

 
>
> One way to accomplish that is to register a bunch of event *filters*
> with the top level control, to prevent the events to arrive at the
> inner control, forwarding these events to the top level controller,
> which in JavaFX case is the behavior.
>
>  
>
> The other way which I think will be easier, is to use the proposed
> InputMap to remove the undesired mappings from the inner control. 
> Doing so does not involve subclassing of the inner controls, since the
> input map and the mappings will be a public API.  With the mappings
> disabled (or remapped to the functions provided by the top level
> control), there is no contention between the two anymore.  The top
> level /control/'s /controller/ is in full /control/, it can do
> anything that's required to the inner controls - setting pseudo
> styles, inserting text, etc.  As an example - the right arrow key in
> the combo box's text field can be remapped to a function which checks
> if the caret is at the last position and then move the focus to the
> button, if so desired.  And if the caret is not at the last position,
> the default function of text area is invoked - all that enabled by the
> InputMap.
>
There is no subclassing needed in the focus delegation API either;
that's one of the hacks it will be removing.  InputMap does not need to
be involved with this at all.  I mean, you are basically seeing exactly
the intention (the top level control's controller is in full control)
but then involve the InputMaps unnecessarily.  InputMaps are just event
handlers backed by a Key to Action map. Adding focus concerns here is
clearly overstepping their bounds, not to mention they would have to
support arbitrary events.

--John
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20241208/da515f83/attachment.htm>


More information about the openjfx-dev mailing list