On 12/11/2024 18:31, Andy Goryachev wrote:
I am not sure this is the best solution, since it does not solve the
problem of multiple actors adding their event handlers. I do like the
idea of prioritized event handlers, because it solves the problem
**reliably**.
I think there is no way around it - we need different priorities. It
may be a set or may be a wide range of integers (for the maximum
flexibility), but the main idea is that, at least in controls, we have
a situation where there are at least these priorities:
I think if we can find a solution that doesn't require priorities that
it should by far be preferred. You have to think more from a user
perspective. They have a Button, and they add an Event Handler to do
something with the SPACE key. They run their code, and they find SPACE
just disappears and never arrives at their handler. However, if they
add the handler in the Constructor of a Button subclass, or before the
Skin is applied, then the handler does work. There should not be a
difference for the user, because the Event Handler system does not
stipulate that there are internal handlers that are sharing this
infrastructure; for the user the system looks like it is available
solely for their use.
Let's say you now add priorities. What possible reason could a user
have for adding an event handler (for SPACE key) that wouldn't work (too
low priority) or doesn't work reliably (equal priority)? In other
words, the only relevant priority for users is the one that will make
their handler work, which more often than not will be MAX.
Now let's say we have a Node, and we add several handlers:
Event.ANY (+10)
KeyEvent.KEY_ANY (+20)
KeyEvent.KEY_PRESSED (0)
What order are they going to be called? Without priorities that would
be KEY_PRESSED, KEY_ANY, ANY
Now let's have a hierarchy of Nodes, A -> B -> C -- I add the following
handlers:
A: KeyEvent.KEY_PRESSED (+10)
B: KeyEvent.KEY_PRESSED (0)
C: KeyEvent.KEY_PRESSED (+20)
Which one gets the event first? Does having a handler higher up the
hierarchy trump priorities?
Providing an alternative solution that doesn't expose the user to a
priority system that further complicates an already complicated system
would be a big win. If we can simply say to the user your
filters/handlers will always go first that's a big win.
- application event filters
- application event handlers
- event handlers set by the skin
- ifUnconsumed-like handlers set by the skin
- ifUnconsumed-like handlers set by the application
Let's say a KeyEvent.KEY_PRESSED is fired at a Control (focusOwner),
then the order in which the event can be acted upon is:
- Scene hotkeys (Cut/Copy/Paste etc)
- Scene filters (set by user, KeyEvent.KEY_PRESSED first, then
KeyEvent.ANY, then InputEvent.ANY, then Event.ANY)
- Parent filters (set by user, same order as above...)
- Control filters (set by user)
- Control handlers (set by user)
- Parent handlers (set by user)
- Scene handlers (set by user)
- Scene checks unconsumed status, then processes the unconsumed handlers
in order
- Scene default button handling and mnemonic handling (if still
unconsumed at this stage)
The order of the unconsumed handlers is the order they were registered
in, which depends on where they were called from:
- ifUnconsumed called by Behavior filters set on Control
- ifUnconsumed called by Skin filters set on Control (possibly to
forward these events to an inner component although they should only do
so when ifUnconsumed calls them back)
- ifUnconsumed called by Behavior handlers set on Control
- ifUnconsumed called by Skin handlers set on Control
Note that Behavior always has the option to go first (and Skins should
install their internal Behaviors early) by installing a filter.
Behavior's handlers also go first, but can be overridden by a Skin event
filter.
Also note: the handlers may still get "mixed up" (user/system handlers),
but as all Behavior/Skins will use `ifUnconsumed` and so go last in all
cases, this has become irrelevant.
SO, neither Skin nor Behavior directly act on any incoming event, they
only register their interest. No Skin internal components are directly
receiving events here. Any event that is directly targeted at a Skin
internal component can use regular means to handle events as there will
not be a conflict with user handlers as those components are private to
the Skin.
--John