gpui_runtime/application.rs
1//! The [`Application`] type: building an app, configuring it before launch, and
2//! starting the platform run loop that drives it.
3
4use anyhow::Result;
5use gpui_authoring::{
6 App, AppCell, AssetSource, AsyncApp, BackgroundExecutor, ForegroundExecutor, FramePipeline,
7 LayoutEngine, Platform, QuitMode, TextSystem, WindowId, http_client::HttpClient,
8};
9use std::{ffi::OsString, path::PathBuf, rc::Rc, sync::Arc};
10
11/// A reference to a GPUI application, typically constructed in the `main` function of your app.
12/// You won't interact with this type much outside of initial configuration and startup.
13pub struct Application(Rc<AppCell>);
14
15/// A strong handle to an [`Application`] started with [`Application::run_embedded`].
16///
17/// Dropping this handle releases the app, so an embedder must hold it for as long as the
18/// app should run. While held, it is the embedder's entry point back into GPUI each time
19/// the external run loop gives it control.
20pub struct ApplicationHandle {
21 app: Rc<AppCell>,
22}
23
24impl ApplicationHandle {
25 /// Invoke `f` with the app context. Must not be called re-entrantly from code that
26 /// is already inside an update; the app state is a `RefCell` and will panic on a
27 /// double borrow.
28 pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> R {
29 let cx = &mut *self.app.borrow_mut();
30 f(cx)
31 }
32
33 /// An [`AsyncApp`] for use across await points. It holds the app weakly; keeping the
34 /// app alive remains this handle's job.
35 pub fn to_async(&self) -> AsyncApp {
36 self.update(|cx| cx.to_async())
37 }
38}
39
40/// Represents an application before it is fully launched. Once your app is
41/// configured, you'll start the app with `App::run`.
42impl Application {
43 /// Builds an app with a caller-provided platform implementation.
44 pub fn with_platform(platform: Rc<dyn Platform>) -> Self {
45 Self(App::new_app_for_platform(platform))
46 }
47
48 /// Builds an app with accessibility (AccessKit) integration forcibly
49 /// disabled.
50 ///
51 /// In this mode, accessibility APIs (e.g.
52 /// [`div().role()`][gpui_authoring::StatefulInteractiveElement::role])
53 /// silently no-op.
54 ///
55 /// See the [accessibility guide](gpui_authoring::_accessibility) for an
56 /// overview of the features this disables.
57 pub fn new_inaccessible(platform: Rc<dyn Platform>) -> Self {
58 let this = Self::with_platform(platform);
59 this.0.borrow_mut().set_accessibility_force_disabled(true);
60 this
61 }
62
63 /// Assigns the source of assets for the application.
64 pub fn with_assets(self, asset_source: impl AssetSource) -> Self {
65 self.0.borrow_mut().set_asset_source(Arc::new(asset_source));
66 self
67 }
68
69 /// Configures arguments to pass when restarting the application.
70 pub fn with_restart_arguments(self, arguments: Vec<OsString>) -> Self {
71 self.0.borrow_mut().set_restart_arguments(arguments);
72 self
73 }
74
75 /// Sets the HTTP client for the application.
76 pub fn with_http_client(self, http_client: Arc<dyn HttpClient>) -> Self {
77 self.0.borrow_mut().set_http_client(http_client);
78 self
79 }
80
81 /// Configures when the application should automatically quit.
82 /// By default, [`QuitMode::Default`] is used.
83 pub fn with_quit_mode(self, mode: QuitMode) -> Self {
84 self.0.borrow_mut().set_quit_mode(mode);
85 self
86 }
87
88 /// Sets the factory that creates each window's layout engine.
89 ///
90 /// Defaults to the engine bundled with GPUI. Supply a different
91 /// implementation to swap the layout engine without touching windows.
92 pub fn with_layout_engine(
93 self,
94 layout_engine: impl Fn() -> Box<dyn LayoutEngine> + 'static,
95 ) -> Self {
96 self.0
97 .borrow_mut()
98 .set_layout_engine_factory(Rc::new(layout_engine));
99 self
100 }
101
102 /// Sets the factory that creates each window's frame pipeline.
103 ///
104 /// Defaults to the immediate-mode pipeline bundled with GPUI, which
105 /// re-evaluates the view tree every frame. Supply a different implementation
106 /// to change how a window draws its frames.
107 pub fn with_frame_pipeline(
108 self,
109 frame_pipeline: impl Fn(WindowId) -> Box<dyn FramePipeline> + 'static,
110 ) -> Self {
111 self.0
112 .borrow_mut()
113 .set_frame_pipeline_factory(Rc::new(frame_pipeline));
114 self
115 }
116
117 /// Sets the text system used to shape and lay out text.
118 ///
119 /// Defaults to the text system bundled with GPUI. Supply a different
120 /// implementation to swap the shaper and line layout engine.
121 pub fn with_text_system(self, text_system: Arc<dyn TextSystem>) -> Self {
122 self.0.borrow_mut().set_text_system(text_system);
123 self
124 }
125
126 /// Start the application. The provided callback will be called once the
127 /// app is fully launched.
128 ///
129 /// On WebAssembly, this returns immediately and retains the app for the lifetime
130 /// of the Wasm instance. Use [`Self::run_embedded`] to control its lifetime explicitly.
131 pub fn run<F>(self, on_finish_launching: F)
132 where
133 F: 'static + FnOnce(&mut App),
134 {
135 let this = self.0.clone();
136 let platform = self.0.borrow().platform();
137 platform.run(Box::new(move || {
138 let cx = &mut *this.borrow_mut();
139 on_finish_launching(cx);
140 }));
141
142 #[cfg(target_family = "wasm")]
143 std::mem::forget(self);
144 }
145
146 /// Start the application for an embedder that drives the run loop itself.
147 ///
148 /// On ordinary platforms `Platform::run` blocks for the lifetime of the app, and the
149 /// app state is kept alive by [`Application::run`]'s stack frame. Embedded platforms —
150 /// where the run loop belongs to someone else, e.g. GPUI compiled into a Wasm guest,
151 /// or a GPUI view hosted inside a foreign native application — implement
152 /// `Platform::run` to invoke the launch callback and return immediately. This method
153 /// supports that shape: it returns an [`ApplicationHandle`] that keeps the app alive
154 /// and lets the embedder re-enter it whenever the external run loop yields control.
155 pub fn run_embedded<F>(self, on_finish_launching: F) -> ApplicationHandle
156 where
157 F: 'static + FnOnce(&mut App),
158 {
159 let this = self.0.clone();
160 let platform = self.0.borrow().platform();
161 platform.run(Box::new(move || {
162 let cx = &mut *this.borrow_mut();
163 on_finish_launching(cx);
164 }));
165 ApplicationHandle { app: self.0 }
166 }
167
168 /// Register a handler to be invoked when the platform instructs the application
169 /// to open one or more URLs.
170 pub fn on_open_urls<F>(&self, callback: F) -> &Self
171 where
172 F: 'static + FnMut(Vec<String>),
173 {
174 let platform = self.0.borrow().platform();
175 platform.on_open_urls(Box::new(callback));
176 self
177 }
178
179 /// Invokes a handler when an already-running application is launched.
180 /// On macOS, this can occur when the application icon is double-clicked or the app is launched via the dock.
181 pub fn on_reopen<F>(&self, mut callback: F) -> &Self
182 where
183 F: 'static + FnMut(&mut App),
184 {
185 let this = Rc::downgrade(&self.0);
186 let platform = self.0.borrow().platform();
187 platform.on_reopen(Box::new(move || {
188 if let Some(app) = this.upgrade() {
189 callback(&mut app.borrow_mut());
190 }
191 }));
192 self
193 }
194
195 /// Returns a handle to the [`BackgroundExecutor`] associated with this app, which can be used to spawn futures in the background.
196 pub fn background_executor(&self) -> BackgroundExecutor {
197 self.0.borrow().background_executor().clone()
198 }
199
200 /// Returns a handle to the [`ForegroundExecutor`] associated with this app, which can be used to spawn futures in the foreground.
201 pub fn foreground_executor(&self) -> ForegroundExecutor {
202 self.0.borrow().foreground_executor().clone()
203 }
204
205 /// Returns the app's [`TextSystem`].
206 pub fn text_system(&self) -> Arc<dyn TextSystem> {
207 self.0.borrow().text_system().clone()
208 }
209
210 /// Returns the file URL of the executable with the specified name in the application bundle
211 pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
212 self.0.borrow().path_for_auxiliary_executable(name)
213 }
214}