I kind of share Martin's sentiment.

In my opinion, there are two general problems with the FX event dispatching:

1. general lack of predictability in the order of event handling which becomes 
a problem when skin gets replaced in Control
2. Event.isConsumed() debacle and the possibility of creating a malformed 
dispatcher by the application, something that should not be possible

For #1, Michael tried adding event handling priorities, and I suggested to use 
the InputMap specifically for Controls (Andy), both of which did not get 
traction for various reasons.

In Swing, this issue can easily be solved via removing and re-registering 
listeners at the right order via public API, if I remember correctly.  We can't 
use that in FX because the listeners are not accessible to the application code.

To me, either solution is fine (of course, I favor the InputMap one because it 
limits the number of effective priorities to a minimum required for the skins 
to function, since the application code can always add a listener at higher 
priority, and because the application code cannot use priorities given to 
skins, while at the same time allowing for custom skins).

The main reason I dislike the idea of "default event handlers" is that it's a 
public API that can be misused by the application code and break the skins (if 
I read it correctly).

The reason I mentioned #2 is that it is somewhat relevant to the discussion, as 
in "why do we need to write custom dispatchers at all?"  There should be only 
two methods, in my opinion, one that dispatches an event that bubbles up (with 
filters and handlers), and one that sends an event to a single target Node and 
nothing else.  <rant>Somehow, Swing got the Events right - it manages to 
dispatch one (1) event in total, and the dispatching stops once the event is 
consumed.  The FX decided it needed to reinvent the wheel and leave multiple 
booby traps in the process.</rant>

This isn't exactly rocket science, we should be able to figure something out.  
Maybe there is another option that will satisfy everyone?

What do you think?

-andy



From: Martin Fox <[email protected]>
Date: Friday, January 9, 2026 at 10:36
To: Michael Strauß <[email protected]>
Cc: OpenJFX <[email protected]>, Andy Goryachev 
<[email protected]>
Subject: [External] : Re: Default event handlers

Default handlers might make sense for Control (it’s reasonable to assume that a 
behavior or skin may install handlers) but the rest of the system avoids using 
handlers and instead relies on custom dispatchers. It seems wrong to add calls 
for registering default handlers on Scene and Window when they make a point of 
not using handlers to begin with. Could we restrict this to Control? Or at the 
very least Node?

If this was restricted to Control would we still make the call for registering 
a default handler public? For existing controls outside developers should only 
register primary handlers.

The documentation for Event.preventDefault() doesn't mention what happens if 
it's called in a filter.

Event.preventDefault() is only useful if a primary handler or filter wants to 
skip the default without consuming the event. This sounds reasonable but I 
can’t come up with a specific use case. Could you provide an example?

It’s unfortunate that the only way for a handler to send information to a 
dispatcher is to attach it as state on the event (and it’s unfortunate that 
there's no good way to keep that state from bleeding over to another 
dispatcher). If we’re going through the trouble we should consider generalizing 
this and opening it up. For example, we could allow a handler or filter to 
attach an arbitrary object to the event for the dispatcher to retrieve. It’s 
worth thinking about (if only for a moment). On the flip side preventDefault() 
is well-defined and based on a web standard so it makes sense to add this bit 
even it’s only used by Control or Node.

Martin

On Jan 5, 2026, at 8:46 AM, Andy Goryachev <[email protected]> wrote:

Michael:

Does the new approach fix https://bugs.openjdk.org/browse/JDK-8231245 ?

Thanks,
-andy



From: openjfx-dev <[email protected]> on behalf of Michael Strauß 
<[email protected]>
Date: Thursday, December 25, 2025 at 02:49
To: openjfx-dev <[email protected]>
Subject: Re: Default event handlers

I've run into some problems while trying to make event handlers on
skins only act when the event isn't consumed by user code. Consider a
button that is nested in some hierarchy:

scene
--> root
----> button

When the button receives a MOUSE_RELEASED event, it consumes the event
and fires off an ACTION event. Doing that in a default handler is
fine, but as as consequence, the MOUSE_RELEASED event won't be
immediately consumed. Instead, it bubbles up the entire hierarchy
before it is finally handled and consumed by the button in its default
handler after the dispatch chain has completed. This is clearly not
what we want, as it can potentially cause ancestors to act on the
event, too. (By the way, this also rules out an event system where
events are dispatched in prioritized capture/bubble phases.)

I think what we need is quite a bit simpler. Default event handlers
are still the way to go, but they shouldn't act across the entire
dispatch chain. Instead, they should only act locally on a single
event target: when a target receives an event, it first invokes its
regular handlers. Then, if the event is still eligible for default
handling, the default handlers are invoked. Skins and behaviors should
always use default handlers, never regular handlers.

In the example, the button would consume the MOUSE_RELEASED event in
its default handler, which will prevent it from bubbling up the
hierarchy. If user code adds an event handler to the button, the user
handler will always be invoked first and gets to decide whether to
consume the event (Event.consume()), prevent the default handler from
running (Event.preventDefault()), or let it continue to flow.

Using this simplified model, I've been able to switch over InputMap
and ListenerHelper (and therefore almost all controls) to use default
handling, and it seems to work pretty well.

Here is a PR with the implementation: 
https://github.com/openjdk/jfx/pull/2022<https://urldefense.com/v3/__https://github.com/openjdk/jfx/pull/2022__;!!ACWV5N9M2RV99hQ!OnSepyYfvYeheRw8A-xyeweOQoUPembkgJ5KD2QjYVbJGVRN-iqEo4Smiq1ZXkQuvgntR64VLg5zARo7H4N4y5oXH34$>

Reply via email to