<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<p><br>
</p>
<div class="moz-cite-prefix">On 24/09/2024 20:53, Andy Goryachev
wrote:<br>
</div>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="Generator"
content="Microsoft Word 15 (filtered medium)">
<style>@font-face
{font-family:Wingdings;
panose-1:5 0 0 0 0 0 0 0 0 0;}@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}@font-face
{font-family:"Yu Gothic";
panose-1:2 11 4 0 0 0 0 0 0 0;}@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;}@font-face
{font-family:"\@Yu Gothic";
panose-1:2 11 4 0 0 0 0 0 0 0;}p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0in;
font-size:12.0pt;
font-family:"Aptos",sans-serif;}a:link, span.MsoHyperlink
{mso-style-priority:99;
color:#467886;
text-decoration:underline;}p.p1, li.p1, div.p1
{mso-style-name:p1;
mso-margin-top-alt:auto;
margin-right:0in;
mso-margin-bottom-alt:auto;
margin-left:0in;
font-size:12.0pt;
font-family:"Aptos",sans-serif;}span.outlook-search-highlight
{mso-style-name:outlook-search-highlight;}span.EmailStyle23
{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;}div.WordSection1
{page:WordSection1;}ol
{margin-bottom:0in;}ul
{margin-bottom:0in;}</style>
<div class="WordSection1">
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Dear
John:<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"">Now
with the test sprint over, let me try to respond to your
earlier questions (sorry for the delay!).</span></p>
</div>
</blockquote>
Looks like you guys did a lot of useful work in a short time. It's
good to see many tests being converted.<br>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<div class="WordSection1">
<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"">>
What is the point of TraversalEvent.NODE_TRAVERSED?<o:p></o:p></span></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">My
understanding it's an alternative to adding a
TraversalEngine.TraverseListener in the original
implementation.<o:p></o:p></span></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">The
main purpose, I think, is to notify the control of the fact
that its child got focus.<o:p></o:p></span></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">You
might be right that it might be possible to replace the
event by adding a listener to Scene.focusOwnerProperty,
though it's likely to complicate the event handling
machinery in the skins.</span></p>
</div>
</blockquote>
Yeah, I think this should not be part of the public API if it is to
handle some internal Skin problem. Events should be
consumable/filterable to prevent some action from occurring. This
Event would make sense if it initiated traversal, but instead only
informs about it.<br>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<div class="WordSection1">
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">>
What does it mean if I filter this event? What if I consume
it?<o:p></o:p></span></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Current
behavior of Skins that depend on it will break.<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"">Were
there other questions? Please let me know.</span></p>
</div>
</blockquote>
Please have a look at <a class="moz-txt-link-freetext" href="https://bugs.openjdk.org/browse/JDK-8340852">https://bugs.openjdk.org/browse/JDK-8340852</a> --
I think this should be fixed sooner rather than later, and will
probably simplify a traversal API implementation.<br>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<div class="WordSection1">
<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> <br>
</o:p></span></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Allow
me to summarize various suggestions and ideas floated during
the discussion:<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"">Additional
problems:<o:p></o:p></span></p>
<ul style="margin-top:0in" type="disc">
<li class="MsoNormal" style="mso-list:l3 level1 lfo1"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">existing
problem of consuming events that had no effect on the
control(s)</span></li>
</ul>
</div>
</blockquote>
<p>These should be fixed on a case by case basis yes.</p>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<div class="WordSection1"><br>
<ul style="margin-top:0in" type="disc">
<li class="MsoNormal" style="mso-list:l3 level1 lfo1"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">priority
of handling e.g. accelerators vs regular key events</span></li>
</ul>
</div>
</blockquote>
<p>Accelerators are handled by Scene with no way to get in between.
That sounds like a different topic altogether, and one I wouldn't
be so sure off you should be allowed to get in between. If there
is some kind of prioritized event handler system, then the user
could potentially intercept these by installing a handler with a
higher priority.</p>
<p>Most of the issues I've had with keys being consumed too early is
caused by Controls directly installing navigational keys in their
input maps, instead of leaving them to bubble up to Scene. This
is fine for controls that have specific navigation needs, but
currently all controls do this or they would break when nested
inside a ScrollPane (see
<a class="moz-txt-link-freetext" href="https://bugs.openjdk.org/browse/JDK-8340852">https://bugs.openjdk.org/browse/JDK-8340852</a>).<br>
</p>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<div class="WordSection1">
<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"">You
proposed an alternative design which consists of<o:p></o:p></span></p>
<ul style="margin-top:0in" type="disc">
<li class="MsoNormal" style="mso-list:l2 level1 lfo2"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">creating
a new hierarchy of TraversalEvents</span></li>
</ul>
</div>
</blockquote>
<p>This probably won't work, I was looking for a real purpose for
having a TraversalEvent. I think it would be sufficient to give
users tools on Scene level to do the most common navigation
options (ie. directional and logical). Anything more custom just
means that user can implement this by doing their own node
traversal (there is more than sufficient information there to
implement custom traversal strategies, beyond those that we wish
to provide out of the box with a simple
focusNext/Previous/Up/Down/Left/Right API).</p>
<p>I've dumbed down my idea to just fixing ScrollPane, and perhaps
adding some methods to Scene to trigger directional/logical
navigation. That should solve most concerns, with the left over
niche cases probably being so specific that it would require
scanning the Scene graph to find what Node you'd want to focus...<br>
</p>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<div class="WordSection1">
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">The
benefits of this idea are:<o:p></o:p></span></p>
<ul style="margin-top:0in" type="disc">
<li class="MsoNormal" style="mso-list:l2 level1 lfo2"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">ability
to completely customize traversal ("crazy custom
navigation")</span></li>
</ul>
</div>
</blockquote>
<p>We don't need this, you can already do whatever crazy custom
navigation you want, we just need the standard navigation to be
available easily (and if ScrollPane bug is solved, then you get
that completely free from Scene for your custom control). More
complex navigation can be handled by calling methods on Scene in
handlers. For example, here is a simple navigation method that you
can implement yourself in current FX:</p>
<div style="background-color:#ffffff;padding:0px 0px 0px 2px;">
<div
style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:11pt;white-space:pre;"><p
style="margin:0;"><span style="color:#000000;"> </span><span
style="color:#3f5fbf;">/**</span></p><p style="margin:0;"><span
style="color:#3f5fbf;"> * Focuses the first focusable node under the given node.</span></p><p
style="margin:0;"><span style="color:#3f5fbf;"> *</span></p><p
style="margin:0;"><span style="color:#3f5fbf;"> * </span><span
style="color:#7f9fbf;font-weight:bold;">@param</span><span
style="color:#3f5fbf;"> node a </span><span style="color:#3f3fbf;">{@link Node}</span><span
style="color:#3f5fbf;"> or child of the given node to give focus.</span></p><p
style="margin:0;"><span style="color:#3f5fbf;"> * </span><span
style="color:#7f9fbf;font-weight:bold;">@return</span><span
style="color:#3f5fbf;"> </span><span style="color:#7f7f9f;"><code></span><span
style="color:#3f5fbf;">true</span><span style="color:#7f7f9f;"></code></span><span
style="color:#3f5fbf;"> if focus was given, otherwise </span><span
style="color:#7f7f9f;"><code></span><span
style="color:#3f5fbf;">false</span><span style="color:#7f7f9f;"></code></span></p><p
style="margin:0;"><span style="color:#3f5fbf;"> */</span></p><p
style="margin:0;"><span style="color:#000000;"> </span><span
style="color:#0000a0;font-weight:bold;">public</span><span
style="color:#000000;"> </span><span
style="color:#0000a0;font-weight:bold;">static</span><span
style="color:#000000;"> </span><span
style="color:#0000a0;background-color:#d4d4d4;font-weight:bold;">boolean</span><span
style="color:#000000;"> focus(Node node) {</span></p><p
style="margin:0;"><span style="color:#000000;"> </span><span
style="color:#0000a0;font-weight:bold;">if</span><span
style="color:#000000;">(node </span><span
style="color:#0000a0;font-weight:bold;">instanceof</span><span
style="color:#000000;"> Parent p) {</span></p><p style="margin:0;"><span
style="color:#000000;"> </span><span
style="color:#0000a0;font-weight:bold;">for</span><span
style="color:#000000;">(Node child : p.getChildrenUnmodifiable()) {</span></p><p
style="margin:0;"><span style="color:#000000;"> </span><span
style="color:#0000a0;font-weight:bold;">if</span><span
style="color:#000000;">(</span><span
style="color:#000000;font-style:italic;">focus</span><span
style="color:#000000;">(child)) {</span></p><p style="margin:0;"><span
style="color:#000000;"> </span><span
style="color:#7f0055;background-color:#d4d4d4;font-weight:bold;">return</span><span
style="color:#000000;background-color:#d4d4d4;"> </span><span
style="color:#0000a0;background-color:#d4d4d4;font-weight:bold;">true</span><span
style="color:#000000;background-color:#d4d4d4;">;</span></p><p
style="margin:0;"><span style="color:#000000;"> }</span></p><p
style="margin:0;"><span style="color:#000000;"> }</span></p><p
style="margin:0;"><span style="color:#000000;"> }</span></p><p
style="margin:0;"><span style="color:#000000;"> </span></p><p
style="margin:0;"><span style="color:#000000;"> </span><span
style="color:#0000a0;font-weight:bold;">if</span><span
style="color:#000000;">(node.isFocusTraversable() && !node.isDisabled() && node.isVisible()) {</span></p><p
style="margin:0;"><span style="color:#000000;"> node.</span><span
style="color:#000000;background-color:#ceccf7;">requestFocus</span><span
style="color:#000000;">();</span></p><p style="margin:0;"><span
style="color:#000000;"> </span><span
style="color:#7f0055;background-color:#d4d4d4;font-weight:bold;">return</span><span
style="color:#000000;background-color:#d4d4d4;"> </span><span
style="color:#0000a0;background-color:#d4d4d4;font-weight:bold;">true</span><span
style="color:#000000;background-color:#d4d4d4;">;</span></p><p
style="margin:0;"><span style="color:#000000;"> }</span></p><p
style="margin:0;"><span style="color:#000000;"> </span></p><p
style="margin:0;"><span style="color:#000000;"> </span><span
style="color:#7f0055;background-color:#d4d4d4;font-weight:bold;">return</span><span
style="color:#000000;background-color:#d4d4d4;"> </span><span
style="color:#0000a0;background-color:#d4d4d4;font-weight:bold;">false</span><span
style="color:#000000;background-color:#d4d4d4;">;</span></p><p
style="margin:0;"><span style="color:#000000;"> }</span></p><p
style="margin:0;"><span style="color:#000000;">
</span></p><p style="margin:0;"><span style="color:#000000;">Ideally, you shouldn't need to do yourself. It should be sufficient to call a `focusNext` type method.
</span></p></div>
</div>
<p></p>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<div class="WordSection1"><br>
<ul style="margin-top:0in" type="disc">
<li class="MsoNormal" style="mso-list:l2 level1 lfo2"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">setting
of traversal policies with CSS</span></li>
</ul>
</div>
</blockquote>
I think this would be highly useful and so must be considered to be
a possibility. As explained in one of my replies, JavaFX CSS is not
limited to visual policies.<br>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<div class="WordSection1">
<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"">Some
additional issues were touched upon during the discussion:<o:p></o:p></span></p>
<ul style="margin-top:0in" type="disc">
<li class="MsoNormal" style="mso-list:l4 level1 lfo6"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">ScrollPane
consuming navigational keys</span></li>
</ul>
</div>
</blockquote>
That's the big one IMHO. Fix that, and most problems are already
solved; the traversal API should see how much is still needed
**after** that problem is fixed.<br>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<div class="WordSection1">
<ul style="margin-top:0in" type="disc">
<li class="MsoNormal" style="mso-list:l6 level1 lfo7"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">some
(ScrollPane, Spinner, TextField) control needlessly
consume key events (see also
<u>JDK-8320557)</u></span></li>
</ul>
</div>
</blockquote>
Yeah, we should just make PR's for those and get them fixed.<br>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<div class="WordSection1">
<ul style="margin-top:0in" type="disc">
<li class="MsoNormal" style="mso-list:l0 level1 lfo8"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">possible
accessibility regression with ScrollPane</span></li>
</ul>
</div>
</blockquote>
<p>I didn't see this anywhere.</p>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<div class="WordSection1">
<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"">Is
this a complete list? Did I miss anything?</span></p>
</div>
</blockquote>
<p>Looks pretty complete. Thanks.</p>
<p>--John<br>
</p>
<blockquote type="cite"
cite="mid:BL3PR10MB618559EE8E194D53DCE12B81E5682@BL3PR10MB6185.namprd10.prod.outlook.com">
<div class="WordSection1">
<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"">-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>
<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="color:black">From:
</span></b><span style="color:black">John Hendrikx
<a class="moz-txt-link-rfc2396E" href="mailto:john.hendrikx@gmail.com"><john.hendrikx@gmail.com></a><br>
<b>Date: </b>Thursday, September 19, 2024 at 07:24<br>
<b>To: </b>Andy Goryachev
<a class="moz-txt-link-rfc2396E" href="mailto:andy.goryachev@oracle.com"><andy.goryachev@oracle.com></a>,
<a class="moz-txt-link-abbreviated" href="mailto:openjfx-dev@openjdk.org">openjfx-dev@openjdk.org</a>
<a class="moz-txt-link-rfc2396E" href="mailto:openjfx-dev@openjdk.org"><openjfx-dev@openjdk.org></a><br>
<b>Subject: </b>Re: [External] : Re: Proposal:
Focus Traversal API<o:p></o:p></span></p>
</div>
<p>My apologies then, I was a bit impatient. Good luck
with the test sprint then!<o:p></o:p></p>
<p>--John<o:p></o:p></p>
<div>
<p class="MsoNormal">On 18/09/2024 17:13, Andy Goryachev
wrote:<o:p></o:p></p>
</div>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<div>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Oh,
sorry, I did not mean to ignore your comments. I
should have mentioned we are having a bi-annual
"test sprint" and work exclusively on the test
suite. You made a lot of good comments that
require some thought and careful consideration,
for which I simply had no spare CPU cycles last
week or this week. Sorry, will definitely respond
in detail early next week.</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">-andy</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></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="color:black">From:
</span></b><span style="color:black">John
Hendrikx <a
href="mailto:john.hendrikx@gmail.com"
moz-do-not-send="true">
<john.hendrikx@gmail.com></a><br>
<b>Date: </b>Tuesday, September 17, 2024
at 23:05<br>
<b>To: </b>Andy Goryachev <a
href="mailto:andy.goryachev@oracle.com"
moz-do-not-send="true"><andy.goryachev@oracle.com></a>,
<a href="mailto:openjfx-dev@openjdk.org"
moz-do-not-send="true"
class="moz-txt-link-freetext">openjfx-dev@openjdk.org</a>
<a href="mailto:openjfx-dev@openjdk.org"
moz-do-not-send="true">
<openjfx-dev@openjdk.org></a><br>
<b>Subject: </b>Re: [External] : Re:
Proposal: Focus Traversal API</span><o:p></o:p></p>
</div>
<p>Andy,<o:p></o:p></p>
<p>As you're not responding to any of the
suggestions or any of my questions, but are
only re-iterating points that I believe are
not going to be a benefit to the long term
viability of FX, I see no point in continuing
the discussion further.<o:p></o:p></p>
<p>--John<o:p></o:p></p>
<div>
<p class="MsoNormal">On 18/09/2024 01:09, Andy
Goryachev wrote:<o:p></o:p></p>
</div>
<blockquote
style="margin-top:5.0pt;margin-bottom:5.0pt">
<div>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Dear
John:</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">You
do bring a lot of good points, no
doubt. And I do agree with a lot of the
suggestion, but I still want to
emphasize two points:</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">1.
The backward compatibility should not be
dismissed that easily. There is a
number of existing applications out
there and we do not want to break them.
Whether the behavior is specified or not
is irrelevant, we do not want to cause
mayhem from the customers and developers
alike whose keyboard navigation suddenly
changed.</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">2. I
question the cost benefit analysis of
the redesign idea. While I agree with
you that it might help with some unusual
cases, the overall benefit is rather
limited. The benefit of the proposed
solution is, in my opinion, far greater:
it allows for custom traversal policies
(a feature that has been requested
multiple times) and enables focus
traversal from custom components,
something of a lesser value, but still
important. Exposing the existing APIs
is a relatively cheap solution that will
give us two features at nearly zero
cost. On the other hand, I doubt that
our team, or yourself, are willing
commit substantial development effort to
redesign the thing to use events. Which
brings me to the choice I mentioned
earlier: realistically, we have a choice
of providing two requested features
soon, or never.</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">I
would also encourage other members of
the development community to voice their
opinion on the subject, perhaps there is
something else we can do to move
forward.</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Thank
you
</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">-andy</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></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="color:black">From:
</span></b><span
style="color:black">John
Hendrikx <a
href="mailto:john.hendrikx@gmail.com" moz-do-not-send="true">
<john.hendrikx@gmail.com></a><br>
<b>Date: </b>Saturday,
September 14, 2024 at 09:41<br>
<b>To: </b>Andy Goryachev <a
href="mailto:andy.goryachev@oracle.com" moz-do-not-send="true"><andy.goryachev@oracle.com></a>,
<a
href="mailto:openjfx-dev@openjdk.org" moz-do-not-send="true"
class="moz-txt-link-freetext">openjfx-dev@openjdk.org</a>
<a
href="mailto:openjfx-dev@openjdk.org" moz-do-not-send="true">
<openjfx-dev@openjdk.org></a><br>
<b>Subject: </b>[External] :
Re: Proposal: Focus Traversal
API</span><o:p></o:p></p>
</div>
<p>Hi Andy,<o:p></o:p></p>
<p>First let me say that when it comes
to designing an API, you really need
to take the time to think the
solution through. The current
internal solution was probably kept
internal for exactly that reason,
insufficient time to work out the
kinks and look into alternatives.<o:p></o:p></p>
<p>An API is almost impossible to
change later, so the general rule is
that if you're not sure about an
API, then its better to have no
API. This is why I think it is
important that we first look for
what the API should look like, then
worry about how this can be fitted
onto JavaFX. Making concessions
related to the current
implementation before having a clear
idea of how the API should
preferably work is not part of
that. You start making concessions
only when it turns out the preferred
design would encounter unresolvable
problems in the current
implementation.<o:p></o:p></p>
<p>Since I think there is very little
public API related to focus
traversal, nor is there any
specification of how it currently
works, I think we have a lot of room
to maneuver. This is why I think we
should first reach a consensus on
the API, then look how it can be
fitted on top of FX. Sometimes a
well thought out API also is a
natural fit, and may be easier to
migrate to than you think.<o:p></o:p></p>
<div>
<p class="MsoNormal">On 14/09/2024
00:17, Andy Goryachev wrote:<o:p></o:p></p>
</div>
<blockquote
style="margin-top:5.0pt;margin-bottom:5.0pt">
<div>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Dear
John, Everyone:</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Thank
you for a thoughtful
response! Some of the ideas
you described definitely
deserve further
consideration. If I were to
summarize:</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">1.
move the focus traversal logic
away from the components and
into the Scene</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">2.
re-implement focus traversal
through TraversalEvents rather
than responding directly to
KeyEvents</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">3.
(more) standard policies</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">4.
using CSS</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">(there
is of course more topics in
your response, but let me
start with the 4 above)</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">#1
</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">I
generally like this idea. In
some sense it is already how
things work internally, but
without the ability to
customize that (i.e. by
introducing custom traversal
keys, or removing existing
ones). The downside is
substantial: not only we'd
need to re-design the whole of
the focus traversal, but also
rework the existing control's
behaviors. Did I mention the
risk of regression, given the
absence of comprehensive
behavioral tests?</span><o:p></o:p></p>
</div>
</blockquote>
<p>There's two things here.<o:p></o:p></p>
<p>1. There is no need to re-design
the whole focus traversal. The old
internal system can be gradually
replaced (it works by directly
consuming KeyEvents after all).<o:p></o:p></p>
<p>2. Regression. When nothing is
specified, and the fact that
controls **ought** to work like
other common controls in different
UI toolkits, is it a regression when
focus traversal works the same as
those other platforms, even if it
may be a regression from the point
of view of FX? For example, a
Spinner will currently react to any
mouse key, where as other common
toolkits only react to the left
mouse button. Is it a regression if
FX is adjusted to also only react to
the left mouse button? It's not
specified anywhere.<o:p></o:p></p>
<p>I think we have sufficient space to
maneuver here as long as we are not
making focus traversal completely
different from how it commonly works
in UI's.<o:p></o:p></p>
<p class="MsoNormal"
style="margin-bottom:12.0pt">Can
there be regressions versus the
current (unspecified)
implementation? Sure, there can
be. Is that necessarily bad? That
depends. If the new focus traversal
works like it does on all other
toolkits, then no, it is more of a
bug fix. Did we break something
with the new implementation? That's
always possible, but will then be
fixed as soon as it is reported.<o:p></o:p></p>
<blockquote
style="margin-top:5.0pt;margin-bottom:5.0pt">
<div>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">#2</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">This
may or may not be an integral
part of #1. Potentially, it
allows for injection of events
by the application code, as
well as simplifies creation of
complex custom controls. The
latter becomes possible with
the original proposal, so net
benefit is limited to the
first part, I think.</span><o:p></o:p></p>
</div>
</blockquote>
<p class="MsoNormal"
style="margin-bottom:12.0pt">I think
TraversalEvents are quite central to
making this an API that will really
stand the test of time. It
leverages the existing event system,
giving you all the power that comes
with it. You did not answer my
question about the TraversalEvents
in your design. Why are the Events
when they can't be triggered,
filtered or consumed?<o:p></o:p></p>
<blockquote
style="margin-top:5.0pt;margin-bottom:5.0pt">
<div>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">#3</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">One
obvious possibility is to
enable creation of a simple
policy based on a list of
Nodes. I must mention one use
case that is impossible to
cover with pre-defined policy
is one where navigation
depends on some state. Such a
policy must be implemented
programmatically. I think one
property should be sufficient
- I am strongly against adding
two properties here.</span><o:p></o:p></p>
</div>
</blockquote>
<p>Programmatic escapes can always be
achieved by responding directly to a
TraversalEvent. I think however
this should be a rare case, and
standard policies should really
cover almost all use cases. It may
be a gap that should be
investigated, and the API adjusted
for (usually the "exceptions" are
well worth looking into to see if
with a tweak they can't become
"standard"). As for being "strongly
against" having two properties --
that's an odd stance to take without
motivating it. It could also be
rolled into "one" where the Policy
is a record with the two values, but
I think we're getting ahead of
ourselves here. First the API, then
the implementation.<o:p></o:p></p>
<p>I do think however there is great
value in having the Logical and
Directional navigation split. Often
you'll only want to replace one of
these with a custom policy (or a
different standard policy), so that
the other navigation method can be
used to escape the control. For
example, a Toolbar could be tabbed
in an out of (using Logical
navigation) while the Directional
navigation is cyclic (and thus can't
be used to escape the control's
context).<o:p></o:p></p>
<blockquote
style="margin-top:5.0pt;margin-bottom:5.0pt">
<div>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">#4</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">The
idea of using CSS to specify
traversal policy seems wrong
to me: the CSS defines the
presentation aspects (styles)
rather than behavioral ones.
I know it is possible to set
custom skins and the
corresponding behavior via
CSS, and we know why (skins
define the appearance), but we
should not go beyond that, in
my opinion.</span><o:p></o:p></p>
</div>
</blockquote>
<p>I see no problem styling such
properties. They're FX properties,
and it would be very convenient to
style them to globally alter how
focus works, instead of having to
rely on, say, Builders or Factories
for controls where traversal
policies can be applied. There are
also already properties that don't
only influence the look of
controls. "-fx-skin" being the most
obvious one, but there is also
"-fx-focus-traversable",
"-fx-context-menu-enabled",
"-fx-block-increment",
"-fx-unit-increment",
"-fx-pannable", "-fx-initial-delay",
"-fx-repeat-delay",
"-fx-collapsible", "-fx-show-delay",
"-fx-show-duration",
"-fx-hide-delay", and probably
more. Aside from "-fx-skin" none of
these properties have a visual
impact, but instead alter behavior.<o:p></o:p></p>
<p>Note: I'm not saying this needs to
be there immediately. I just want
to make sure we're not closing off
this direction, as again, it would
be a huge hassle to do this
programmatically. In "code" the
only things I usually do on my
controls are the following:<o:p></o:p></p>
<p>- I define the container hierarchy
(VBox, HBox, which children go
where)<br>
- I set a style name<br>
- I set anything that unfortunately
cannot be CSS styled (things like
ALWAYS, SOMETIMES, NEVER grow
policies, Grid sizes, etc, things
that are clearly "visual" but still
can't be styled).<o:p></o:p></p>
<p>All the rest I don't touch, or want
to touch. Having to select a
traversal policy for every control
of type X I create is just
cumbersome and unnecessary. There
will be a call then to set this
"globally", and then there will be
the question, do we make something
custom with many limitations because
it doesn't fit our conceptions of
what (FX) CSS is for (ie, not style,
but only *visual* style) or do we
just expose these properties as
Styleable leveraging an existing
powerful system with almost zero
effort?<o:p></o:p></p>
<blockquote
style="margin-top:5.0pt;margin-bottom:5.0pt">
<div>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">--</span>
<o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">There
is one more aspect of the
problem that I think we should
consider. The current
proposal does not change the
implementation in any material
way, nor does it change the
behavior, thus can be done
quickly. The benefit everyone
gets from it is ability to
trigger focus traversal and to
control it via custom
policies. Any other solution
will require resources and the
bandwidth we currently don't
have, which means the
<i>probability</i> of it being
added to FX is virtually
zero. Let me emphasize, I am
not against attempting to
discuss or implement the best
possible solution, but we
should be aware of the
limitations of the reality we
live in.</span><o:p></o:p></p>
</div>
</blockquote>
<p>"Quickly" and API's are
incompatible with each other. There
is nothing worse than exposing an
API quickly, which then becomes a
burden on the system -- I think the
current CSS API is a prime example
of where "quickly" has gone wrong,
costing us tremendous amounts of
effort to make even minor changes to
it. <o:p></o:p></p>
<p>I urge you to ignore the current
implementation, think thoroughly how
(in an ideal world) you would want
such an API to work (from a user
perspective, not from an
implementor's perspective) and only
then see how this could be made to
fit into JavaFX.<o:p></o:p></p>
<p>This is exactly what I did. I did
not look at the implementation,
although I'm aware of some of it. I
looked at how I as a user of FX am
building applications, the struggles
I have with it currently, (with
controls for example "eating"
KeyEvents), and how I would like to
be able to adjust focus traversal.
Do I want to respond to
"KeyCode.LEFT" or do I want to
respond to "TraversalEvent.LEFT"?
Do I also need to respond to
"KeyCode.NUM_PAD_LEFT"? These
things should be abstracted, and
preferably I should just be able to
choose from common navigation
standards. And when I do want to
change such a standard, in 99% of
the cases that will be the case for
all similar controls in my
application. How do I do such
things currently if I want to change
something for all controls in my
application? I use CSS.<o:p></o:p></p>
<p>Also I think this can be
implemented gradually. Here's a
potential plan:<o:p></o:p></p>
<p>1. Have Scene listen to unused
KeyEvents and translate them to
TraversalEvents<o:p></o:p></p>
<p>Benefit: gives custom controls a
way to respond to keyboard based
navigation in a platform agnostic
way; this probably already removes
the biggest roadblock for custom
controls...<o:p></o:p></p>
<p>Public API: Limited to a new Event<o:p></o:p></p>
<p>2. Start converting existing
controls to listen to TraversalEvent
instead of KeyEvent<br>
<br>
This hits a lot of controls, but
should be relatively easy to do, and
it can be all kept internal for
now. It can be done in a few
batches.<o:p></o:p></p>
<p>Benefit: for each control
converted, user can now
programmatically trigger focus
changes, and by overriding things at
Scene level can completely change
navigation keys<o:p></o:p></p>
<p>Public API: none<o:p></o:p></p>
<p>3. Implement a number of standard
policies internally (OPEN, CONFINED,
CYCLIC, IGNORED)<o:p></o:p></p>
<p>Convert any controls that could use
these as their default, removing any
custom logic if it happens to match
one of the defaults.<o:p></o:p></p>
<p>Benefit: less code to maintain and
debug, gives us experience with
which policies make sense and where
the gaps are<o:p></o:p></p>
<p>Public API: none<o:p></o:p></p>
<p>Order: It is possible to do this
before 2, and so some of the control
conversions could just consist of
removing their custom logic, and
selecting a standard policy.<o:p></o:p></p>
<p>4. Expose policy
property/properties on Parent<o:p></o:p></p>
<p>Any controls that are not using a
custom policy anymore (of type
IGNORED) can now be user adjusted.
We don't have to guarantee that each
policy makes sense for each control.
Changing a default IGNORED policy to
a standard one will change the
behavior (as intended) but it need
not be a "complete" behavior that
users like. This is not FX's
problem, and can be improved upon
later.<o:p></o:p></p>
<p>Benefit: users can now change
policies on any existing control,
even ones with a custom policy; many
of the controls may support a switch
between OPEN, CONFINED and CYCLIC
out of the box.<o:p></o:p></p>
<p class="MsoNormal">Public API: new
properties on Parent<o:p></o:p></p>
<p>5. Perhaps expose some helpful
tools to calculate the "next" Node
for a given traversal option.<br>
<br>
This can be done at any stage, and
can be considered completely
separate. It is IMHO a relatively
low priority need.<br>
<br>
Benefit: less work for control
implementors (although they could
just "copy" said code)<br>
<br>
Public API: Maybe some methods in
Node, or some kind of static helper.<o:p></o:p></p>
<p>6. CSS styleable properties<o:p></o:p></p>
<p>If we really want to give power to
our users, and impress them with a
flexible focus traversal API, then
make these properties styleable.<o:p></o:p></p>
<p>Benefit: allow users to pick any
control, and set is policy globally
or within a subset of controls (ie.
dialogs, popups, etc).<o:p></o:p></p>
<p>Public API: Nothing in Java, but
document as CSS properties<o:p></o:p></p>
<p>--John<o:p></o:p></p>
<blockquote
style="margin-top:5.0pt;margin-bottom:5.0pt">
<div>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Thank
you,</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">-andy</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></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="color:black">From:
</span></b><span
style="color:black">openjfx-dev
<a
href="mailto:openjfx-dev-retn@openjdk.org" moz-do-not-send="true">
<openjfx-dev-retn@openjdk.org></a> on behalf of John Hendrikx <a
href="mailto:john.hendrikx@gmail.com" moz-do-not-send="true">
<john.hendrikx@gmail.com></a><br>
<b>Date: </b>Wednesday,
September 11, 2024 at
19:05<br>
<b>To: </b><a
href="mailto:openjfx-dev@openjdk.org" moz-do-not-send="true"
class="moz-txt-link-freetext">openjfx-dev@openjdk.org</a> <a
href="mailto:openjfx-dev@openjdk.org" moz-do-not-send="true">
<openjfx-dev@openjdk.org></a><br>
<b>Subject: </b>Re:
Proposal: Focus
Traversal API</span><o:p></o:p></p>
</div>
<p>Hi Andy / List,<o:p></o:p></p>
<p>I've given this some
thought first, without
looking too much at the
proposal.<o:p></o:p></p>
<p>In my view, focus
traversal should be
implemented using events,
and FX should provide
standard handling of these
events controlled with
properties (potentially
even CSS stylable for easy
mass changing of the
default navigation
policy).<br>
<br>
## KeyEvent and
TraversalEvent separation<o:p></o:p></p>
<p>I think the cleanest
implementation would be to
implement a KeyEvent
listener on Scene that
takes any unused
KeyEvents, checks if
they're considered
navigation keys, and
converts these keys to a
new type of event, the
TraversalEvent. The
TraversalEvent is then
fired at the original
target. The TraversalEvent
is structured into
Directional and Logical
sub types, and has leaf
types UP/DOWN/LEFT/RIGHT
and NEXT/PREVIOUS. Scene
is the logical place to
handle this as without a
Scene there is no focus
owner, and so there is no
point in doing focus
traversal.<o:p></o:p></p>
<p>This separation of
KeyEvents into
TraversalEvents achieves
the following:<o:p></o:p></p>
<p>- User can decide to act
on **any** key, even
navigation keys, without
the system interfering by
consuming keys early,
unexpectedly or even
consuming these keys
without doing anything
(sometimes keys get
consumed that don't
actually change
focus...). The navigation
keys have many possible
dual purposes, and robbing
the user of the
opportunity to use them
due to an overzealous
component interpreting
them as traversal keys is
very annoying. Dual
purposes include for
example cursor control in
TextField/TextArea,
Scrollbars, etc. The user
should have the same
control here as these FX
controls have.<br>
<br>
- Scene is interpreting
the KeyEvents, and this
interpretation is now
controllable. If I want a
Toolbar (or the whole
application) to react to
WASD navigation keys, then
installing a KeyEvent
handler at Scene level or
at any intermediate Parent
level that converts WASD
to UP/LEFT/DOWN/RIGHT
Traversal events would
affect this change easily.<br>
<br>
- The separation also
allows to block Focus
Traversal only, without
blocking the actual Keys
involved. If I want to
stop a Toolbar from
reacting to LEFT/RIGHT,
but I need those keys
higher up in the
hierarchy, then I'm
screwed. With the
separation, the key events
are unaffected, and I can
block Toolbars from
reacting specifically to
traversal events only.<o:p></o:p></p>
<p>## Traversal Policy
Properties on Parent<br>
<br>
I think FX should provide
several policies out of
the box, based on common
navigation patterns. The
goal here is to have
policies in place that
cover all use cases in
current FX provided
controls. This will
provide a good base that
will cover probably all
realistic work loads that
custom controls may have.
The goal is not to support
every esoteric form of
navigation, instead an
escape hatch will be
provided in the form of
disabling the standard
navigation.<o:p></o:p></p>
<p>In order to achieve this,
I think Parent should get
two new properties, which
control how it will react
to Directional and Logical
navigation. These will
have default values that
allow navigation to flow
from Node to Node within a
Parent and from Parent to
its Parent when navigation
options in a chosen
direction are exhausted
within a Parent. Custom
controls like Combo boxes,
Toolbars, Button groups,
etc, can change the
default provided by a
Parent (similar to how
some controls change the
mouse transparent flag
default).<o:p></o:p></p>
<p>These two properties
should cover all realistic
needs, and IMHO should be
considered to be CSS
stylable in the future to
allow easy changing of
default policies of
controls (ie. want all
Toolbars to react
differently to navigation
keys, then just style the
appropriate property for
all toolbars in one go).<o:p></o:p></p>
<p>Parent will use these
properties to install an
event handler that reacts
to TraversalEvents (not
KeyEvents). This handler
can be fully disabled, or
overridden (using
setOnTraversalEvent).<o:p></o:p></p>
<p>- logicalTraversalPolicy<br>
-
directionalTraversalPolicy<o:p></o:p></p>
<p>The properties can be set
with a value from a
TraversalPolicy enum. I
would suggest the
following options:<o:p></o:p></p>
<p>- OPEN<o:p></o:p></p>
<p>This policy should be the
default policy for all
Parents. It will act and
consume a given
TraversalEvent only when
there is a suitable target
within its hierarchy. If
there is no suitable
target, or the target
would remain unchanged,
the event is NOT consumed
and left to bubble up,
allowing its parent(s) to
act on it instead.<o:p></o:p></p>
<p>- CONFINED<o:p></o:p></p>
<p>This policy consumes all
TraversalEvents,
regardless of whether
there is something to
navigate to or not. This
policy is suitable for
controls that have some
kind of substructure that
we don't want to
accidentally exit with
either Directional or
Logical navigation. In
most cases, you only want
to set one of the
properties to CONFINED as
otherwise there would be
no keyboard supported way
to exit your control.
This is a suitable policy
for say button groups,
toolbars, comboboxes, etc.<o:p></o:p></p>
<p>- CYCLIC<o:p></o:p></p>
<p>Similar to CONFINED but
instead of stopping
navigation at the controls
logical boundaries, the
navigation wraps around to
the logical start. For
example, when were
positioned on the right
most button in a button
group, pressing RIGHT
again would navigate to
the left most button.<br>
<br>
- IGNORED<o:p></o:p></p>
<p>This is similar to the
mouseTransparent property,
and basically leaves the
TraversalEvent to bubble
up. This policy allows
you to completely disable
directional and/or logical
navigation for a control.
Useful if you want to
install your own handler
(the escape hatch) but
still want to keep either
the default directional or
logical navigation.<o:p></o:p></p>
<p>Possible other options
for this enum could
include a version that
consumes all
TraversalEvents (BLOCK)
but I don't see a use for
it at the moment. There
may also be variants of
CONFINED and CYCLIC that
make an exception for
cases where there is only
a single choice
available. A ButtonGroup
for example may want to
react differently
depending on whether it
has 0, 1 or more buttons.
Whether these should be
enshrined with a custom
enum value, or perhaps a
flag, or just left up to a
custom implementation is
something we'd need to
decide still.<o:p></o:p></p>
<p>## Use Cases<o:p></o:p></p>
<p class="MsoNormal">1) User
wants to change the
behavior of a control from
its default to something
else (ie. a Control that
is CYCLIC can be changed
to OPEN or CONFINED)
<o:p></o:p></p>
<p>Just call the setters
with the appropriate
preferred policy. This
could be done in CSS for
maximum convenience to
enable a global change of
all similar controls.<o:p></o:p></p>
<p>2) User wants to act on
Traversal events that the
standard policy leaves to
bubble up<o:p></o:p></p>
<p>Just install a Traversal
event handler either on
the control or on its
parent (depending on their
needs). A potential
action to an unused
Traversal event could be
to close a Dialog/Toast
popup, or a custom
behavior like selecting
the first/last item or
next/previous row (ie. if
I press "RIGHT" and there
is no further right item,
a user could decide to
have this select the first
item again in the current
Row or the first item in
the **next** Row).<o:p></o:p></p>
<p>3) User wants to do crazy
custom navigation<o:p></o:p></p>
<p>Set both policies
to IGNORED, then install
your own event handler (or
use the
setOnTraversalHandler to
completely override the
handler). Now react on
the Traversal events,
consuming them at will and
changing focus to whatever
control you desire.<o:p></o:p></p>
<p>4) User wants to change
what keys are considered
navigation keys<o:p></o:p></p>
<p>Install event handler on
Scene (or any intermediate
Parent) for KeyEvents,
interpret WASD keys as
UP/LEFT/DOWN/RIGHT and
sent out a corresponding
Traversal event<o:p></o:p></p>
<p>5) User wants to use keys
that are considered
navigation keys for their
own purposes<o:p></o:p></p>
<p>Just install a KeyEvent
handler as usual, without
having to worry that
Skins/Controls eat these
events before you can get
to them<br>
<br>
6) User wants to stop a
control from reacting to
traversal events, without
filtering navigation keys
completely<o:p></o:p></p>
<p>With the separation of
unconsumed KeyEvents into
TraversalEvents, a user
can now block only the
latter to achieve this
goal without having to
blanket block certain
KeyEvents.<o:p></o:p></p>
<p>-----<o:p></o:p></p>
<p>About the Proposal:<o:p></o:p></p>
<p>I think the Goals are
fine as stated, although I
think we differ on what
the Traversal events
signify.<o:p></o:p></p>
<p>I think CSS support
should be considered a
possible future goal. The
proposal should therefore
take into account that we
may want to offer this in
the future.<o:p></o:p></p>
<p>Motivation looks okay.<o:p></o:p></p>
<p>> The focus traversal
is provided by the
FocusTraversal class which
offers static methods for
traversing focus in
various directions,
determined by the
TraversalDirection enum.<o:p></o:p></p>
<p>I think these methods
don't need to be exposed
with a good selection of
standard TraversalPolicy
options. After all, there
are only so many ways that
you can do a sensible
navigation action without
confusing the user, and
therefore I think these
policy options will cover
99% of the use cases
already. For the left
over 1% we could
**consider** providing
these focus traversal
functions as a separate
public API, but I would
have them return the Node
they would suggest, and
leave the final decision
to call requestFocus up to
the caller. Initially
however I think there is
already more than enough
power for custom
implementations to listen
to Traversal events and do
their own custom
navigation. If it is not
similar to one of the
standard navigation
options, the
traverseUp/Down functions
won't be of much use then
anyway.<o:p></o:p></p>
<p>About your typical
example:<o:p></o:p></p>
<p> Node from = ...<br>
switch
(((KeyEvent)event).getCode())
{<br>
case UP:<br>
FocusTraversal.traverse(from,
TraversalDirection.UP,
TraversalMethod.KEY);<br>
event.consume();<br>
break;<br>
case DOWN:<br>
// or use the
convenience method<br>
FocusTraversal.traverseDown(from);<br>
event.consume();<br>
break;<br>
}<o:p></o:p></p>
<p>I think this is not a
good way to deal with
events.<o:p></o:p></p>
<p>1) The event is consumed
regardless of the outcome
of traverse. What if
focus did not change?
Should the event be
consumed?<o:p></o:p></p>
<p>2) This is consuming
KeyEvents directly,
robbing the user of the
opportunity to act on keys
considered "special" by
FX.<o:p></o:p></p>
<p>3) This code is not only
consuming KeyEvents
directly, but also
deciding what keys are
navigation keys.<o:p></o:p></p>
<p>So I think this
example code should be
different. However, first
I expect that in most
cases, configuring a
different traversal policy
on your Parent subclass
will already be sufficient
in almost all cases
(especially if we look at
FX current controls and
see if the suggested
policies would cover those
use cases). So this code
will almost never be
needed. However, in the
event that you need
something even more
specific, you may consider
handling Traversal events
directly. In which case
the code should IMHO look
something like this:<o:p></o:p></p>
<p> Node from = ...<br>
<br>
Node result =
switch(traversalEvent.getEventType())
{<br>
case
TraversalEvent.UP ->
FocusTraversals.findUp(from);<br>
case
TraversalEvent.DOWN ->
FocusTraversals.findDown(from);<br>
// etc<br>
}<br>
<br>
if (result != null) {<br>
result.requestFocus();<br>
traversalEvent.consume();<br>
}<o:p></o:p></p>
<p>Note that the above code
leaves the final decision
to call requestFocus up to
the caller. It also
allows the caller to
distinguish between the
case where there is no
suitable Node in the
indicated direction and
act accordingly. <o:p></o:p></p>
<p>This allows it to NOT
consume the event if it
prefers its Parent to
handle it (if the control
doesn't want CYCLIC or
CONFINED style
navigation). It also
allows it to further
scrutinize the suggested
Node, and if it decides it
does not like it (due to
some property or CSS style
or whatever) it may follow
up with another findXXX
call or some other option
to pick the Node it
wants. It also allows (in
the case of no Node being
found) to pick its own
preferred Node in those
cases. In other words, it
is just far more flexible.<o:p></o:p></p>
<p>I'm not sure yet where to
place these static helper
methods (if we decide to
expose them at all
initially), or even if
they should be static.
Given that its first
parameter is always a
Node, a non-static
location for them could
simply be on Node itself,
in which case the calling
convention would become
"Node result =
from.findTraversableUp()"
(suggested name only)<o:p></o:p></p>
<p>> Focus traversals
generate a new type of
event, encapsulated by the
class TraversalEvent which
extends
javafx.event.Event, using
the event type
TraversalEvent.NODE_TRAVERSED.<o:p></o:p></p>
<p>What is the point of this
event? If you want to
know that focus changed,
you can add a listener to
Scene.focusOwnerProperty.
What does it mean if I
filter this event? What
if I consume it? I don't
think this should be an
event at all, unless
implemented as I suggested
above, where
consuming/filtering/bubbling
can be used to control how
controls will react to
navigation events.<o:p></o:p></p>
<p>--John<o:p></o:p></p>
<p> <o:p></o:p></p>
<p> <o:p></o:p></p>
<p class="MsoNormal"
style="margin-bottom:12.0pt">On 03/09/2024 21:33, Andy Goryachev wrote:<o:p></o:p></p>
<blockquote
style="margin-top:5.0pt;margin-bottom:5.0pt">
<div>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121">Dear
fellow developers:</span><o:p></o:p></p>
<p class="MsoNormal"
style="font-variant-caps:normal;orphans:auto;text-align:start;widows:auto;word-spacing:0px">
<span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121"> </span><o:p></o:p></p>
<p class="MsoNormal"
style="font-variant-caps:normal;orphans:auto;text-align:start;widows:auto;word-spacing:0px">
<span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121">I'd
like to propose the
public focus
traversal API:</span><o:p></o:p></p>
<p class="MsoNormal"
style="font-variant-caps:normal;orphans:auto;text-align:start;widows:auto;word-spacing:0px">
<span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121"> </span><o:p></o:p></p>
<p class="p1"
style="margin:0in;font-variant-caps:normal;orphans:auto;text-align:start;widows:auto;word-spacing:0px">
<span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121"><a
href="https://urldefense.com/v3/__https:/github.com/andy-goryachev-oracle/Test/blob/main/doc/FocusTraversal/FocusTraversal.md__;!!ACWV5N9M2RV99hQ!LnjDXwUbbEymf9b1gkZFia8vuewsVJy6_49It-IKw66U9mS78PjdIPotBpc7AXlSfY7N5xcRXsmcPQhOzavk4z9VkPv-$"
title="https://github.com/andy-goryachev-oracle/Test/blob/main/doc/FocusTraversal/FocusTraversal.md"
moz-do-not-send="true"><span style="color:#0078D7">https://github.com/andy-goryachev-oracle/Test/blob/main/doc/<span
class="outlook-search-highlight">Focus</span>Traversal/<span
class="outlook-search-highlight">Focus</span>Traversal.md</span></a></span><o:p></o:p></p>
<p class="MsoNormal"
style="font-variant-caps:normal;orphans:auto;text-align:start;widows:auto;word-spacing:0px">
<span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121"> </span><o:p></o:p></p>
<p class="MsoNormal"
style="font-variant-caps:normal;orphans:auto;text-align:start;widows:auto;word-spacing:0px">
<span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121">Draft
PR:</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121"> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121"><a
href="https://urldefense.com/v3/__https:/github.com/openjdk/jfx/pull/1555__;!!ACWV5N9M2RV99hQ!LnjDXwUbbEymf9b1gkZFia8vuewsVJy6_49It-IKw66U9mS78PjdIPotBpc7AXlSfY7N5xcRXsmcPQhOzavk49fH_P2p$"
moz-do-not-send="true">https://github.com/openjdk/jfx/pull/1555</a></span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121"> </span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121">Your
comments and
suggestions will be
warmly accepted and
appreciated.</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121"> </span><o:p></o:p></p>
<p class="MsoNormal"
style="font-variant-caps:normal;orphans:auto;text-align:start;widows:auto;word-spacing:0px">
<span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121">Thank
you</span><o:p></o:p></p>
<p class="MsoNormal"
style="font-variant-caps:normal;orphans:auto;text-align:start;widows:auto;word-spacing:0px">
<span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121"> </span><o:p></o:p></p>
<p class="MsoNormal"
style="font-variant-caps:normal;orphans:auto;text-align:start;widows:auto;word-spacing:0px">
<span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16";color:#212121">-andy</span><o:p></o:p></p>
<p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""> </span><o:p></o:p></p>
</div>
</blockquote>
</div>
</div>
</div>
</div>
</blockquote>
</div>
</div>
</div>
</div>
</blockquote>
</div>
</div>
</div>
</div>
</blockquote>
</div>
</div>
</div>
</div>
</blockquote>
</body>
</html>