In Runt's UI I had a quite specific requirements on how I needed the information about the focused view to be stored. The UI combines retained and immediate UI. That and some specific features of Runt make for quite specific requirements that I didn't anticipate at the beginning.
The UI tree is a retained structure kept in memory with Views having stable pointers at all times. Immediate mode is used to do
draw that happen every frame for every view (in tree order). This results in a requirement of having immediate access to as many properties as possible, focus being one of the top ones.
Originally I just stored the pointer of the focused view to the window struct, and made a contract that anytime you want to change the focus, you need to call
view_focus(View* view). First part didn't last long, though.
Once the Shell entered the game, I needed the shell to know which view was being targeted with commands. So if the target view is a code editor, I can ask it which commands it implements, and am able to call the command's function with the correct context information when it's executed.
Not only that, if you pay close attention to well made desktop applications, you can see the focus doesn't reset when you're activating tabs or windows. That would happen if you only kept one pointer to the focused view.
To illustrate the solution say you have following view structure:
Window Root - Panels - Panel 0 - Code Editor 0 - Code Editor 1 - ... - Panel 1 - Shell
Say you focus
Code Editor 1.
One solution would be to store a focus chain:
Window.focused = Root,
Root.focused = Panels,
Panels.focused = Panel 0, and finally
Panel0.focused = Code Editor 1. However this way, most of the information stored is useless, and to get to the focused view in a certain UI branch you need to recurse every time.
My solution is to store the pointer to
Code Editor 1 on every of it's parents
Window. This way, I know that when Window is activated, I need to set the focus to
Code Editor 1, and be done with it. It of course needs some careful update when the focus changes, or the view is removed, but these actions have very low frequency (compared with accessing the
Now imagine I focus
Shell, I'll write the value of
focused in parenthesis after each UI node:
Window (Shell) Root (Shell) - Panels (Code Editor 1) - Panel 0 (Code Editor 1) - Code Editor 0 - Code Editor 1 - ... - Panel 1 - Shell
Now it is very easy to tell what is the context for commands executed in
Shell. It just needs to look at
focused property of
Panels sibling. Additionally I can restore focus of any branch of the UI very easily.
When I recall the code acrobatics I had to do with third party UI libraries in prototypes I did in the past, it almost makes all the ready-made libraries not worth the time. Runt's UI core is quite tiny (cca 1k lines including layouts) and does exactly what the application needs.