3
\$\begingroup\$

I'm designing a GUI framework based on trees of widgets. Widgets are general control and container structures that are everything from panels to buttons to file dialogs. Widgets have an array of child widgets; a button contains a label and maybe an icon. Widgets are fed input events where the widget first does its own logic with the event then passes it on to its children. The application feeds input events to a single root widget, which can be anything from a single button in the middle of the screen to a full control window.

I need a way to handle which widget(s) receives keyboard events. I've decided that one of the widgets in a given widget tree would be the focused widget. Widgets would have a gainFocus method and a loseFocus method, which would be used in response to mouse click events or manually by the application. It's unlikely I'll implement tabbing. When one widget gains focus, any existing focused widget would naturally lose focus.

I still want each ancestor of the focused widget to see a given keyboard event in turn before it's given to the focused widget itself. So if I have a panel with a text box in it, the panel would see the event before the text box does. Therefore I really need to keep track of the chain of widgets from the root widget to the focused widget. I also need to maintain data integrity when I change focus so that all widgets in a tree can correctly tell whether or not they're the focused widget or an ancestor of the focused widget.

I've so far come up with giving widgets a hasFocus boolean and a focusedChildIndex integer. hasFocus would be set to true when the widget has focus. focusedChildIndex would be set to the index of the focused child in its child array, which could be the focused widget itself or the next widget in the chain leading to the focused widget, or -1 to indicate that there's no focused child. Setting these variables when one widget in the tree gets its gainFocus or loseFocus method invoked is something I'm stumped on though.

Edit

So we're clear, this is what I think my widget class will be like:

class Widget
    private Widget parent
    private Array<Widget> children

    // Methods for getters, adding widgets to widget trees, etc.
    ...

    public void mouseClick(int x, int y)
        doMouseClickEvent(x, y)
        for c in children
            c.mouseClick(int x, int y)

As you can see in mouseClick the widget sees the event before its children does. I want keyboard inputs to work the same way, except this time I want to only have a single chain from the root to the focused widget to see the event. Plus I need to make sure that when one widget gains focus the currently focused widget in the tree loses focus.

\$\endgroup\$

3 Answers 3

2
\$\begingroup\$

I'm pretty sure all the widgets have parent in their information (except the root widget of course). So you can go back all the way up to the root whenever a key is pressed and pass events to all of them. I'm not really sure if you are found of static fields/methods but it can easily be implemented using those.

class widget
{
    public widget parent; // a referece to it's parent
    public static widget focusedWidget;
    public static void keypress(Key  k)
    {
        stack <widget> widgets;
        widgets.push(focusedWidget);
        while(widgest.Top.parent != null)   // first create a list of all widgets in the way back to root
            widgets.push(widgest.Top.parent);

        while(widgets.count != 0)           // then calling all widgets in that list starting from root
        {
            widgets.Top.OnKeyPress(k);
            widgets.pop();
        }
    }

}

also you can completely change how you look at the problem and add a focused widget to all your widgets. This way you'll have a little bit advantage of remembering which widgets had focus when user was working with another widget (maybe doing something in another window). but keeping track of is focused get's a little bit harder.

class widget
{
    public widget parent; // a referece to it's parent
    public widget focusedWidget;
    public void OnKeyPress(Key  k)
    {
        // do something here
        focusedWidget.ObKeyPress(k);
    }
    public bool hasFocus{ 
        get
        {
            if(parent == null || parent.hasFocus)
                return parent.focusedWidget == this;
        }
    }
}
\$\endgroup\$
4
  • \$\begingroup\$ So you're suggesting that the focused widget be a static variable in the Widget class? That would make things easy if my game is only ever going to have a single widget tree (i.e. single root widget). I may want more than one though that the game can switch between, such as a main control panel and a modal dialog box. \$\endgroup\$
    – Aaron
    Aug 3, 2012 at 21:45
  • \$\begingroup\$ @Aaron as I said you could use either of those two methods, at least those are the ones I've seen being used so far. if you are going to have multiple widget roots (or even multiple windows) I suggest using the later method. though it's a little bit harder to get started with but it gives you a lot more flexibility. \$\endgroup\$
    – Ali1S232
    Aug 3, 2012 at 22:25
  • \$\begingroup\$ I'm unsure how exactly you're setting the focusedWidget reference in the second method. Does a widget's focusedWidget point to one of its immediate children, leading to the focused widget, or does it point directly to the focused widget? Also, the hasFocus method in your second example would seem to only work if the widget has a parent. \$\endgroup\$
    – Aaron
    Aug 3, 2012 at 22:46
  • 1
    \$\begingroup\$ @Aaron focusedWidget always points to one of it's immediate children. this allows you to easily traverse from root to the actual focused widget and ask all of them to do what ever they need to do (see how Onkeypressed work). it also kinda helps saving what objects were previously focused. for example if you change the active window, you still know which widget was selected. Also I assumed you only have just one root for all of your widgets (though you might want not to assume that), meaning root is always focused. it doesn't really hurts if you create a single root for all of widgets. \$\endgroup\$
    – Ali1S232
    Aug 3, 2012 at 23:40
