Raygui – A simple and easy-to-use immediate-mode GUI library
github.com
github.com
I’ve used Raylib and raygui on a few RPi based projects and recommend it. It’s a really simple, intuitive way to get an OpenGL-based UI running. Good alternative to web-based UIs because it has similar simplicity but runs on devices with lower specs. I built this pinball machine with raylib: https://youtu.be/iiBn7FVzlcc
To them the whole UI is one big black box, so your program is completely useless to anyone with a wide range of disabilities.
Disclaimer: I'm the author.
Or are there any "screen reader" libraries?
Not only that, but I thhink assistive tech should allow you do to easier GUI testing too, but haven't digged into that idea yet (probably folks already are)
On GNU/Linux, I think the only thing that exists in the Gtk+ features that were originally sponsored by Sun.
While it's popular to mock electron, it does make it easy to make accessible cross-platform apps, whereas most of the "minimal alternatives" are entirely inaccessible.
Other than game-development why is im-gui better than those normal widget kits we are relatively more familiar with: gtk, qt,etc)? or im-gui is specifically for gaming coding?
This makes it easier to create certain types of UIs where the main interaction is to modify some data structure directly, which is how many simple tools behave.
I reimplemented the text rendering in it at one stage (we needed to support arbitrary unicode strings, specifically file names with CJK characters), drilling all the way down to individual OpenGL calls to get it running [1].
Apart from any driver-level caches, there really is no UI state.
[1] For what it's worth, this was an easier undertaking than simply recompiling Qt.
Regarding complex widgets like TextInput or Tables having state, I'm completely OK with that. The problem with state in a retained-mode GUI is that it's stored in two places and can easily fall out of sync/create performance-related footguns/create unnecessary work building widgets. Whether or not they're "pure" immediate mode is not the point.
You just have to let people go through this phase. You should nod, smile, and compliment them on the economy of their vision. This phase hones skills people need for more serious endeavors. Butt God help you if you actually use one of these "small" systems in production!
You comfort yourself by saying they will run into the same problems that just wait their time in a dark corners of software or hardware. But they generally are in a much better position, with their smaller and simpler software, to deal with it than with old software.
The "BS" complexity of retained-mode GUIs, as you suggested, is not really BS but the logical result of using a client/server model. That said, for the average desktop application (where all necessary state is local to the user's own machine) this model really is a poor fit and ImGUIs shine.
A lot of the evangelism comes from the fact that many more conservative devs still insist on using the more complex client/server retained-mode paradigm, even when immediate-mode is more suitable to the task at hand.
I was tasked with using both types of frameworks back in my old job.
ImGui eliminates entire classes of bugs related to state synchronisation, and was much easier to make performant because it was overall simpler[1].
It's also much simpler to customise, as widgets are nothing more than functions that draw graphics according to a couple of input variables.
I think a lot of people get confused, as they think ImGuis mean redrawing every asset every frame and therefore performance should be bad. In practice, though, there are driver-side caches and it's absolutely excellent to both work with and use the results of.
[1] While in theory retained-mode GUIs should be as performant as ImGuis or better, in practice it's much easier in retained-mode to accidentally perform an expensive action multiple times in a row in response to a single user interaction. Just tick "select all" on a WordPress page with 100+ elements and you'll see it immediately (well, immediately after several billion cycles have passed anyway!).
The concept of gui fully controlled by program data works well for interfaces consisting of a single button. As soon as we have two buttons, each button has a position, and the positions is the data of the gui itself (retained data), not of the program.
Imagine a multi-tab interface - which tab is currently active and has its controls drawn is the property of the gui (retained data), again.
An action that can be invoked through a menu and can also be added to a toolbar. The toolbar can be expanded or collapsed. Again, that's data of the gui itself (of the view), not of the program (the model).
The currently focused button should be highlighted somehow - the property of the gui again.
Selected elements of a list (they should be highlighted). Expanded/collapsed state of a tree widget nodes. Scroll positions.
So, the immediate mode guis are interesting and somewhat liberating because they demonstrate one can easily build simple ui without complex widget libs, but the results are limited, and very soon stop being really immediate mode. When evolving, they become reinvented wheels of retained mode guis.
I think you’re arguing with some fundamental “philosophical” stance on what data should be owned by the user and what data should be owned by the GUI. But I feel that pragmatically immediate-mode GUIs are the most efficient at creating complex interfaces (like the ones you have described). The parts where IMGUi are lacking is in complex flexbox layouting, theming, and animations, which isn’t required in editor-like applications for professionals but might matter with end-user applications.
The only way to implement those things in an immediate mode framework is to cache a lot of state. I haven't found this to be any easier, once the layout becomes complex enough the amount of information that needs to be cached approaches what it would be in retained mode. Because of this I've also found the performance of immediate mode to be very bad. Especially when it involves:
- large pieces of reflowing text (this means no text editors or word processors)
- very large datasets in list/tree/column/grid widgets (this means no database editors or spreadsheets)
- something like a flexbox, css grid or a constraint-solver based layout (this means no responsive layouts)
These are all nice things that don't fit well with those patterns. Without any caching or keying it's functionally equivalent to having a React layout that diffs the entire tree before every redraw.
The main use cases for immediate mode appears to only be quick prototyping, and making debugging tools for games and simulators. That's not bad by any means, but I've never seen one that is able to efficiently do complex layouts with large datasets.
It’s can be such a fast and light way to work. Get this thing done. And move on to the next thing.
To me it’s just a valuable addition to our toolbox and our quest to use the right tool for the right job.
It is like in games, everyone ends up implementing their own scene graph, while asserting they don't need the one offered by retained mode APIs.
Edit: Looks like it uses real fonts it just includes a crappy one. I'll have to take a closer look at this.
Usually you want app GUIs with a style that matches the rest of the apps. Though mobile kinda changed that. I don't know of a performant UI framework for building non traditional desktop apps.
Unfortunately this doesn’t work on iOS (probably for power consumption reasons). Apple does have some filters for things like Gaussian blurs, but they are a private API.
Then you do something like rootGUI.Query("elementID").RegisterCallback( e => otherFunc(e));
if checkbox(&show_debug, "Show debugging features") {
if button("Run some test") {
test()Defining the UI via code becomes cumbersome, stuff like adding attributes, etc
button.SetBackground(<color>)
or
<button backgroundColor="<color>" />
I see no difference there.
EDIT: Ok the main difference I guess would be having to switch files (and thus context) which could be annoying. Plus there's also the non-zero performance impact from having to parse an XML format.
<Box> <Button> <Icon/> </Button> </Box>
In code it's something weird like
Box(button( Icon() )))
Box(
Button(
Icon(...)
)
)
Weirder than: <Box>
<Button>
<Icon ... />
</Button>
</Box>With XML I'm attaching my callbacks via code while defining the UI elsewhere.
With pure code, I have to attach the callbacks directly to the object.
Theirs a reason you don't just use JavaScript to define React components
The main benefit of most immediate mode guis I've seen is that it's trivial to expose internal values to a ui in one line of code. No callbacks, no looking for the right place in any XML file or similar. Instead you add these lines when you need them and they show up in a ui:
checkbox(&godmode, "Activate Godmode")
slider(&enemy_aggro, "Enemy aggression", 0, 100)Not that any of this is relevant to immediate mode GUI, but the actual attachment of callbacks can be done identically in code no matter how the base UI design is done if you want it separately, but if you do it in code (whether with normal syntax or a funky transform like JSX) you can also have the option of direct attachment.
> With pure code, I have to attach the callbacks directly to the object.
No, you don't, but you can, and as it turns out, that's kind of popular.
> Theirs a reason you don't just use JavaScript to define React components
You can, in fact, but even when you use JSX, there is a reason you don't use actual (HT/X)ML code, but instead a language that compiles to, and let's you freely mix in, JavaScript.
Would you really define your UI in code over XML or HTML
The more complex and more dynamic the UI, the worse the separation gets. The worst of it being when you’re writing code to generate your XML/HTML, and probably introducing a third “templating” language to continue the pretense that HTML/XML is not just standard data structures defined in a different syntax.
The purpose of XML/HTML is to make your GUI specification independent of the programming language itself, and independent of any particular library (anyone can spit out an HTML <button>… it’s quite difficult for C++ to construct a Java Button class).
And there’s some notion that it’s easier for “non-programmers” to edit, without being bogged down programming language details (its “easier” to learn HTML than Java… and as programmers we eat the cost by now having to learn both)
It might just be a Unity thing, but up until they released their XML UI tools, I could not build a decent UI for the life of me. Basic things like getting buttons to appear where I want them to are very hard without XML
And it should largely take you the same amount of effort;
Div (
button(…)
)
And <Div>
<button>…</button>
</Div>
is equivalent. Things like separating your definition from your styling isn’t a feature of HTML/CSS, it’s a feature of the API design — the API just happens to be encoded one way or the other. In-line callbacks vs a selector + callbacks is the same as well (you still need a way to navigate the hierarchy, but XPATH & co. makes just as much sense on an object tree as it does on XML — although with code, you could also just keep the pointer around)I don’t know unity’s API’s, but I’d bet that their GUI API doesn’t actually look much like their XML API; and that if they did, you’d have had just as much, or little, trouble picking it up.
Horizontal:
box 10.WPerc, 100, 8.Em, 2.Em
Button:
text: fmt"Clicked4: {self.count:4d}"
setup: size 8.Em, 2.Em
onClick: self.count.inc()
https://github.com/elcritch/fidget/blob/devel/tests/basicwid...button.SetIcon(<icon-path-or-font-glyph>)
box.add(button)
Doesn't really seem all that weird to me.
High refresh rate screens are a reality so you only have 3-6ms to render each frame, as well as high resolution screens at 4k-8k. That's a lot of pixels to push with a CPU on every frame. As another comment points out we also need accessibility. I would generalize this need as "GUIs need to be reinterpretable", this need is not limited to those with disabilities and such a capability can be useful to anyone that wants to customize, automate, and integrate their digital tools.
Are immediate-mode GUIs up to the task? What is driving their authors to pursue this design? Why aren't they choosing a declarative design?
> Are immediate-mode GUIs up to the task?
Yes, there's no reason why imgui's can't do any of that.
> What is driving their authors to pursue this design?
To name a few: Ridiculous bloat and complicated development with things like Qt. No real GUI framework provided by Microsoft apart of their 4-5 half-abandoned offerings.
Also imgui is way simpler to wrap your head around. It's just straight up function calls with no complicated abstractions.
> Why aren't they choosing a declarative design?
Why should it be declarative though? imgui code looks fairly declarative when you read it and it's easy to understand what UI events cause changes
The fact that it requires an order of magnitude less code to do functionally the same thing as traditional toolkits.
>Why aren't they choosing a declarative design?
Because they aren't trying to build a native, standalone desktop application. Declarative design makes little sense for quick and dirty developer tools that just need to directly view and modify simple data structures.
This is a common misconception.
At least with Dear ImGui, all of the actual rendering is performed by the graphics driver, which has a cache.
So, while you may have to queue 100 "render texture" calls every frame, there's no real difference between ImGUI and retained-mode when it comes to the actual frames being rendered.
> Are immediate-mode GUIs up to the task? What is driving their authors to pursue this design? Why aren't they choosing a declarative design
Immediate mode UIs make managing state much easier, and avoids callbacks for event processing. That alone is worth it in my opinion.
> Are immediate-mode GUIs up to the task?
Enough for companies like Unity/UE4 to sponsor the github projects.
Most just don't this (and instead use dynamic buffers which are updated with new data each frame) because the state diffing would complicate the implementation and is usually "not worth it", but they still implement batching within a single frame and reduce the amount of draw calls as much as possible (for instance in Dear ImGui, there's usually one draw call per scroll/clip region so you get away with a handful or at most a few dozen draw calls even for complex UIs).
My main problem with DearImgui is lack of RTL language support. I think one of the biggest factors which deter people from using immediate mode GUI is lack of support for many languages.
Couterintuitively this is often less complicated than the traditional "retained mode" style of GUI libraries because there is no duplication of state. That means no setup or teardown of widget object trees, no syncing of state between GUI objects and application code, no hooking up or removing event handlers, no "data binding", etc. The structure and function of your UI is naturally expressed in the code of the functions that draw it, instead of in ephemeral and opaque object trees that only exist in RAM after they're constructed at runtime. You retain control over the event loop and you define the order in which everything happens rather than receiving callbacks in some uncertain order from someone else's event dispatching code.
Crucially, "immediate mode" is a statement about the interface of the library, not the internals. Common objections of the form "immediate mode GUIs can't support X" or "immediate mode GUIs are inefficient because Y" are generally false and based on a misconception that immediate mode GUIs are forbidden from retaining any internal state at all. It is perfectly normal for an immediate mode library to retain various types of internal state for efficiency or other reasons, and this is fine as long as the state stored in user code remains the source of truth. This can even go as far as internally constructing a whole retained widget tree and maintaining it via React-like tree diffing.
Here's a checkbox in Jetpack Compose with the required state annotation and event handling stuff, which if forgotten or done incorrectly will make the checkbox silently malfunction:
val checkedState = remember { mutableStateOf(true) }
Checkbox(
checked = checkedState.value,
onCheckedChange = { checkedState.value = it }
)
And here's a checkbox in Dear Imgui which just uses a plain bool and no event handlers (also note that this includes a clickable text label which would require additional event handlers with Compose): static bool foo = true; // could be any bool in the application, nothing special about this one
ImGui::Checkbox("Foo enabled", &foo)
Here's a great example of how the retained state in Jetpack Compose can confuse people: https://stackoverflow.com/questions/70040541/jetpack-compose...TLDR: instead of declaring your GUI objects/components ahead of time, and letting a framework render the prepared scene graph (while calling you back when there are user triggered state changes), you just draw the GUI objects yourself in your main loop, just like you draw all your other objects (backgrounds, sprites, models etc).