Representing keyboard and mouse focus

Started by
10 comments, last by Amr0 11 years, 8 months ago
I'm designing a GUI system where I use a Widget class to represent everything. A widget can have child widgets (buttons in a panel, label in a button) and my game feeds the root widget input events from SFML or whatever. When given an event the widget does an action in response then (optionally) hands the event down to its children.

[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?
Advertisement
Since, by definition, only one widget can ever have focus at a time, why not just store a reference to that widget someplace centralized?

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

I said I want all ancestors of the focused widget to see the events directed at it. Suppose we have a widget tree like this:

  • Main Panel

    • Panel A

      • Button A
      • Button B
  • Panel B


  • If Button A had the focus, I'd want the event processing to go Main Panel > Panel A > Button A, in that order, instead of the event going straight to Button A.

    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.

    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.

    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.
    I'm not clear on what you gain by complicating your focused event dispatch in this manner?

    Wielder of the Sacred Wands
    [Work - ArenaNet] [Epoch Language] [Scribblings]


    I'm not clear on what you gain by complicating your focused event dispatch in this manner?

    How is focus normally represented in GUIs then? And how are dragging scrollbars normally handled?
    Exactly as I described. You track which single and specific widget has focus, and nothing else.

    Dragging widgets is typically done with a secondary type of "focus" that (at least in the Windows world) is referred to as "mouse capture."

    Wielder of the Sacred Wands
    [Work - ArenaNet] [Epoch Language] [Scribblings]

    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.

    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.
    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

    Wielder of the Sacred Wands
    [Work - ArenaNet] [Epoch Language] [Scribblings]


    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.

    This topic is closed to new replies.

    Advertisement