<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Hi Andy,<br>
    </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.</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.</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.<br>
    </p>
    <div class="moz-cite-prefix">On 14/09/2024 00:17, Andy Goryachev
      wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:BL3PR10MB61852ADD6DF9BEB30CE40D78E5652@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.EmailStyle22
        {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, Everyone:<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"">Thank
            you for a thoughtful response!  Some of the ideas you
            described definitely deserve further consideration.  If I
            were to summarize:<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"">1.
            move the focus traversal logic away from the components and
            into the Scene<o:p></o:p></span></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<o:p></o:p></span></p>
        <p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">3.
            (more) standard policies<o:p></o:p></span></p>
        <p class="MsoNormal"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">4.
            using CSS<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"">(there
            is of course more topics in your response, but let me start
            with the 4 above)<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"">#1
            <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"">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></p>
      </div>
    </blockquote>
    <p>There's two things here.</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).<br>
    </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.</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.<br>
    </p>
    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.<br>
    <blockquote type="cite"
cite="mid:BL3PR10MB61852ADD6DF9BEB30CE40D78E5652@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"">#2<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"">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></p>
      </div>
    </blockquote>
    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?<br>
    <blockquote type="cite"
cite="mid:BL3PR10MB61852ADD6DF9BEB30CE40D78E5652@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"">#3<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"">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></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.</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).<br>
    </p>
    <blockquote type="cite"
cite="mid:BL3PR10MB61852ADD6DF9BEB30CE40D78E5652@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"">#4<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"">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></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.</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:</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).</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?<br>
    </p>
    <blockquote type="cite"
cite="mid:BL3PR10MB61852ADD6DF9BEB30CE40D78E5652@BL3PR10MB6185.namprd10.prod.outlook.com">
      <div class="WordSection1"><span
style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">--<o:p></o:p></span>
        <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"">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></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.  </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.</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.<br>
    </p>
    <p>Also I think this can be implemented gradually.  Here's a
      potential plan:<br>
    </p>
    <p>1. Have Scene listen to unused KeyEvents and translate them to
      TraversalEvents<br>
    </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...<br>
    </p>
    <p>Public API: Limited to a new Event<br>
    </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.</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<br>
    </p>
    <p>Public API: none<br>
    </p>
    <p>3. Implement a number of standard policies internally (OPEN,
      CONFINED, CYCLIC, IGNORED)</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.<br>
    </p>
    <p>Benefit: less code to maintain and debug, gives us experience
      with which policies make sense and where the gaps are<br>
    </p>
    <p>Public API: none</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.<br>
    </p>
    <p>4. Expose policy property/properties on Parent</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.<br>
    </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.<br>
    </p>
    Public API: new properties on Parent<br>
    <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.</p>
    <p>6. CSS styleable properties</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.</p>
    <p>Benefit: allow users to pick any control, and set is policy
      globally or within a subset of controls (ie. dialogs, popups,
      etc).</p>
    <p>Public API: Nothing in Java, but document as CSS properties<br>
    </p>
    <p>--John</p>
    <blockquote type="cite"
cite="mid:BL3PR10MB61852ADD6DF9BEB30CE40D78E5652@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"">Thank
            you,<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">openjfx-dev
                    <a class="moz-txt-link-rfc2396E" href="mailto:openjfx-dev-retn@openjdk.org"><openjfx-dev-retn@openjdk.org></a> on behalf of
                    John Hendrikx <a class="moz-txt-link-rfc2396E" href="mailto:john.hendrikx@gmail.com"><john.hendrikx@gmail.com></a><br>
                    <b>Date: </b>Wednesday, September 11, 2024 at 19:05<br>
                    <b>To: </b><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: Proposal: Focus Traversal API<o:p></o:p></span></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">On 03/09/2024 21:33, Andy Goryachev
                wrote:<br>
                <br>
                <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://github.com/andy-goryachev-oracle/Test/blob/main/doc/FocusTraversal/FocusTraversal.md"
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://github.com/openjdk/jfx/pull/1555"
                        moz-do-not-send="true"
                        class="moz-txt-link-freetext">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>
  </body>
</html>