Representing keyboard and mouse focus
#1 Members - Reputation: 132
Posted 08 August 2012 - 05:05 PM
[source lang="cpp"]class Widget private Widget parent private Array<Widget> children // True if mouse hit this widget or any of its children public bool mouseDown(int mx, int my) bool gotClicked = false bool blockChildren = false bool childGotClicked = false if visible and enabled if contains(mx, my) gotClicked = true blockChildren = onMouseDown(mx, my) if not blockChildren // Children are ordered front to back. // Children are not necessarily inside the widget. for c in children childGotClicked = c.mouseDown( x - this.x, y - this.y) if childGotClicked gotClicked = childGotClicked break return gotClicked // True if mouse event is blocked from the children protected virtual bool onMouseDown(int mx, int my)[/source]
In addition to mouseDown, widgets will respond to mouse drag and mouse up events. I'm going to ignore mouse move events so that I don't have to worry about enter and leave actions, and because touchscreens are teh futures.
I'm thinking about good ways to handle keyboard and mouse focus. A widget gets keyboard focus either programatically or by being clicked, and has all keyboard events forwarded to it. Mouse focus is something I'm thinking of in terms of widgets like scrollbars responding to mouse drag events even when the mouse is outside of them. I figure that would be like one widget receiving all mouse drag events, so the widget would have mouse focus in the same way a widget can have keyboard focus until the next mouse-up. Plus I want the ancestors of the focused widget (either keyboard or mouse) to see the relevant event first like with the mouseDown event.
One idea I came up with is representing focus state with four variables: hasKeyFocus, hasMouseFocus, keyFocusedChildIndex, and mouseFocusedChildIndex. A widget responds to a key if its hasKeyFocus is true or its keyFocusedChildIndex is greater than -1, and the widget passes the event along to its child at children[keyFocusedChildIndex]. A widget uses its hasMouseFocus and mouseFocusedChildIndex variables in the same way for mouse drag and mouse up events.
Is this a good way to handle focus for key and mouse drag events?
#2 Moderators - Reputation: 7549
Posted 08 August 2012 - 05:41 PM
[Work - ArenaNet] [Epoch Language] [Scribblings] [Journal - peek into my shattered mind]
#3 Members - Reputation: 132
Posted 08 August 2012 - 05:55 PM
- Main Panel
- Panel A
- Button A
- Button B
- Panel A
- Panel B
As for keeping the information in a centralized location, I want as little overhead as possible. I don't want to wrap my widgets in a "gui manager" class. The application just needs to keep a reference to one or more root widgets, where a widget can be anything from a single button to a large control panel.
#4 Crossbones+ - Reputation: 962
Posted 08 August 2012 - 06:27 PM
If anything, I think a central location would actually reduce overhead. If you don't want a manager or globals, you could go for a static members.As for keeping the information in a centralized location, I want as little overhead as possible. I don't want to wrap my widgets in a "gui manager" class. The application just needs to keep a reference to one or more root widgets, where a widget can be anything from a single button to a large control panel.
#5 Moderators - Reputation: 7549
Posted 09 August 2012 - 12:26 AM
[Work - ArenaNet] [Epoch Language] [Scribblings] [Journal - peek into my shattered mind]
#7 Moderators - Reputation: 7549
Posted 09 August 2012 - 08:04 PM
Dragging widgets is typically done with a secondary type of "focus" that (at least in the Windows world) is referred to as "mouse capture."
[Work - ArenaNet] [Epoch Language] [Scribblings] [Journal - peek into my shattered mind]
#8 Members - Reputation: 132
Posted 10 August 2012 - 04:43 PM
One example I had in mind for my project is a panel with a draggable row of pictures. I can either click on a picture to "select" it and have some data show up somewhere, or I can grab and drag anywhere in the whole row panel to scroll it.
That is, I'd want to be able to click-and-drag anywhere inside the panel - including inside a picture - and drag the row of pictures around. Clicking and not moving the mouse while over a picture would be interpreted as selecting the picture.
I figured I could make this work by letting pictures under the mouse (if any) get the focus but let the parent panel see the events sent to them. That way if the mouse is dragged beyond a certain threshold I can have the panel do its scrolling action even when it's a picture that has the actual focus.
Edited by AaronWizardstar, 10 August 2012 - 04:44 PM.
#9 Moderators - Reputation: 7549
Posted 10 August 2012 - 05:35 PM
on_gui_event(event)
pass_event_to_widget(current_focus_widget, event)
pass_event_to_widget(widget, event)
if(widget.parent)
if(pass_event_to_widget(widget.parent, event))
return true
if(widget.wants_event(event))
widget.handle_event(event)
return true
return false
[Work - ArenaNet] [Epoch Language] [Scribblings] [Journal - peek into my shattered mind]
#10 Members - Reputation: 703
Posted 11 August 2012 - 06:38 AM
I'd still like the ancestors of the focused widget to see the event before the widget itself sees it though.
One example I had in mind for my project is a panel with a draggable row of pictures. I can either click on a picture to "select" it and have some data show up somewhere, or I can grab and drag anywhere in the whole row panel to scroll it.
[...]
Hi. I'd like to 1-up ApochPiQ's recommendation on following the design of a single focus widget at a time. Using a hierarchical focus approach can complicate things. Consider: you have a text box in a scrollable window. The user clicks on the text box and presses the right arrow key to move the caret in the text box, but the parent window sees this and scrolls to the right. Chaos.
The 1-or-zero focus window/widget approach, which happens to be what all GUI libraries I've come across use, has proven to be functional and versatile enough to implement whatever functionality you can think of.
Regarding your picture panel, the functionality of scrolling when the user clicks and drags and selecting when the user clicks but does not drag can be implemented using the single focus widget approach in a number of ways. The first obvious way is to not use widgets for the pictures in the panel and instead have the panel draw the pictures itself, as well as perform the required mouse hit-testing. This approach is used a lot. For example, tabs in a tab control are not individual widgets. There is no set criteria, but in general, if it's "a component in a list" kind of thing and doesn't require complex internal user interactions, it's not a widget, but an internal component of the parent widget.
So, the picture panel detects when the user clicks, and waits for the user to release the mouse button. If during this wait the user moves the mouse a certain number of pixels away from the original click location, it initiates a scroll operation. Otherwise, the picture is selected when the mouse button is released.
#11 Members - Reputation: 132
Posted 11 August 2012 - 05:22 PM
So rather than pass events to the root widget, where a widget first processes the event then decides which child to pass the event to, I'd pass events to the focused widget, where a widget first sends the even to its parent then processes the event.That's easy enough, just use something like this pseudocode:
on_gui_event(event) pass_event_to_widget(current_focus_widget, event) pass_event_to_widget(widget, event) if(widget.parent) if(pass_event_to_widget(widget.parent, event)) return true if(widget.wants_event(event)) widget.handle_event(event) return true return false
Hm. My example of the scrollable row of pictures would have been implemented with a panel that contains a row layout widget, with the row layout widget containing a bunch of image widgets, If the panel becomes a single widget without focusable sub-widgets then would it still be possible for me to reuse the image and row layout widgets? I want to leverage composition as much as possible throughout my GUI system, e.g. buttons contain text labels.Regarding your picture panel, the functionality of scrolling when the user clicks and drags and selecting when the user clicks but does not drag can be implemented using the single focus widget approach in a number of ways. The first obvious way is to not use widgets for the pictures in the panel and instead have the panel draw the pictures itself, as well as perform the required mouse hit-testing. This approach is used a lot. For example, tabs in a tab control are not individual widgets. There is no set criteria, but in general, if it's "a component in a list" kind of thing and doesn't require complex internal user interactions, it's not a widget, but an internal component of the parent widget.
#12 Members - Reputation: 703
Posted 12 August 2012 - 02:43 AM
But that's not the point. The point is that for input processing, a single input processor (widget) at a time is more intuitive, predictable, and less bug-prone. But then again, using a hierarchical focus approach is not necessarily WRONG. As long as you understand what's going on in your system and have a clear understanding of how input messages are being passed around and processed, and if you feel it's more intuitive for you, then go ahead and use it. Just with all design choices, if you later find that you are doing a lot of shortcuts and hacks in your code, it's an indication that the design may be flawed. So pick something, work with that, and see how it works for you.






