korge
korge copied to clipboard
Fix event system: make it more flexible now that K/N is faster + prevent overlapping of mouse events + key events with focus manager
Some work already done regarding to using proper events. But still we need to handle mouse events overlapping, and maybe limiting the scope of key events
Is there any documentation or outline on how the event system will work when complete? For example, I'm running into a few issues that I would hope would be accounted for:
- Some way to stop an event from triggering downstream handlers ( I think
stopPropagation()will handle this?) - Some way to either define the order of the event handlers, or conversely, a way to handle event both while navigating down through the dispatch chain, as well as back up.
- Right now it seems the parents handlers are triggered before their children. I think this makes sense from an implementation, but for example, for MouseEvents, I think I would want the order to be based on what your mouse is over on screen. For example, a View with a button I think should trigger the button first on a click, rather than the view. A way to either define that, or have the event dispatch down, and then back up, may suffice.
- JavaFX does something similar, with the concept of event filters (trigger on the way down) and event handlers (trigger on the way back up).
I think having a way to trigger based on what's immediately rendered under the mouse first, and a way to stopPropagation() of events would solve the issue of overlapping mouse events as well.
Thanks for the details @cmhulbert , it is really useful. I believe this should be fixed already since this is important, and this problem has been there for quite a long time already. I'm going to make an implementation and that covers the stop propagation and the direction, and I'll put you as reviewer to see if it fits your needs and makes sense to you.
Hey @cmhulbert I investigated a bit, and the only issue I could find is an issue with buttons inside a scrollable area that were being clickable despite being invisible.
Do you have other cases where this is failing?
TL;DR;
I also investigated JS, and in JS key events happen from the focused component and bubbles up. UITextInput has into account focusing. But key events are propagated in the case of korge.For mouse events, we didn't cached view bounds initially, so to avoid expensive stuff, we did it another way. Now that view bounds are cached, we could like figure out the view over the mouse, and then bubble up. But there are some events like onUpOutside that need this propagation behaviour to work properly.
So, there are a few issues that I see, but maybe I'm not doing this right. Here is the first snippet, which is ideally the way I would like to write the event handling:
Korge(
windowSize = Size(800, 800),
virtualSize = Size(200, 200)
) {
solidRect(150, 150, Colors.GREEN) {
x = 50f
y = 50f
zIndex = 15.0f
onEvent(MouseEvent.Type.CLICK) {
print("Green")
it.stopPropagation()
}
}
solidRect(150, 150, Colors.RED) {
zIndex = 10.0f
onEvent(MouseEvent.Type.CLICK) {
print("Red")
it.stopPropagation()
}
solidRect(50, 50, Colors.WHITE) {
x = 75f
y = 75f
zIndex = 20.0f
onEvent(MouseEvent.Type.CLICK) {
print("White")
it.stopPropagation()
}
}
}
}
Looks like this:
In this case, clicking in any portion of either the Red, White, or Green square will print
Green. I think this is because that is the first child of the stage, and It's always handling a mouse click, and always telling it to stopPropagation. I think what I would expect is that it only triggers the mouse click event if it passes the hitTest. And then it would be in order of the focused view, like you mentioned above.
So the second thing I notice, is that if you call the same code, but add in the hitTests explicitly, it almost behaves the way I would expect:
Korge(
windowSize = Size(800, 800),
virtualSize = Size(200, 200)
) {
solidRect(150, 150, Colors.GREEN) {
x = 50f
y = 50f
zIndex = 15.0f
onEvent(MouseEvent.Type.CLICK) {event ->
hitTestLocal(localMousePos(views))?.let {
print("Green")
event.stopPropagation()
}
}
}
solidRect(150, 150, Colors.RED) {
zIndex = 10.0f
onEvent(MouseEvent.Type.CLICK) {event ->
hitTestLocal(localMousePos(views))?.let {
print("Red")
event.stopPropagation()
}
}
solidRect(50, 50, Colors.WHITE) {
x = 75f
y = 75f
zIndex = 20.0f
onEvent(MouseEvent.Type.CLICK) {event ->
hitTestLocal(localMousePos(views))?.let {
print("Green")
event.stopPropagation()
}
}
}
}
}
In this case, clicking Red will print Red, and clicking Green will print Green but clicking White will still print Green. Since White is in front of Green, I would I think expect it to trigger first, and then stop the event from propagating to Green