The important point is that I don't allocate and re-initialize the state of all controls. It's enough if you do from scratch the layout - that gets much simpler if you don't have to code a diff against the previous state.
So I basically have a simple Widget abstraction - a draw function and a process-input function + some identification data (implemented as a union of void-ptr and int). I can then make lists of these abstract Widgets, and each list represents the set of childs of a superordinate element.
This abstraction allows me to construct hierarchies and to have unabstracted and very flexible code for the layout, while the layout and input processing code can be generic - it's a simple loop over the list, calling the callback with the abstract handle.
If there are ever performance problems you can still fluidly move more towards some retained layout state. This for sure will require more complex code, but if you just do it for complex layouts with lots of elements, I figure you get the best of both worlds.
Mouse and drag handling is a little bit tricky as well - how to keep sending events to the slider control widget for example even if the mouse is temporarily outside the widget? The important realization is that dragging needs global state. You set the mouse globally as "grabbed" and provide a callback to execute when there are more mouse events.
- it is capable of storing state: storing position in scroll view, which page is currently visible, etc. - it tracks dependencies, so ideally UI building code is executed again only if something changed that would affect it's output.