What's the right UI framework pattern?

I'm making a UI system (mostly for fun and to learn), and I'm trying to figure out the right pattern for it. I want to use it for games and editors. So I was thinking some type of immediate mode GUI since in game UIs, it doesn't make sense to have events when states change, and it is pretty high performance, especially when moving position of elements a lot. And simple. Immediate mode pattern has a few issues though, there isn't really a good way to have an editor for building the UI visually. And if there was some type of editor, doing bindings would require boxing for each value (at least I can't think of another way without boxing). It is difficult for extensions to modify the UI. For retained mode patterns, if you do something like MVVM it is heavily event driven, which isn't great for game UIs and has lots of boilerplate, also rather complex to make. But is quite easy to do a visual UI editor/builder for. Retained mode GUI also generally are less performant when moving elements and such. I looked at like a MVU pattern from Uno, but that seems to have a lot of the same issues as MVVM had, with performance and events. I feel like there are other patterns, one that would fit well with what I want. But I'm having trouble even finding different patterns to look in to. To summarize what I am looking for: - High performance updates. - Mostly not event driven. - Easily modifiable from third-party. - Buildable from an editor. Any suggestions?
18 Replies
Anton
Anton2mo ago
Make a data oriented layer above a regular immediate mode GUI using which you'd generate the UI from an editor. Modifiable by third party — what does this mean?
MechWarrior99
MechWarrior99OP2mo ago
Not sure I follow what you mean exactly.
Modifiable by third party
Like with say, HTML you can get the elements of a page and then insert new child elements or modify them.
Anton
Anton2mo ago
It need to be data oriented then, there's no other way It means that you put the widget data to draw in an array and then send them to the system Not like if (Button(x, y, ...)) ... but like
Button b = new Button(x, y, ...);

// later

if (DrawButton(b)) ...;
Button b = new Button(x, y, ...);

// later

if (DrawButton(b)) ...;
I don't see it being possible unless you have something like this Or an array of Buttons So people could modify what to day by adding to the array This can be added on top of your main system
MechWarrior99
MechWarrior99OP2mo ago
Ahh yeah yeah. That makes sense. I was thinking about if each widget was a struct, and re-create them each 'frame'/'render'. But that feels like it would be potentially pretty memory heavy, and can't think of a good way of doing it without using interfaces, which would degrade the performance more.
Anton
Anton2mo ago
At least that's how I see it Still if you're going to have an editor, you need it to be able to serialize and deserialized the widgets Unless you want it parsing actual draw code, you need a way to store instructions as data So with an editor it's kind of a necessity to have a layer like this
MechWarrior99
MechWarrior99OP2mo ago
If they are just structs that shouldn't be an issue.
Anton
Anton2mo ago
What do you need interfaces for?
MechWarrior99
MechWarrior99OP2mo ago
If I did the structs, they way I see it I would at least need something like an IDraw { void Draw(DrawContext ctx); }, along with a IWidget { IEnumerable<IWidget> CreateChildren(); }, With IDraw handling drawing of 'primitive' widgets, like text, rectangle, circle, etc.
Anton
Anton2mo ago
Well that's just a regular object oriented system then For performance and flexibility you could use an ECS This feels like a great use case for one
MechWarrior99
MechWarrior99OP2mo ago
Yeah... I know... I still struggle fully grasping how to design data oriented systems at times
Anton
Anton2mo ago
I mean you probably could just pool the objects And have an object oriented design here But it will be slower
MechWarrior99
MechWarrior99OP2mo ago
I was thinking about doing it like an ECS, but considering A, things need to be computed in a specific order, and B, I can't see how to do the layout. Makes sense to do a component-per-layout type, but when you go to evaluate a entity, how do you know which layout it has? Can't really do all of the VerticalStack layout components and then all the Grid layout components since the child entities sizes rely on the parents*
Anton
Anton2mo ago
I was thinking applying the layout to the base data before drawing, and drawing out of order in a loop (because you've saved the context) I've never done something like this Yeah I guess you'd still need to go by layers somehow Perhaps you could order things in the arrays by layers So you'd have to jump around less And store the layer By layer I mean like how nested it is
MechWarrior99
MechWarrior99OP2mo ago
Right, but to do that would need to have like a ILayout { void Layout(Size size); } interface that components/widgets implement. Which kind of is not great for performance on structs.
Anton
Anton2mo ago
You don't. You'd just store each type in a separate array Store the data of all VerticalStacks in a single array Then when you're going to process the vertical stacks, you know all the items in the array are vertical stacks So you will be able to call the concrete function for the vertical stack ILayout { void Layout<T, U>(ReadOnlySpan<T> dataArray, Span<U> layoutArray) } T = vertical stack U = the layout struct that T produces The size is going to be precomputed and stored in the T Precomputed from the other layers The upper layers i guess So yeah I guess there's going to be another step First make a layout from all things in a layer Then apply it to the next layer Then move on to the next layer Idk, that's just how I see it
MechWarrior99
MechWarrior99OP2mo ago
Oh that is different than what I thought you were saying. But I either way I see what you are saying. But then the question is I think is if the extra complexity is worth the performance. My thinking originally with the structs was to re-create them each frame. But I guess that would really be not the best performance wise probably.
Anton
Anton2mo ago
Idk one would have to try and measure it Also I just thought of a thing If you could make layouts be like a one fixed thing Only determined by the data They could stay in a single array
MechWarrior99
MechWarrior99OP2mo ago
Yeah guess so. The thing I do like about normal IMGUI is not having to manage the state of the elements, like if you select a 'tab' you don't have to 'unselect' another 'tab'. I don't think so, as I want to be able to support like radial layout or stacks etc. I was trying to look at what thing like Comet , ReactMAUI and ReactJS do, but truth be told I didn't fully follow and didn't seem to fit.

Did you find this page helpful?