aboutsummaryrefslogtreecommitdiff
path: root/_posts
diff options
context:
space:
mode:
Diffstat (limited to '_posts')
-rw-r--r--_posts/2025-04-13-2025-survey-of-rust-gui-libraries.md108
-rw-r--r--_posts/2025-10-04-job-hunt.md7
2 files changed, 63 insertions, 52 deletions
diff --git a/_posts/2025-04-13-2025-survey-of-rust-gui-libraries.md b/_posts/2025-04-13-2025-survey-of-rust-gui-libraries.md
index 28eaf48..ac2bb75 100644
--- a/_posts/2025-04-13-2025-survey-of-rust-gui-libraries.md
+++ b/_posts/2025-04-13-2025-survey-of-rust-gui-libraries.md
@@ -350,7 +350,10 @@ Per the README, [Freya](https://freyaui.dev/) is “a cross-platform GUI library
[Evidently](https://book.freyaui.dev/differences_with_dioxus.html), it takes the logic and structure of Dioxus and but renders everything itself instead of using Diet Electron.
I did grumble about the Diet Electron-hood of Dioxus, so maybe this is the exact thing I was hoping for all along.
-Freya’s latest stable release depends on the prior minor version of Dioxus, which may or may not have a slightly different `rsx!` macro (Dioxus 0.6 uses `rsx! {}` but Freya’s Dioxus 0.5 uses `rsx!()`, and I forget if that’s actually different or not), but this still looks a lot like our Dioxus code:
+*Edit 2025-04-15*: I’ve been asked to run this again with the week-old release candidate rather than the year-old latest stable release, and I’ve ignored other year-old latest stable releases so it’s fair to do that here too.
+Next time, unless I forget to, I’m deciding up front when to use a stable release vs an unstable release vs the main branch.
+
+Freya’s README example uses `rsx!()` rather than the Dioxus docs’ `rsx! {}`, and it still feels weird to me that those are interchangeable, but aside from that, the only difference from the Dioxus code is some component names:
```rust
let mut text = use_signal(|| "Hello, world!".to_string());
@@ -365,13 +368,12 @@ rsx!(
![a screenshot of a text label and a text field both saying Hello, world!](/assets/2025-04-13-freya.png)
-Narrator appears to almost understand the structure of this window — it’s seeing that there’s a text edit, at least — but it can’t actually figure out what any of the text actually is.
-The IME activates, but in the wrong place on screen, and neither the provisional kana nor the final kanji actually shows up in the text input.
-Even my WinCompose shortcuts don’t work.
+Narrator sees the text label fine, but something’s gone wacky with the text input: Narrator can see that there’s a text input with those bounds on the screen, but it doesn’t seem like it actually knows the contents of the text input.
+The IME shows up in the corner of the screen, and the provisional states aren’t displayed at all, but the final kanji land correctly in the text input once I accept them from the converter.
There are some drawbacks to rendering things yourself instead of letting Chrome-via-Edge-via-WebView2 do it for you, it seems.
Regardless, it’s extremely cool that Dioxus is set up in a way that makes this possible, and it’s extremely cool that someone’s trying to do it.
-It is not, however, at a point where I would recommend using it.
+I’m not sure that Freya is quite ready for serious use yet, but it’s definitely promising.
## fui
@@ -1048,6 +1050,8 @@ and this is all just misery upon misery.
Half the point of Rust is the sheer quantity of bugs that it can catch at compile time, and if your IPC is just tossing strings around and praying at runtime, you may as well be just writing vanilla JavaScript.
(I checked, and even if your frontend is TypeScript, the `invoke` IPC boundary just takes a `string` rather than a union of the actual legal values.)
+*edit 2026-01-03*: I learned at the [Utah Rust meetup](https://www.meetup.com/utah-rust/) that if you’re writing your frontend in TypeScript you can use [tauri-specta](https://github.com/specta-rs/tauri-specta) to generate type safe IPC wrappers.
+
Hilariously, the Tauri docs [claim](https://tauri.app/develop/calling-rust/) that the command mechanism is “for reaching Rust functions with type safety”, as distinct from their event system, which is even less type safe.
With events, you’re tossing strings and arbitrary JavaScript payloads around in both the frontend and the host, so technically it’s not false to claim that only doing that on one end is more type safe, but type checking only at one end is like putting a lock on your bike but not running it through the bike rack: you aren’t tying two things together, you’re just tying one thing to itself and praying.
Even to the limited extent that half of type safety could be useful, though, they’ve picked the wrong half: there’s inherently only one implementation of the command, but there can be many calls to it, so it’d be far more valuable to have type safety at the call sites than at the implementation site.
@@ -1334,8 +1338,8 @@ Let’s pick some winners.
If you’d rather take the quirks of CSS layout over the quirks of some other layout engine, [Dioxus](#dioxus) seems like a pretty reasonable choice; Diet Electron is definitely better than regular Electron, and that may be good enough for you, but it feels not-better-enough to me (although my sense of not-better-enough is [non-standard](https://en.wikipedia.org/wiki/Obsessive%E2%80%93compulsive_personality_disorder)).
If you like DSL-driven UIs that are putting serious effort into developer tooling, [Slint](#slint) might be for you.
If you want to avoid DSLs and macros and write only regular Rust, [egui](#egui) offers that.
-If you’re looking for something to invest in early, [Xilem](#xilem) is basically usable now if you don’t mind pointing at the Git repo and putting up with some jank.
-There are open issues for improving accessibility in [Floem](https://github.com/lapce/floem/issues/8), [Freya](https://github.com/marc2332/freya/issues/844), and [iced](https://github.com/iced-rs/iced/issues/552), so it may be worth at least keeping an eye on those issues, although the iced issue has been open for 4½ years now.
+If you’re looking for something to invest in early, [Freya](#freya) and [Xilem](#xilem) are both basically usable now if you don’t mind living on the bleeding edge and putting up with some jank.
+There are open issues for improving accessibility in [Floem](https://github.com/lapce/floem/issues/8) and [iced](https://github.com/iced-rs/iced/issues/552), so it may be worth at least keeping an eye on those issues, although the iced issue has been open for 4½ years now.
I would not describe any of these as a super easy slam dunk obviously correct choice, but there are a lot of reasonable options available, and that’s better than 2021, where I was [longing for wxPython](/_posts/2021-10-24-2021-survey-of-rust-gui-libraries.md#whining) by the end of the post.
Maybe better things are possible after all.
@@ -1346,48 +1350,48 @@ Now if you’ll excuse me, I’ve got some `cargo clean`s to run:
## The Table
-| library | works at all? | screen reader accessible? | IME works? |
-|---------------------------------------------|---------------------------|---------------------------|------------------------------------------------------------------------------------------|
-| [Azul](#azul) | linker hell | | |
-| [cacao](#cacao) | macOS-specific | | |
-| [core-foundation](#core-foundation) | macOS-specific | | |
-| [Crux](#crux) | no desktop targets | | |
-| [Cushy](#cushy) | yes! | nope | composer hidden, converter works |
-| [CXX-Qt](#cxx-qt) | linker hell | | |
-| [Dioxus](#dioxus) | yes! | yes! | yes! |
-| [Dominator](#dominator) | web-specific | | |
-| [egui](#egui) | yes! | yes! | composer works, Tab press stolen from converter |
-| [Floem](#floem) | yes! | nope | nope |
-| [fltk](#fltk) | yes! | with extra crate | yes! |
-| [flutter_rust_bridge](#flutter_rust_bridge) | kinda, but state hell | yes! | kinda, but state hell |
-| [Freya](#freya) | yes! | nope | nope |
-| [fui](#fui) | qmake hell | | |
-| [GemGui](#gemgui) | technically | | |
-| [GPUI](#gpui) | yes! | nope | yes! |
-| [GTK 3](#gtk-3) | unmaintained | | |
-| [GTK 4](#gtk-4) | yes! | nope | yes! |
-| [Iced](#iced) | yes! | nope | nope |
-| [imgui](#imgui) | yes! | nope | nope |
-| [KAS](#kas) | yes! | nope | nope |
-| [kittest](#kittest) | only for testing | | |
-| [Leptos](#leptos) | web-specific | | |
-| [lvgl](#lvgl) | C dependency hell | | |
-| [Makepad](#makepad) | yes! | nope | composer outside window, converter works |
-| [masonry](#masonry) | yes! | content but not position | yes! but some temporary tofu |
-| [Maycoon](#maycoon) | no text input widget | | |
-| [Pax](#pax) | no Windows support | | |
-| [qmetaobject](#qmetaobject) | no windows-msvc | | |
-| [relm](#relm) | uses unmaintained GTK 3 | | |
-| [Relm4](#relm4) | yes! | nope | yes! |
-| [Ribir](#ribir) | kinda, but state hell | nope | composer hidden, converter works |
-| [Rinf](#rinf) | does not use Rust for GUI | | |
-| [rui](#rui) | yes! | nope | nope |
-| [Slint](#slint) | yes! | yes! | missing glyphs in provisional states but logic works and final kanji displayed correctly |
-| [Tauri](#tauri) | yes! | yes! | composer outside window, converter works |
-| [tinyfiledialogs](#tinyfiledialogs) | not general-purpose | | |
-| [Tk](#tk) | yes! | nope | yes! |
-| [Vizia](#vizia) | yes! | structure but not content | converter outside window, everything works |
-| [WebRender](#webrender) | too low-level | | |
-| [windows](#windows) | i don’t know Win32 | | |
-| [WinSafe](#winsafe) | yes! | yes! | yes! |
-| [Xilem](#xilem) | yes! | content but not position | yes! but some temporary tofu |
+| library | works at all? | screen reader accessible? | IME works? |
+|---------------------------------------------|---------------------------|----------------------------------|------------------------------------------------------------------------------------------|
+| [Azul](#azul) | linker hell | | |
+| [cacao](#cacao) | macOS-specific | | |
+| [core-foundation](#core-foundation) | macOS-specific | | |
+| [Crux](#crux) | no desktop targets | | |
+| [Cushy](#cushy) | yes! | nope | composer hidden, converter works |
+| [CXX-Qt](#cxx-qt) | linker hell | | |
+| [Dioxus](#dioxus) | yes! | yes! | yes! |
+| [Dominator](#dominator) | web-specific | | |
+| [egui](#egui) | yes! | yes! | composer works, Tab press stolen from converter |
+| [Floem](#floem) | yes! | nope | nope |
+| [fltk](#fltk) | yes! | with extra crate | yes! |
+| [flutter_rust_bridge](#flutter_rust_bridge) | kinda, but state hell | yes! | kinda, but state hell |
+| [Freya](#freya) | yes! | mostly, but some content missing | composer hidden, converter works |
+| [fui](#fui) | qmake hell | | |
+| [GemGui](#gemgui) | technically | | |
+| [GPUI](#gpui) | yes! | nope | yes! |
+| [GTK 3](#gtk-3) | unmaintained | | |
+| [GTK 4](#gtk-4) | yes! | nope | yes! |
+| [Iced](#iced) | yes! | nope | nope |
+| [imgui](#imgui) | yes! | nope | nope |
+| [KAS](#kas) | yes! | nope | nope |
+| [kittest](#kittest) | only for testing | | |
+| [Leptos](#leptos) | web-specific | | |
+| [lvgl](#lvgl) | C dependency hell | | |
+| [Makepad](#makepad) | yes! | nope | composer outside window, converter works |
+| [masonry](#masonry) | yes! | content but not position | yes! but some temporary tofu |
+| [Maycoon](#maycoon) | no text input widget | | |
+| [Pax](#pax) | no Windows support | | |
+| [qmetaobject](#qmetaobject) | no windows-msvc | | |
+| [relm](#relm) | uses unmaintained GTK 3 | | |
+| [Relm4](#relm4) | yes! | nope | yes! |
+| [Ribir](#ribir) | kinda, but state hell | nope | composer hidden, converter works |
+| [Rinf](#rinf) | does not use Rust for GUI | | |
+| [rui](#rui) | yes! | nope | nope |
+| [Slint](#slint) | yes! | yes! | missing glyphs in provisional states but logic works and final kanji displayed correctly |
+| [Tauri](#tauri) | yes! | yes! | composer outside window, converter works |
+| [tinyfiledialogs](#tinyfiledialogs) | not general-purpose | | |
+| [Tk](#tk) | yes! | nope | yes! |
+| [Vizia](#vizia) | yes! | structure but not content | converter outside window, everything works |
+| [WebRender](#webrender) | too low-level | | |
+| [windows](#windows) | i don’t know Win32 | | |
+| [WinSafe](#winsafe) | yes! | yes! | yes! |
+| [Xilem](#xilem) | yes! | content but not position | yes! but some temporary tofu |
diff --git a/_posts/2025-10-04-job-hunt.md b/_posts/2025-10-04-job-hunt.md
new file mode 100644
index 0000000..f8743c9
--- /dev/null
+++ b/_posts/2025-10-04-job-hunt.md
@@ -0,0 +1,7 @@
+---
+title: I’m Job Hunting
+---
+
+My employer’s remote work policies are changing, and Salt Lake City isn’t “commutable distance” from Boston, so I’m looking for a new job. If you’re hiring software developers (remote in the US, or hybrid in Salt Lake City) to work on something that’s prosocial and technically ambitious, [let me know](mailto:melody@boringcactus.com).
+
+I’ve done backend, frontend, mobile, devops, internal tooling, release engineering, CI, and more. I’m incredibly flexible, and I learn fast, so if you have a mission I can believe in, I can be effective and comfortable in any role and any language and tech stack.