1
\$\begingroup\$

What you're talking about here is an event-driven system. This is what most GUI systems use. It's always good to steal from the masters so, let's take a look at Flash's event driven system.

enter image description here

In this diagram, the user clicks on the red box. An click event is generated with the red box as target. As the event travels from the stage down to the box, every attached listener is called, which can stop the event from travelling further.

For instance, if the user clicks on the red box, but the root element has focus then the root element can stop the event from travelling futher.

Once the event reaches its target in the capturing phase, an optional bubbling phase occurs. Here, the event travels from the target element back up to the root. This is useful to for instance tell a parent element that a child element should have focus.

The event takes the following path:

enter image description here

So how would you implement this in C++? Well, take a look at the excellent libRocket. They have implemented an event-driven GUI system in C++. The gist of it is that an event should be an actual object and that you can attach event listeners to widgets. The event object keeps track of what phase it is in and what its target is. The widgets can listen to certain events and stop the event from progressing further.

The code for libRocket is well documented, I'd suggest taking a look at it for ideas.

Images were taken from Introduction to event handling in ActionScript 3.0 .

\$\endgroup\$
1
  • \$\begingroup\$ I already have an idea about how input events will be passed to widgets. What I need is a way to mark a widget as focused or unfocused such that I know what path down the tree to take when passing an event from the root and such that every widget in the tree has correct knowledge of its focused state. \$\endgroup\$
    – Aaron
    Aug 3, 2012 at 21:39
0
\$\begingroup\$

I believe I've come up with a solution.

As I said earlier, I can use a hasFocus boolean to designate the actual focused widget and a focusedChildIndex to indicate which child widget leads to the focused widget.

class Widget
    private Widget parent
    private Array<Widget> children

    // True if this is the focused widget
    private bool hasFocus
    // Index of child that leads to the focused widget
    private int focusedChildIndex


    new()
        ...

        hasFocus = false
        focusedChildIndex = -1


    // Returns -1 if not an immediate child
    public int getIndexOfChild(Widget child)
        int index = -1
        for int i = 0, children.size() - 1
            if children[i] == child
                index = i
                break
        return index


    // Root of tree this widget is in, or widget itself if it is the root
    public Widget getRoot()
        Widget result = this
        Widget p = parent

        while p != null
            result = p
            p = p.getParent()

        return result


    public bool hasFocus()
        return hasFocus


    public int getFocusedChildIndex()
        return focusedChildIndex


    public void gainFocus()
        if not hasFocus
            getRoot().loseFocus()

            hasFocus = true
            onGainFocus()

            if parent != null
                parent.focusOnChild(this)


    // Used by gainFocus
    public void focusOnChild(Widget child)
        // This method can be run if
        //   - The widget is not already focused on a child
        //   - The given child is actually a child of this widget
        //   - The child is either the focused widget or has a focused child
        int index = getIndexOfChild(child)
        if (focusedChildIndex == -1) and (index > -1)
          and (child.hasFocus()
          or (child.getFocusedChildIndex() > -1))
            focusedChildIndex = index
            if parent != null
                parent.focusOnChild(this)


    public void loseFocus()
        if (parent != null) and (parent.getFocusedChildIndex() > -1)
            parent.loseFocus()
        else
            if hasFocus
                hasFocus = false
                onLoseFocus()

            if focusedChildIndex > -1
                int oldIndex = focusedChildIndex
                focusedChildIndex = -1
                children[oldIndex].loseFocus()


    protected virtual void onGainFocus()
    protected virtual void onLoseFocus()
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .