I'm making a very small in-game GUI library for my gamedev projects, but I'm having trouble finding a clean way of handling focus.
My library supports nested ListBoxes and widget hierarchies, but I can't seem to find a way to prevent strange behavior when dealing with widget focus. Example form:
|------------|
| Form [X] |
|------------|
| |
| [Button01] |
| |
| [List1][v] |
| |
| [Button02] |
| |
|------------|
My design features a Context
object that stores a list of Widget
objects. A Widget
can have any number of children. I have facilities to recursively iterate all children/parents of a Widget
.
My focus logic currently is:
If the context is busy (dragging, resizing, editing...), do not change focus
If mouse is pressed outside of the context or context is unfocused, unfocus everything
Find the topmost pressed child, if any
Find the "deepest" pressed child in the hierarchy
Unfocus everything but the widgets as deep as the deepest child, and focus the context
This, however, fails in many occasions, especially with nested lists and TextBoxes. I tried to find a good solution online but couldn't find any article/tutorial. I'm not sure if I should give exclusive focus to special widgets, or if there is a "good" algorithm that works properly in any situation.
How do GUI libraries usually handle widget focus?
Are there special instructions to give exclusive focus to widgets such as ListBoxes or ComboBoxes?
Here's the source code of my GUI library, and a video here.
I suggest to define a tree like data structure that has node defined as data (window handle, bool isFocus and some additional data as you require) plus few pointers to nodes - parent, first child, left sibling, right sibling. Also, store the node pointer within the window data area so that from given window handle you may retrieve the node pointer. From node pointer, data would give you the window handle anyway. Now, whenever the root window loses focus, because you clicked mouse on other application or hit alt+tab or something, the main window loses focus and you get appropriate message of kill focus. Handle that, from the window handle reset all isFocus for the node tree. Whenever you click on some window, you get gainfocus message, from window handle find out the node, set isFocus as true. And set all its parent nide's isFocus as true until you reach the top node (root). Does it help?
I don't know any UI Algorithm or what is the proper way with UI , however I did implement a generic UI library a long time ago (not the best but it served it's purpose for that time) , and maybe the approach used can suit your needs.
In order to handle events from parents to children , two functions were used, with the following prototypes :
int handleEventReceived(int eventType, void* data);
int getEventConsumers(int eventType, vector<IConsumers*>& consumers);
Ofc eventTypes were found in an enum.
With the first function implemented in all the childres, a recursive function can be used in order to dispatch event to every children and children's children (so a broadcast). When a child answers the event, it returns 0, otherwise it returns some error code (for example ERRROR_EVENT_NOT_TREATED).
However, if you want only specific parts of your UI to respond to a certain event you use the 2nd function.
The 2nd function returns the "maximum response priority" , and the consumers associated with this priority. For example if your contaiers have 3 buttons with priority 5 and 2 buttons with priority 6 , it will return 6 and an array containing the two buttons, to which you can send the event.
In your case, you say that you need to handle focus for sub-components. Using the approach described above , I would create an event ET_FOCUS_RECEIVED , and put different priorities for this event on each UI element according to the need.
In your situation , if you put the event priority to increase as the element goes deeper in the hierarchy , elements from same depth will have same priority (so widgets as deep as the deepest child will all treat the event).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With