<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Aptos;
panose-1:2 11 0 4 2 2 2 2 2 4;}
@font-face
{font-family:"Iosevka Fixed SS16";
panose-1:2 0 5 9 3 0 0 0 0 4;}
@font-face
{font-family:"Times New Roman \(Body CS\)";
panose-1:2 11 6 4 2 2 2 2 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0in;
font-size:10.0pt;
font-family:"Aptos",sans-serif;}
a:link, span.MsoHyperlink
{mso-style-priority:99;
color:blue;
text-decoration:underline;}
span.EmailStyle19
{mso-style-type:personal-reply;
font-family:"Iosevka Fixed SS16";
color:windowtext;}
.MsoChpDefault
{mso-style-type:export-only;
font-size:10.0pt;
mso-ligatures:none;}
@page WordSection1
{size:8.5in 11.0in;
margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
{page:WordSection1;}
--></style>
</head>
<body lang="EN-US" link="blue" vlink="purple" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">>
</span><span style="font-size:11.0pt">Yes, each step of the event dispatch chain gets its own event<br>
instance.</span><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Yep, this seems unnecessary and counterproductive to me. All we need is to drop the target field from the event.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">>
</span><span style="font-size:11.0pt">2. ComboBox's skin has installed an event filter on ComboBox</span><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">So we have another scenario where different priorities are needed: adding event filters.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">>
</span><span style="font-size:11.0pt">3. However, it must forward the event to the TextField (as otherwise<br>
the TextField doesn't work), so it fires off a copy of the event<br>
targeted at TextField.</span><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Maybe instead, there should be a way to send the event to a Node directly, without bubbling up. These internal events should never propagate outside of the skin's internals.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">>
</span><span style="font-size:11.0pt">Consumed events are dispatched to all listeners on the<br>
same step of the event dispatch chain. I've often suggested that we<br>
finally fix this bug.</span><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Agree.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Moving to the second part:<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">>
</span><span style="font-size:11.0pt">1. When you have abstraction, you also want encapsulation.</span><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Agree. Swing does not do it (combo box's text field is a top level component, being the to "focusOwner"), but I buy the idea of encapsulation of controls.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">>
</span><span style="font-size:11.0pt">2. From this it necessarily follows that the focus owner must always<br>
be the control, and not any part of its internal and opaque<br>
substructure.</span><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Agree.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">>
</span><span style="font-size:11.0pt">3. When the control is the focus owner, then it will receive all key<br>
events. We need a way to reconcile the JavaFX event system with the<br>
internal substructure, without exposing the internals to the outside<br>
world, and without changing the user model of the control.</span><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Agree.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">>
</span><span style="font-size:11.0pt">This is part of what focus delegation solves: it separates different<br>
levels of abstraction. It allows key events to be sent to the control,<br>
which is exactly what users of a control would expect. Every layer of<br>
abstraction will see exactly what it would expect to see: an event<br>
that is targeted at itself, not at some other thing.<br>
<br>
> It also allows users to reason about events. For example, when a<br>
listener inspects a key event, it will see that the event is targeted<br>
at the ComboBox. If it was instead targeted at FakeFocusTextField, the<br>
listener couldn't reason about the event, as there is no such thing in<br>
the user model.</span><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Well, we don't need to add a bunch of weird properties for that (the first part). Just send the events to the skin's components directly, preventing the bubbling up part.
There is no need for Event.target because there is no extraneous events being bubbled up, and both CB and TF can process the events as they come in.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">What do you think?<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">-andy<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<div id="mail-editor-reference-message-container">
<div>
<div>
<div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal" style="margin-bottom:12.0pt"><b><span style="font-size:12.0pt;color:black">From:
</span></b><span style="font-size:12.0pt;color:black">Michael Strauß <michaelstrau2@gmail.com><br>
<b>Date: </b>Monday, December 9, 2024 at 14:59<br>
<b>To: </b>Andy Goryachev <andy.goryachev@oracle.com><br>
<b>Cc: </b>openjfx-dev <openjfx-dev@openjdk.org><br>
<b>Subject: </b>[External] : Re: Focus delegation API<o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal"><span style="font-size:11.0pt">Hi Andy, your example highlights the observable defect that I've been<br>
trying to explain. Answers to your questions below.<br>
<br>
<br>
On Mon, Dec 9, 2024 at 10:27</span><span style="font-size:11.0pt;font-family:"Arial",sans-serif"> </span><span style="font-size:11.0pt">PM Andy Goryachev<br>
<andy.goryachev@oracle.com> wrote:<br>
><br>
> OK, let's backtrack a bit.<br>
><br>
> consider this example<br>
> <a href="https://urldefense.com/v3/__https:/github.com/andy-goryachev-oracle/Test/blob/main/src/goryachev/research/ComboBox_Events.java__;!!ACWV5N9M2RV99hQ!LKECD0WK6vtwHmtgrjyLZtP8ewEdwKDErX57HgvozbPNhmM-FGxTjzFchI_2JaLjjbmfjDLtbAPuQ-n2C5uFsQa3hx6M$">
https://urldefense.com/v3/__https://github.com/andy-goryachev-oracle/Test/blob/main/src/goryachev/research/ComboBox_Events.java__;!!ACWV5N9M2RV99hQ!LKECD0WK6vtwHmtgrjyLZtP8ewEdwKDErX57HgvozbPNhmM-FGxTjzFchI_2JaLjjbmfjDLtbAPuQ-n2C5uFsQa3hx6M$</a>
<br>
><br>
> it's a single combo box with application-level filters and handlers attached to scene, combo box, and the combo box editor.<br>
><br>
> pressing and releasing a key, we get the following output:<br>
><br>
> > user presses 'a' key<br>
> 1. stage filter: KEY_PRESSED h=913 target=876<br>
> 2. combobox filter: KEY_PRESSED h=457 target=876<br>
> 3. stage filter: KEY_PRESSED h=487 target=553<br>
> 4. combobox filter: KEY_PRESSED h=123 target=553<br>
> 5. editor filter: KEY_PRESSED h=372 target=553<br>
> 6. editor handler: KEY_PRESSED h=372 consumed! target=553<br>
> [...]<br>
> This output is very puzzling, please help me understand it.<br>
><br>
> - with the exception of events logged from the editor, the events being sent are all different instances (h= prints the event's hashCode)<br>
<br>
Yes, each step of the event dispatch chain gets its own event<br>
instance. This is due to the immutable nature of the Event class, as<br>
on each step the source (and/or target) field must be changed; the way<br>
JavaFX does this is by copying the event instance and adjusting the<br>
fields in the process.<br>
<br>
<br>
> - where did the duplicate event in step 3 come from? step 9? step 15?<br>
<br>
That's the core of the issue. Here's what's happening, step by step:<br>
<br>
1. ComboBox is the focus owner of the scene, so the KEY_PRESSED event<br>
is sent to the ComboBox.<br>
<br>
2. ComboBox's skin has installed an event filter on ComboBox, and when<br>
it receives a key event, it consumes the event. This is why event<br>
dispatching stops after step 2 in your example.<br>
<br>
3. However, it must forward the event to the TextField (as otherwise<br>
the TextField doesn't work), so it fires off a copy of the event<br>
targeted at TextField. The new event starts again at the root ("stage<br>
filter") and travels to the ComboBox ("combobox filter").<br>
<br>
4. When ComboBox receives the duplicate event, it inspects the target<br>
of the event, and realizes that it is targeted at the TextField. In<br>
this special case, the event is not consumed, but allowed to travel<br>
further.<br>
<br>
5. Now the duplicate event reaches the TextField ("editor filter"),<br>
where nothing happens, and begins to bubble up ("editor handler").<br>
This is where the event is consumed by the TextField<br>
<br>
<br>
I hope that you agree that the duplicated event, being visible to all<br>
listeners, is a defect. When a key is pressed once, a listener should<br>
not see two KEY_PRESSED events for the same key.<br>
<br>
<br>
> - why a consumed event is being dispatched to the editor in line 6? line 12?<br>
<br>
That's a bug. Consumed events are dispatched to all listeners on the<br>
same step of the event dispatch chain. I've often suggested that we<br>
finally fix this bug.<br>
<br>
<br>
> Maybe we should ask the question why in this case the Scene.focusOwner is the ComboBox, and not its editor, the TextField? Having the actual component that receives the input events to be the focus owner would eliminate the need for hacks in the skin to
send events, wouldn't it?<br>
><br>
> Or, if we say that's the grand design of javafx - that the skins must forward event copies to the skin constituents, then adding a handler to the top level control (combo box in this case) should result in that handler to be called before the skin's one,
right?<br>
<br>
The core of JavaFX is just the scene graph, there's no concept of<br>
controls or skins (that's added by javafx.controls). I would say that<br>
a "skin", "shadow DOM", "internal/external" type of abstraction is a<br>
very useful abstraction, and JavaFX should support the creation of<br>
such abstractions.<br>
<br>
1. When you have abstraction, you also want encapsulation. That means<br>
that the internal implementation of a control should not leak into its<br>
user model. Whether ComboBox contains a TextField, or whether it<br>
"custom draws" its text editor (for example, by writing pixels<br>
directly to a Canvas) shouldn't affect users of ComboBox.<br>
<br>
2. From this it necessarily follows that the focus owner must always<br>
be the control, and not any part of its internal and opaque<br>
substructure. Users of ComboBox shouldn't have to change their code<br>
when they switch to a custom-drawing skin.<br>
<br>
3. When the control is the focus owner, then it will receive all key<br>
events. We need a way to reconcile the JavaFX event system with the<br>
internal substructure, without exposing the internals to the outside<br>
world, and without changing the user model of the control.<br>
<br>
This is part of what focus delegation solves: it separates different<br>
levels of abstraction. It allows key events to be sent to the control,<br>
which is exactly what users of a control would expect. Every layer of<br>
abstraction will see exactly what it would expect to see: an event<br>
that is targeted at itself, not at some other thing.<br>
<br>
It also allows users to reason about events. For example, when a<br>
listener inspects a key event, it will see that the event is targeted<br>
at the ComboBox. If it was instead targeted at FakeFocusTextField, the<br>
listener couldn't reason about the event, as there is no such thing in<br>
the user model.<br>
<br>
<br>
> This, I think, brings us again to the discussion of event handling priority. BTW, you never responded to my questions in<br>
> <a href="https://mail.openjdk.org/pipermail/openjfx-dev/2024-November/050655.html">
https://mail.openjdk.org/pipermail/openjfx-dev/2024-November/050655.html</a><br>
<br>
I did: <a href="https://mail.openjdk.org/pipermail/openjfx-dev/2024-November/050753.html">
https://mail.openjdk.org/pipermail/openjfx-dev/2024-November/050753.html</a><o:p></o:p></span></p>
</div>
</div>
</div>
</div>
</div>
</body>
</html>