gpui: Add run_embedded and ApplicationHandle for externally driven run loops (#60574)

On ordinary platforms, `Platform::run` blocks for the lifetime of the
app, and `Application::run`'s stack frame keeps the app state alive.
Embedded platforms invert that: the run loop belongs to someone else.

`Application::run_embedded` supports that shape: it starts the app
exactly like `run()`, but returns an `ApplicationHandle` holding the
strong app handle.

Release Notes:

- N/A
This commit is contained in:
Mikayla Maki authored and GitHub committed 2026-07-08 15:46:41 +00:00
1 parent 883bb315fd
commit 244a8d349c
1 file changed
+47
+47
View File
@@ -143,6 +143,31 @@ impl Drop for AppRefMut<'_> {
/// You won't interact with this type much outside of initial configuration and startup.
pub struct Application(Rc<AppCell>);
/// A strong handle to an [`Application`] started with [`Application::run_embedded`].
///
/// Dropping this handle releases the app, so an embedder must hold it for as long as the
/// app should run. While held, it is the embedder's entry point back into GPUI each time
/// the external run loop gives it control.
pub struct ApplicationHandle {
app: Rc<AppCell>,
}
impl ApplicationHandle {
/// Invoke `f` with the app context. Must not be called re-entrantly from code that
/// is already inside an update; the app state is a `RefCell` and will panic on a
/// double borrow.
pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> R {
let cx = &mut *self.app.borrow_mut();
f(cx)
}
/// An [`AsyncApp`] for use across await points. It holds the app weakly; keeping the
/// app alive remains this handle's job.
pub fn to_async(&self) -> AsyncApp {
self.update(|cx| cx.to_async())
}
}
/// Represents an application before it is fully launched. Once your app is
/// configured, you'll start the app with `App::run`.
impl Application {
@@ -209,6 +234,28 @@ impl Application {
}));
}
/// Start the application for an embedder that drives the run loop itself.
///
/// On ordinary platforms `Platform::run` blocks for the lifetime of the app, and the
/// app state is kept alive by [`Application::run`]'s stack frame. Embedded platforms —
/// where the run loop belongs to someone else, e.g. GPUI compiled into a Wasm guest,
/// or a GPUI view hosted inside a foreign native application — implement
/// `Platform::run` to invoke the launch callback and return immediately. This method
/// supports that shape: it returns an [`ApplicationHandle`] that keeps the app alive
/// and lets the embedder re-enter it whenever the external run loop yields control.
pub fn run_embedded<F>(self, on_finish_launching: F) -> ApplicationHandle
where
F: 'static + FnOnce(&mut App),
{
let this = self.0.clone();
let platform = self.0.borrow().platform.clone();
platform.run(Box::new(move || {
let cx = &mut *this.borrow_mut();
on_finish_launching(cx);
}));
ApplicationHandle { app: self.0 }
}
/// Register a handler to be invoked when the platform instructs the application
/// to open one or more URLs.
pub fn on_open_urls<F>(&self, mut callback: F) -> &Self