Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GUI library: how to correctly handle focus?

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:

  1. If the context is busy (dragging, resizing, editing...), do not change focus

  2. If mouse is pressed outside of the context or context is unfocused, unfocus everything

  3. Find the topmost pressed child, if any

  4. Find the "deepest" pressed child in the hierarchy

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

like image 655
Vittorio Romeo Avatar asked Apr 03 '14 16:04

Vittorio Romeo


2 Answers

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?

like image 178
Dr. Debasish Jana Avatar answered Nov 05 '22 20:11

Dr. Debasish Jana


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

like image 36
MichaelCMS Avatar answered Nov 05 '22 21:11

MichaelCMS