<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<p>My apologies then, I was a bit impatient. Good luck with the
test sprint then!<br>
</p>
<p>--John<br>
</p>
<div class="moz-cite-prefix">On 18/09/2024 17:13, Andy Goryachev
wrote:<br>
</div>
<blockquote type="cite"
cite="mid:BL3PR10MB61853160A21E2EC5F535B0B2E5622@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:"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.EmailStyle230
{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;}</style>
<div class="WordSection1">
<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.<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>
<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>Tuesday, September 17, 2024 at 23:05<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>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>
</body>
</html>