<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Hi Michael,</p>
    <p>What you describe in the generalization sounds excellent, and
      would go a step further and avoids the registration mechanism by
      instead doing multiple dispatch/bubbling phases.  The registration
      mechanism is more a system to avoid doing that multiple times, but
      it could also be enforced based on priority.  The way that would
      work is to leave PREFERRED handlers alone, but wrap any DEFAULT
      handlers to register themselves in a default list and any SYSTEM
      handlers in a system list.  This way dispatch/bubbling occurs
      once, and after it finishes it is immediately known what default
      or system handlers may still need calling (if any).<br>
    </p>
    <p>So just to clarify further how the registration mechanism would
      work (if not clear already now):<br>
    </p>
    <p>It's not exactly a 2nd bubbling phase (although I suppose it
      could be), the event already bubbles (or dispatches) past all
      possible event handlers, but some handlers opt for not acting on
      the event immediately.  Instead they just register their interest
      in being called if bubbling completes without the event being
      consumed.  This 3rd phase therefore only calls the registered
      handlers.  <br>
    </p>
    <p>In other words, a Behavior could do:  <br>
    </p>
    <p>    addEventHandler(KeyEvent.KEY_PRESSED, e -> {<br>
                e.registerDefaultHandler(this::keyPressed);<br>
                // don't consume event; the above should be pretty low
      overhead, same lambda is registered each time<br>
          });<br>
    </p>
    <p>Instead of its more usual:<br>
      <br>
           addEventHandler(KeyEvent.KEY_PRESSED, this::keyPressed);</p>
    <p>Internally, the registerDefaultHander call would probably need to
      keep track of the original event (so the source/target are all
      correct); it's code would be something like:<br>
      <br>
             record DefaultRegistration(Event event, EventHandler
      eventHandler);</p>
    <p>       void registerDefaultHandler(EventHandler<? super T>
      handler) {<br>
                  defaultRegistrations.add(new DefaultRegistration(this,
      handler));<br>
             }<br>
      <br>
      And calling them all at the end would just be something like:<br>
      <br>
            void callDefaultHandlers() {<br>
                if (isConsumed()) return;<br>
      <br>
                for (DefaultRegistrationregistration :
      defaultRegistrations) {<br>
                     
      registration.eventHandler().handle(registration.event());<br>
      <br>
                      if (registration.event().isConsumed()) break;<br>
                }<br>
            }<br>
    </p>
    <p>----<br>
    </p>
    <p></p>
    <p>If I can draw a comparison to something I've done in my own code;
      I had a need for the various components in the scene graph to
      contribute options to a global context menu.  I solved this by
      registering an event handler for a context menu event; each
      interested control or layer could register on this event (the
      event is a subclass of Event with extra capabilties) that they
      have context menu items to provide.  At the root level the event
      is then examined (by in this case another event handler installed
      at the root), and all the interested parties were called to
      provide their items and a single context menu was formed; some
      code:</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:#0000a0;font-weight:bold;">public</span><span style="color:#000000;"> </span><span style="color:#0000a0;font-weight:bold;">class</span><span style="color:#000000;"> PresentationEvent </span><span style="color:#0000a0;font-weight:bold;">extends</span><span style="color:#000000;"> Event {</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;font-weight:bold;">final</span><span style="color:#000000;"> EventType<PresentationEvent> </span><span style="color:#0000c0;">ANY</span><span style="color:#000000;"> = </span><span style="color:#0000a0;font-weight:bold;">new</span><span style="color:#000000;"> EventType<>(EventType.</span><span style="color:#0000c0;">ROOT</span><span style="color:#000000;">, </span><span style="color:#2a00ff;">"PRESENTATION"</span><span style="color:#000000;">);</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;font-weight:bold;">final</span><span style="color:#000000;"> EventType<PresentationEvent> </span><span style="color:#0000c0;">CONTEXT_MENU</span><span style="color:#000000;"> = </span><span style="color:#0000a0;font-weight:bold;">new</span><span style="color:#000000;"> EventType<>(</span><span style="color:#0000c0;">ANY</span><span style="color:#000000;">, </span><span style="color:#2a00ff;">"PRESENTATION_CONTEXT_MENU"</span><span style="color:#000000;">);</span></p><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;font-weight:bold;">final</span><span style="color:#000000;"> EventType<PresentationEvent> </span><span style="color:#0000c0;">REFRESH</span><span style="color:#000000;"> = </span><span style="color:#0000a0;font-weight:bold;">new</span><span style="color:#000000;"> EventType<>(</span><span style="color:#0000c0;">ANY</span><span style="color:#000000;">, </span><span style="color:#2a00ff;">"PRESENTATION_REFRESH"</span><span style="color:#000000;">);</span><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;">private</span><span style="color:#000000;"> </span><span style="color:#0000a0;font-weight:bold;">final</span><span style="color:#000000;"> List<Presentation> </span><span style="color:#0000c0;">presentations</span><span style="color:#000000;"> = </span><span style="color:#0000a0;font-weight:bold;">new</span><span style="color:#000000;"> ArrayList<>();</span></p><p style="margin:0;"><span style="color:#000000;">    </span></p><span style="color:#000000;">      </span><span style="color:#0000a0;font-weight:bold;">public void</span><span style="color:#000000;"> addPresentation(Presentation presentation) {</span><p style="margin:0;"><span style="color:#000000;">        </span><span style="color:#0000a0;font-weight:bold;">this</span><span style="color:#000000;">.</span><span style="color:#0000c0;">presentations</span><span style="color:#000000;">.add(presentation);</span></p><p style="margin:0;"><span style="color:#000000;">      }</span></p><span style="color:#000000;">
