<!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>