Skip to content

Other toolkits

There is no gpui in the core crate. Binding any toolkit means answering one question: how do I post a closure onto the main thread? Register that once and everything else in this guide applies unchanged.

rust
// Cx is whatever your toolkit hands out on the main thread; it need not be Send.
let (bridge, app) = GuiBridge::builder(move |task: Box<dyn FnOnce(&mut MyCx) + Send>| {
    my_toolkit::post_to_main_thread(move || {
        let cx = /* obtain &mut MyCx here */;
        task(cx);
    });
})
.build();
ToolkitThe hop
gpuiChannel + a cx.spawn drain loop calling cx.update — packaged as purview-gpui
Slintslint::invoke_from_event_loop; reach widgets via a weak handle
GTKglib::idle_add
eguiEnqueue, drain on repaint

The Send trick

Cx is carried only inside the poster's boxed closure, so GuiBridge<Cx> and every handle stay Send + Sync even when Cx is not. That is what lets the server run on a background thread while your context never leaves the main one.

Reaching the main thread yourself

Outside an on_ui handler — from an async handler, or any background task — hop explicitly:

rust
let ui = app.ui();                        // Ui<Cx>, cheap to clone, Send + Sync
let value = ui.run(|cx| read_widget(cx)).await;

Ui::run posts your closure and awaits its return value. It panics if the main loop has already exited, which is the honest outcome: there is no context left to run against.