</span></div>
      <div style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:11pt;white-space:pre;"><span style="color:#000000;">      </span><span style="color:#0000a0;font-weight:bold;">public</span><span style="color:#000000;"> List<Presentation> getPresentations() {</span><p style="margin:0;"><span style="color:#000000;">        </span><span style="color:#7f0055;font-weight:bold;">return</span><span style="color:#000000;"> </span><span style="color:#0000a0;font-weight:bold;">new</span><span style="color:#000000;"> ArrayList<>(</span><span style="color:#0000c0;">presentations</span><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></div>
    </div>
    <div class="moz-cite-prefix">Each presentation would add itself as a
      filter in this case:</div>
    <div class="moz-cite-prefix"><br>
    </div>
    <div class="moz-cite-prefix">   
      addEventFilter(PresentationEvent.ANY, e ->
      e.addPresentation(this))</div>
    <div class="moz-cite-prefix"><br>
    </div>
    <div class="moz-cite-prefix">--John<br>
    </div>
    <div class="moz-cite-prefix"><br>
    </div>
    <div class="moz-cite-prefix">On 29/10/2023 05:20, Michael Strauß
      wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:CAJEpuXSM9DONOjWvUHKSUHwDKastV6R8NWDXPesV-qV=0ThfvA@mail.gmail.com">
      <pre class="moz-quote-pre" wrap="">Hi John,

from what I understand, you're essentially suggesting a third phase of
event dispatch:

1. The event starts at the root and travels to the event target
(capturing phase)
2. If the event is not consumed, it travels back to the root, but
default handlers are excluded (bubbling phase)
3. If the event is still not consumed, the bubbling phase is repeated
for default handlers.

Maybe a generalization of that would be the following:

Both the capturing phase and the bubbling phase are repeated for each
priority level, beginning with the highest priority. In each
iteration, the event is only received by handlers with the
corresponding priority. If the event is consumed, it is not propagated
further.

Installing a handler with higher priority therefore has the effect of
"hiding" all handlers with lower priority for the iteration that
corresponds to the handler's priority.

"Blocking default processing" could be achieved by marking the event
as "skip for any lower priority level". This would complete the event
dispatch for the current priority, and then stop.
</pre>
    </blockquote>
  </body>
</html>