-=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- (c) WidthPadding Industries 1987 0|429|0 -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=-
SoCoder -> Blogs Home -> Blogs


 
Cower
Created : 03 June 2010
 
System : Mac

Something about a bad idea



While designing/coding the Nihilit UI toolkit (NUIT for short, since it's a lot more fun to call it 'the newt') there were various issues I was dealing with that mainly pertained to my rather bad experience writing GUIs. I'm actually pretty sure that even in writing NUIT I've repeated a number of mistakes, but they're probably forgivable - the lack of any sort of OS integration (e.g., embedding content in actual windows instead of having windows inside of a native window would be a nice change - this could probably be done, too, but I'm not sure how well) is a big one for me. Not what I plan to talk about, since I think it can be fixed without any huge changes to how NUIT in specific is designed, but it can be problematic at times.

For the most part, I just want to vent about how stupid I was while writing the initial event handling code for NUIT, which is probably viewable in the github repo if you look at the BlitzMax code (NUIT for ooc is receiving my attention right now, since there's not a lot of reason to have something like this in BlitzMax, much as I'd like to think otherwise). Essentially, event handling/propagation was all done bottom-up. That is, an event was sent to the top root window and it would be expected to pass that event onto its children, and then its children would pass the event on and hopefully it would reach the the desired view. It's sort of like an honor system for events - you pass on the event, but there's no guarantee the children will receive it. Sort of like this:



There are a few reasons why I didn't like this and still don't like this. The main problem, though, is that events may never reach their intended destination. Even worse, though, was that the intended destination was never really known. The view you clicked may not be the view that the user would think is receiving the event, and that event is also being blasted to all other views on its way to the view that'll handle the event (if it's not taken by another view that may just be grabbing everything that's sent to it).

These problems are all issues with design on the part of the views that pass on events and such, but leaving that responsibility in the hands of the people writing code, especially when they're expected to do this for all view subclasses, is absurd. There's no reason the code for receiving the mouse click should also check its subviews to see if they want to handle the mouse click. It's unreasonably stupid.

The first issue - whether or not the intended view will receive the event - is something that I honestly don't think I can solve. I can make it simpler though, and I can make it harder for it to fail in most circumstances. The main solution, and this is the painfully obvious one that everyone will call me a moron for, is to 1) find the furthest possible view that can respond to the event and 2) always find the view that's most likely to handle the event.

Essentially, you want a leaf view, but if that won't suffice, at least go for the farthest thing from the root window. This is fairly easy to do: all input in these scenarios is done through some pointing device (in this case a mouse) where you're given an X,Y location where the event is happening, so the simple solution is to search the view hierarchy and find the furthest view that contains that point (this may be the root or nothing at all if the location is outside of the root window's frame).

Once that view is found, if one is found, then pass the event to it. At this point, the view can choose to handle the event, ignore it, or pass it on to its superview. It is completely valid to not pass the event on to a superview and is probably a good idea in most situations, because - most of the time - that event is going to be ignored by all the superviews as well. There is no responsibility by the event code to do anything, and by default, events will be passed to superviews anyway. Ultimately, fewer views will receive the event if it goes unhandled and the desired view should always receive the event first. To illustrate, I'll take the same ugly diagram from above and scribble on it some more:



It looks slightly more complex, probably because there are more lines, but the overall process is far simpler because there's very little confusion as to what is happening between the mouse click (in the picture) and the view receiving the event. The view that first receives that event can safely assume that it was intended to receive the event, because it was either the best match for receiving the event, or it's a superview that was given the event since a subview (probably) didn't handle it and decided that its superview should receive it.

This doesn't resolve some issues with things like what view receives keyboard input. My answer to that is the focal view, which is the first view to receive a mouse input event [not the one to handle the event. I'm still debating if I consider this to be good, but it's difficult to come up with situations where this doesn't work.

I've now lost track of where I was going with this, so I'm ending this rambling. At best, you hopefully are smart enough to not be as stupid as I am most of the time or you've at least learned not to do what I did. At worst, this is all indecipherable rambling and you're probably quoting Nietzsche in your head.

 

Comments


Thursday, 03 June 2010, 06:15
JL235
I use the former in my game framework as it allows a parent to control what it's children can and can't see. But I find this only useful for games and not for GUIs.

Your second solution looks like the correct way to build this, very similar to how Swing deals with it's event handling. Except do you really want the event to be passed up if it's not handled? My trail of thought is that if I click on a button, I didn't click on the window or anything behind the button. I clicked on only the button and so only the button should be given the mouse event.

For a game I can see reasoning for passing it on to the parent, but not for a GUI. Plus If the developer wanted that behaviour then they can easily build it themselves.
Thursday, 03 June 2010, 06:22
Jayenkai
Makes sense..
That way you'd have "Event" once, rather than having to keep passing it on from one element to the next.

You'll also have to decide if "Click different window" = "Click ON the window", or just "Click TO the window".
I'd forgotten that was an issue until I used the Mac, at which point I remembered having to install "XMouse" (I think it was called) on the Amiga!

But, yeah, makes more sense going that way.

I'm still too lazy to do any proper GUI stuff, though, and tend to just check everything to see if the mouse is in it.
Thursday, 03 June 2010, 07:05
Cower
JL235: I'm partly thinking of touch interfaces where you might scroll a view by dragging across it, which may result in your touching another view that could receive it if you don't drag your finger, for example. In those cases, even if the button is the outermost view to receive the event, it could pass the event on to a superview that might see the event and go "I'm going to start scrolling here (since a certain amount of distance from the pivot point has been reached, for example), make me the view that receives future motion events".

In typical desktop GUIs this probably wouldn't happen, but seeing as how ooc compiles to C (and could produce code for systems like the one I mentioned) and nuit-ooc is renderer-agnostic, it could at some point end up on a platform other than a desktop where I think that behavior would be desirable.

I can imagine in other systems it would be good to have parents filter the input of their children, but in the case of a GUI I think it's bad behavior and just results in a nightmare for people working with the GUI who probably expect this behavior.