<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>I'm working on a new version of the behavior proposal after some
      fruitful discussions here on this list, and a meeting with Andy.</p>
    <p>In this meeting a few problems I was unable to address came to
      light, and which I've now addressed in this post:</p>
    <p>1) TextAreaBehavior is relying heavily on internal functions
      provided by the corresponding Skin. These are all related to how
      Text is visually laid out and actions that you can take that would
      need this layout information of which the Control is unaware; the
      simplest example is pressing the "HOME" or "END" key which needs
      to move to the start or end of current line depending on its
      visual bounds.</p>
    <p>2) Having Behaviors as a separate concept didn't seem all that
      useful.<br>
      <br>
      3) The potential for having a HUGE number of semantic events in
      Skin -> Behavior communication<br>
    </p>
    <p>TextAreaBehavior <br>
      ==============</p>
    <p>I think its fair to say that this is definitely one of the more
      unique behaviors in JavaFX.  Together with its Skin it is doing
      things that I don't think any of the other skins do.  The Skin for
      example provides styleable CSS properties, and provides a lot of
      call backs for the behavior.  This latter part I've not seen in
      any other skins (I did a quick search).  I didn't investigate if
      any other Skins are also providing CSS stylable properties.</p>
    <p>Now I think this can mean that perhaps the TextArea control is
      lacking some functions that it should be providing. TextArea is
      aware that its content will be wrapped and split into lines, that
      it has a view port, and provides ways to move the caret.  What it
      doesn't provide is more precise caret control.  It doesn't seem
      like a stretch to also provide for functions that can navigate the
      caret to the start or end of the current line, or to the start or
      end of the current visible page, etc.</p>
    <p>Lacking that, we could also accept that perhaps a Behavior should
      have some more awareness of a related Skin.  In MVC, the
      Controller (Behavior) has a reference to the View (Skin) and also
      has some knowledge of how the View operates. A solution here can
      be to have Skins with very specific needs implement an interface. 
      The Behavior can then be attuned to that interface, which would
      allow a new Skin to be constructed which reuses a lot of an
      existing Behavior without the requirement to subclass a specific
      Skin.  Note that the Behavior can fairly easily access the Skin
      already through Control.  A simple instanceof check will suffice
      to see if it can expand its behavior to support visual operations.</p>
    <p>    void moveCaretToEndOfLine(TextArea control) {  // called in
      response to a KeyEvent<br>
                if (control.getSkinnable() instanceof
      VisuallyCaretAwareInterface x) {<br>
                     x.moveCaret(...);<br>
                }<br>
                // do nothing, wanted behavior not supported by Skin<br>
          }<br>
      <br>
      Usefulness of Behavior Concept<br>
      ==========================<br>
    </p>
    <p>The point was made that creating a new Behavior is still very
      limited in what it allows the user to change how a control
      "feels".  This is because the Skin does a lot of filtering when it
      is passing things to the associated behavior.  For example, the
      SpinnerSkin will call the behavior when the spinner buttons are
      pressed (with any mouse button(!!!)), but the Behavior can't limit
      this to just the left mouse button (like Spinners on Windows do).
      Other Skins will only call the Behavior for specific mouse
      buttons, or for specific events.  In effect, the Skin is already
      dictating part of the behavior (either by omission or by
      generalization) which should be part of the Behavior
      impementation.</p>
    <p>For example, if I want to create a SpinnerBehavior that would
      react to the scroll wheel when it is hovering above the Spinner's
      text field, I'm out of luck; the Skin is not calling the Behavior
      for SCROLL events.<br>
    </p>
    <p>So perhaps we can leverage something here that is already public
      knowledge: the Substructure of Controls. Spinner for example has
      the substructure:</p>
    <ul style="color: rgb(0, 0, 0); font-family: Helvetica, Arial,
      sans-serif; font-size: 14px; font-style: normal;
      font-variant-ligatures: normal; font-variant-caps: normal;
      font-weight: 400; letter-spacing: normal; orphans: 2; text-align:
      start; text-indent: 0px; text-transform: none; widows: 2;
      word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space:
      normal; text-decoration-thickness: initial; text-decoration-style:
      initial; text-decoration-color: initial;">
      <li>text-field — TextField</li>
      <li>increment-arrow-button — StackPane
        <ul>
          <li>increment-arrow — Region</li>
        </ul>
      </li>
      <li>decrement-arrow-button — StackPane
        <ul>
          <li>decrement-arrow — Region</li>
        </ul>
      </li>
    </ul>
    <p>The names here are part of the CSS public API and indicate
      interesting parts of the spinner control.</p>
    <p>What if we completely forbid the Skin from installing event
      handlers, even on its children (or at least the ones that are part
      of the substructure), and let those events bubble up for Behavior
      to catch?  An immediate problem would be how the Behavior would
      know whether I pressed the mouse button on the UP or DOWN button,
      or if I pressed the button on the Text Field portion.  We could
      look at the event's target, but even though you can see which
      specific Node was targetted, the Behavior doesn't know what that
      node means.  So, instead we could ask the Skin this (or
      alternatively, put this in a fixed Key on the Node in its
      mappings, or use the CSS id field for this purpose):</p>
    <p>     interface Skin<T> {<br>
                default String
      determineSubstructureElement(Event<?> event) { return null;
      }<br>
           }</p>
    The Skin will reply here with the publically known style class name
    that it assigns to its substructure elements, or `null` if it
    doesn't support this feature or if the Event passed in doesn't match
    to any (public) substructure element or if the Event just didn't
    belong to this Skins substructure at all.
    <p>The Behavior can then use this "name" or "annotation" as an extra
      piece of information to determine the desired action:</p>
    <p>     void mousePressed(MouseEvent event, Spinner control) {<br>
                 if (event.getButton() == MouseButton.PRIMARY) {<br>
                     control.requestFocus();<br>
      <br>
                     switch
      (control.getSkinnable().determineSubstructureElement(event)) {<br>
                          case "increment-arrow-button" ->
      startSpinningUp();<br>
                          case "increment-arrow" ->
      startSpinningUp();<br>
                          case "decrement-arrow-button" ->
      startSpinningDown();<br>
                          case "decrement-arrow" ->
      startSpinningDown();<br>
                     }<br>
                }<br>
           }<br>
    </p>
    <p>In this way Behaviors can support many more things than the Skin
      initially thought of providing.  You could have the Spinner
      buttons only react on LMB, have the text field respond to SCROLL
      events, have the spinner buttons react differently on long or
      short presses, on drags or gestures, etc...</p>
    <p>Note, the above suggested API fpr determineSubstructureElement
      returns a String with the element name; it could also be a simple
      object that is aware of the nested substructure, so that it can be
      queried "did this Event involve the 'increment-arrow-button' or
      any of its children?" more easily.<br>
    </p>
    <p>Semantic Events<br>
      ==============<br>
    </p>
    <p>Note that the above change sort of eliminates the need for
      Semantic Events between Skins and Behavior, which I think will
      severely cut down on how many Semantic Events will be needed.  As
      the events are now limited to specifying the communication needed
      between Behavior and Control (or only an indirection between
      Behavior and itself), they can be more directly related to state
      changes.  Instead of having 4 events for
      START/STOP_SPINNING_UP/DOWN, we could just have a
      SPINNER_VALUE_CHANGE event which is parameterized.</p>
    <p>The value in still having the semantic events lies in:</p>
    <p>- Being able to indirect several low-level sets of events to a
      single high-level action, allowing the high-level function to be
      remapped or accessed directly<br>
      - Being able to intercept and block events using the normal event
      system before they're acted upon, or even to generate a high level
      event directly</p>
    <p>Who does what?<br>
      ================</p>
    <p>This changed a little bit from my earlier definitions, but I'll
      adjust them here:</p>
    # Controls (no changes)<br>
    <p>- Controls refer to a Skin and Behavior<br>
      - Controls define semantic events<br>
      - Controls can generate semantic events (via mappings)<br>
      - Controls never modify their own (user writable) state on their
      own accord (the user is in full control)<br>
      - Controls provide an override based key mapping system</p>
    <p># Skins (stop acting on events)<br>
      <br>
      - Skins refer to a Control (legacy inheritance) but are limited in
      their allowed interactions (unwritten rule since the beginning)<br>
      - Skins never interprete any events, not even on their children,
      with perhaps the one exception if the handling of the event only
      changes Skin internal state<br>
      - Skins never act upon semantic events<br>
      - Skins provide information about their substructure, either on
      the Node or when asked<br>
    </p>
    <p># Behaviors (also generates semantic events, and reacts on more
      events)<br>
    </p>
    <p>- Behaviors refer to nothing<br>
          - Control (and indirectly Skin) reference is received via
      event handler and listeners only<br>
          - Skins can be checked to see if they support special behavior
      via an Interface<br>
      - Behaviors define base key mappings<br>
          - Controls refer to these, after first checking for any user
      overrides<br>
          - Base key mappings can be global and immutable, user
      overrides (maintained in Control) are mutable<br>
      - Behaviors generate semantic events in response to (combinations
      of) low-level events (or via timers)<br>
      - Behaviors can act upon events of any type (if unconsumed by the
      user) that are targetted at (or bubble up to) the control
      (enforced as that's the only place they can install handlers)<br>
      - Behaviors are allowed to modify control state and their own
      state</p>
    <p>The above definitions seperate the Look & Feel part even
      better than my earlier proposal, and makes Behaviors far more
      powerful than before at only a slight cost to Skins.  Skins now
      truly only provide visuals, and never interprete events, while
      Behaviors have more options to determine what events they are
      interested in and how to respond to them.</p>
    <p>The balance between the amount of code in Skins and Behaviors
      will shift a bit.  More event handling will move to Behaviors
      (where it actually belongs), which may go a long way to also
      making Skins easier to subclass or reimplement.<br>
    </p>
    <p>--John<br>
    </p>
    <p><br>
    </p>
    <p><br>
    </p>
    <p><br>
    </p>
    <p><br>
    </p>
    <p><br>
    </p>
    <p><br>
    </p>
  </body>
</html>