💾 Archived View for tilde.pink › ~slondr › a-tour-of-rust-gui-frameworks.gmi captured on 2024-02-05 at 10:11:28. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
I went on a bit of an impromptu tour of Rust's gui landscape this week, trying to see if it was any better than Erlang's (which...sucks).
Note that when I say "Rust's GUI frameworks" I mean just that. I am totally uninterested in bindings and even less interested in anything which pulls in HTML or JavaScript. There is no Rust implementation of libxcb yet, so virtually all available Rust-only GUI frameworks target wgpu or glow or just have a runtime dependency on SDL. This means very poor integration with native desktop look-and-feel on any platform, but comes with the upside of being more cross-platform by default.
https://github.com/iced-rs/iced
Gave up on this one after about two hours. The documentation is totally out of date, there's a toooon of boilerplate, and even when things worked properly the resulting code was ugly and difficult to work with.
https://github.com/redox-os/orbtk
I want to love Orbtk, and I think, really long-term, it might end up being the best of the lot...eventually. OrbTK, for those of you who don't know, is the toolkit developed for the Redox OS project for all their graphical stuff, such as their windowing system and end-user applications. It's based on an entity-component-system, which I love the idea of (ECSes are awesome!!), and clearly a lot of work as gone into it.
Unfortunately it seems much of that work recently has gone into refactoring and consolidating what functionality exists. The source code of the library is super clean, but documentation is virtually nonexistant. The latest commit and latest published version don't compile, but I found a commit in between the two that does. Trying to reverse-engineer the source code to accomodate the lack of documentation actually wasn't that difficult due to how clean it all is.
Sadly there's some basic functionality missing, such as the ability to render textual content with fonts other than Roboto or with any styling applied, which make it a non-starter for projects that need that. It's probably great to work with if you don't care at all about presentation and just want a super high-performance interactivity library.
Technically the GUI framework here is eframe, not egui, but egui implements most of the features and is the flagship project title so that's what I'm calling it.
After a day of wrestling with orbtk's lack of documentation and new paradigm of connecting state, egui felt like surfacing from a deep underwater dive. I stuck with this one long enough to build a working Spartan browser in only a couple hours.
There's basically no boilerplate for the actual UI construction in egui; most widgets take a single line of code, and flex-like layouts are also widgets. Attaching new layouts and widgets to the application is done by passing around a Ui object and calling widget functions on it, or passing it to more complex layout functions. This makes hacking together a fully-functional, possibly-ugly user interface virtually as fast and fluid as implementing whatever underlying application logic you want to drive via the GUI. It's awesome! My Spartan browser is about 65% dissecting the gemtext file to determine the type of a line, and 45% setup, state management, UI declarations, styling, etc. With orbtk it was closer to 20% application logic, 80% dealing with the GUI library.
With egui, you pretty much have one "App" or "CentralPanel" which is the root of all your widgets. You define this app as a struct, and the members of that struct are your application's global state. Widgets which seem to have their own state really just bind to struct members, so if you have a text box, the text in that text box (both the initial value and after any user input) is precisely "App.input" or whatever you named your struct member tied to the text box. eframe is an immediate-mode GUI, so the relationship between application state and widget state is 100% alignment, all the time. You access widget state by directly access the member of the context variable you pass around, there are no callbacks to register or anything like that.
Widgets which have not only state but also associated functionality typically have that functionality defined right where the widget is defined. For example, here's a button defined between two text labels:
ui.label("Hello"); if ui.button("Go").clicked() { self.times_clicked += 1; } ui.label("World");
Yeah, the conditional with the block defining "clicked" behavior *is* the definition of the widget. No keeping track of hard-coded IDs, no cross-codebase pointer chasing. Most of my prior GUI work is in Qt, and while Qt is awesome for other reasons, it's definitely hyper-optimized for loosely coupled massive codebases. egui feels natural writing short, comprehensives lists of interactivity and functionality and that is super nice.
Definitely the biggest pitfall I see with eframe is exactly that, though. orbtk's ECS model likely scales to infinity and beyond with no need to modify code structure or widget composition paradigms between widget number 2 and widget number 2,000,000 - egui is procedural enough, and keeps state centralized enough, that it smells like I'd have growing pains beyond the 1,000 lines-of-code mark. That's just an intuition, though; I haven't looked too much into other things in the eframe toolbox for programming in the large. I also haven't gotten the chance to look into bevy-egui, which promises an ECS integration with the Bevy game engine. Depending on how they actually implemented that it could be even awesome-er.
If you're interested in seeing my example of a basic eframe app which partially implements a Spartan browser (including gemtext parser), it's on Sourcehut:
The entire program is about 100 lines, contained in main.rs, plus the two dependencies. I don't recommend making this program your primary Spartan client as plenty of features remain unimplemented (eg, keyboard shortcuts), but it works for making requests and parsing of output.
Rust doesn't have a GUI framework that is as robust, fully-featured, and integrated as Qt, which is not surprising as Qt had a few decades head start. However, with eframe we have something that's really fun to quickly throw together an interactive interface for a small to medium amount of business logic. With orbtk we have the promise of something that could reach Qt-scale, once it has time to develop more.
Given the current landscape, I can't help but feel we aren't yet out of the shadow of the death of Amethyst. DECS, the ECS which orbtk is built on, was apparently created as an alternative to specs (Amethyst's ecs), but everyone who was involved in that transition will tell you that Bevy's ECS is miles ahead of where specs was before even Amethyst abandoned it. Maybe on a long enough time span all this other work will obscelete orbtk, as promising as it seems right now. Looking at the readme for bevy_egui, with the example listed, I'm already imagining a NetHack porcelain combining an egui frontend with a bevy_ascii_terminal render viewport for tile goodness with easier inventory management. Hmmmmmmmmm................