Hi Michael,

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).

So just to clarify further how the registration mechanism would work (if not clear already now):

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.

In other words, a Behavior could do:

    addEventHandler(KeyEvent.KEY_PRESSED, e -> {
          e.registerDefaultHandler(this::keyPressed);
          // don't consume event; the above should be pretty low overhead, same lambda is registered each time
    });

Instead of its more usual:

     addEventHandler(KeyEvent.KEY_PRESSED, this::keyPressed);

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:

       record DefaultRegistration(Event event, EventHandler eventHandler);

       void registerDefaultHandler(EventHandler<? super T> handler) {
            defaultRegistrations.add(new DefaultRegistration(this, handler));
       }

And calling them all at the end would just be something like:

      void callDefaultHandlers() {
          if (isConsumed()) return;

          for (DefaultRegistrationregistration : defaultRegistrations) {
registration.eventHandler().handle(registration.event());

                if (registration.event().isConsumed()) break;
          }
      }

----

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:

publicclassPresentationEvent extendsEvent {

publicstaticfinalEventType<PresentationEvent> ANY= newEventType<>(EventType.ROOT, "PRESENTATION");

publicstaticfinalEventType<PresentationEvent> CONTEXT_MENU= newEventType<>(ANY, "PRESENTATION_CONTEXT_MENU");

publicstaticfinalEventType<PresentationEvent> REFRESH= newEventType<>(ANY, "PRESENTATION_REFRESH");

privatefinalList<Presentation> presentations= newArrayList<>();

public voidaddPresentation(Presentation presentation) {

this.presentations.add(presentation);

}

publicList<Presentation> getPresentations() {

returnnewArrayList<>(presentations);

}

}

Each presentation would add itself as a filter in this case:

addEventFilter(PresentationEvent.ANY, e -> e.addPresentation(this))

--John

On 29/10/2023 05:20, Michael Strauß wrote:
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.

Reply via email to