1use scheduler::Instant;
2use std::{
3 any::{TypeId, type_name},
4 cell::{BorrowMutError, Cell, Ref, RefCell, RefMut},
5 ffi::OsString,
6 marker::PhantomData,
7 mem,
8 ops::{Deref, DerefMut},
9 path::{Path, PathBuf},
10 rc::{Rc, Weak},
11 sync::Arc,
12 time::Duration,
13};
14
15use anyhow::{Context as _, Result, anyhow};
16use derive_more::{Deref, DerefMut};
17use futures::{Future, FutureExt, channel::oneshot, future::LocalBoxFuture};
18use itertools::Itertools;
19use parking_lot::RwLock;
20use slotmap::SlotMap;
21
22pub use async_context::*;
23#[cfg(feature = "bench-support")]
24pub use bench_context::{
25 BenchAppContext, BenchMeasurement, BenchReport, BenchWindowContext, CountingAllocator,
26 MetricReport, bench_platform,
27};
28use collections::{FxHashMap, FxHashSet, HashMap, TypeIdHashMap, TypeIdHashSet, VecDeque};
29pub use context::*;
30pub use entity_map::*;
31use gpui_util::{ResultExt, debug_panic};
32#[cfg(any(test, feature = "test-support"))]
33pub use headless_app_context::*;
34use http_client::{HttpClient, Url};
35use smallvec::SmallVec;
36#[cfg(any(test, feature = "test-support"))]
37pub use test_app::*;
38#[cfg(any(test, feature = "test-support"))]
39pub use test_context::*;
40#[cfg(all(target_os = "macos", any(test, feature = "test-support")))]
41pub use visual_test_context::*;
42
43#[cfg(debug_assertions)]
44use crate::InspectorElementRegistry;
45use crate::asset_cache::CachedLoad;
46use crate::subscription::SingleSubscriberSet;
47use crate::{
48 Action, ActionBuildError, ActionRegistry, ActivationPolicy, ActivityGuard, Any, AnyView,
49 AnyWindowHandle, AppContext, AppLifecyclePhase, Arena, ArenaBox, Asset, AssetSource,
50 BackgroundExecutor, Bounds, ClipboardItem, ClipboardReadError, CursorStyle, DispatchPhase,
51 DisplayChanges, DisplayEvent, DisplayId, EventEmitter, ExternalDragPayload, FocusHandle,
52 FocusMap, ForegroundExecutor, Global, GpuDeviceLost, KeyBinding, KeyContext, Keymap, Keystroke,
53 LayoutId, Menu, MenuItem, MissingGlyph, OwnedMenu, PathPromptOptions, Pixels, Platform,
54 PlatformDisplay, PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformTextSystem, Point,
55 PowerMode, Priority, PromptBuilder, PromptButton, PromptHandle, PromptLevel, Render,
56 RenderImage, RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString,
57 SubscriberSet, Subscription, SvgRenderer, SystemNotification, SystemNotificationResponse, Task,
58 TextRenderingMode, TextSystem, ThermalState, Window, WindowAppearance, WindowButtonLayout,
59 WindowHandle, WindowId, WindowInvalidator, WindowingRequest,
60 colors::{Colors, GlobalColors},
61 hash, init_app_menus,
62};
63
64mod async_context;
65#[cfg(feature = "bench-support")]
66mod bench_context;
67mod context;
68mod entity_map;
69#[cfg(any(test, feature = "test-support"))]
70mod headless_app_context;
71#[cfg(any(test, feature = "test-support"))]
72mod test_app;
73#[cfg(any(test, feature = "test-support"))]
74mod test_context;
75#[cfg(all(target_os = "macos", any(test, feature = "test-support")))]
76mod visual_test_context;
77
78pub const SHUTDOWN_TIMEOUT: Duration = Duration::from_millis(200);
81
82#[doc(hidden)]
85pub struct AppCell {
86 app: RefCell<App>,
87 borrow_origin: Cell<Option<(&'static std::panic::Location<'static>, &'static str)>>,
88}
89
90impl AppCell {
91 #[cold]
92 #[track_caller]
93 fn borrow_failed(&self, kind: &str) -> ! {
94 let attempted = std::panic::Location::caller();
95 if let Some((origin, existing_kind)) = self.borrow_origin.get() {
96 panic!(
97 "RefCell already borrowed: attempted {kind} App borrow at {attempted}; \
98 existing {existing_kind} borrow group began at {origin}"
99 );
100 }
101 panic!(
102 "RefCell already borrowed: attempted {kind} App borrow at {attempted}; origin unavailable"
103 );
104 }
105
106 #[doc(hidden)]
107 #[track_caller]
108 pub fn borrow(&self) -> AppRef<'_> {
109 if option_env!("TRACK_THREAD_BORROWS").is_some() {
110 let thread_id = std::thread::current().id();
111 eprintln!("borrowed {thread_id:?}");
112 }
113 let starts_borrow_group = self.app.try_borrow_mut().is_ok();
116 let borrow = match self.app.try_borrow() {
117 Ok(borrow) => borrow,
118 Err(_) => self.borrow_failed("shared"),
119 };
120 if starts_borrow_group {
121 self.borrow_origin
122 .set(Some((std::panic::Location::caller(), "shared")));
123 }
124 AppRef(borrow)
125 }
126
127 #[doc(hidden)]
128 #[track_caller]
129 pub fn borrow_mut(&self) -> AppRefMut<'_> {
130 match self.try_borrow_mut() {
131 Ok(borrow) => borrow,
132 Err(_) => self.borrow_failed("mutable"),
133 }
134 }
135
136 #[doc(hidden)]
137 #[track_caller]
138 pub fn try_borrow_mut(&self) -> Result<AppRefMut<'_>, BorrowMutError> {
139 if option_env!("TRACK_THREAD_BORROWS").is_some() {
140 let thread_id = std::thread::current().id();
141 eprintln!("borrowed {thread_id:?}");
142 }
143 let borrow = self.app.try_borrow_mut()?;
144 self.borrow_origin
145 .set(Some((std::panic::Location::caller(), "mutable")));
146 Ok(AppRefMut(borrow))
147 }
148}
149
150#[doc(hidden)]
151#[derive(Deref, DerefMut)]
152pub struct AppRef<'a>(Ref<'a, App>);
153
154impl Drop for AppRef<'_> {
155 fn drop(&mut self) {
156 if option_env!("TRACK_THREAD_BORROWS").is_some() {
157 let thread_id = std::thread::current().id();
158 eprintln!("dropped borrow from {thread_id:?}");
159 }
160 }
161}
162
163#[doc(hidden)]
164#[derive(Deref, DerefMut)]
165pub struct AppRefMut<'a>(RefMut<'a, App>);
166
167impl Drop for AppRefMut<'_> {
168 fn drop(&mut self) {
169 if option_env!("TRACK_THREAD_BORROWS").is_some() {
170 let thread_id = std::thread::current().id();
171 eprintln!("dropped {thread_id:?}");
172 }
173 }
174}
175
176pub struct Application(Rc<AppCell>);
179
180pub struct ApplicationHandle {
186 app: Rc<AppCell>,
187}
188
189impl ApplicationHandle {
190 pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> R {
194 let cx = &mut *self.app.borrow_mut();
195 f(cx)
196 }
197
198 pub fn to_async(&self) -> AsyncApp {
201 self.update(|cx| cx.to_async())
202 }
203}
204
205impl Application {
208 pub fn with_platform(platform: Rc<dyn Platform>) -> Self {
210 Self(App::new_app(
211 platform,
212 Arc::new(()),
213 Arc::new(NullHttpClient),
214 ))
215 }
216
217 pub fn with_platform_and_text_system(
222 platform: Rc<dyn Platform>,
223 text_system: Arc<dyn PlatformTextSystem>,
224 ) -> Self {
225 Self(App::new_app_with_text_system(
226 platform,
227 Arc::new(()),
228 Arc::new(NullHttpClient),
229 Some(text_system),
230 ))
231 }
232
233 pub fn new_inaccessible(platform: Rc<dyn Platform>) -> Self {
243 let this = Self::with_platform(platform);
244 this.0.borrow_mut().accessibility_force_disabled = true;
245 this
246 }
247
248 pub fn with_assets(self, asset_source: impl AssetSource) -> Self {
250 let mut context_lock = self.0.borrow_mut();
251 let asset_source = Arc::new(asset_source);
252 context_lock.asset_source = asset_source.clone();
253 context_lock.svg_renderer = SvgRenderer::new(asset_source);
254 drop(context_lock);
255 self
256 }
257
258 pub fn with_restart_arguments(self, arguments: Vec<OsString>) -> Self {
260 self.0.borrow_mut().restart_arguments = arguments;
261 self
262 }
263
264 pub fn with_http_client(self, http_client: Arc<dyn HttpClient>) -> Self {
266 let mut context_lock = self.0.borrow_mut();
267 context_lock.http_client = http_client;
268 drop(context_lock);
269 self
270 }
271
272 pub fn with_quit_mode(self, mode: QuitMode) -> Self {
275 self.0.borrow_mut().quit_mode = mode;
276 self
277 }
278
279 pub fn with_windowing(self, request: WindowingRequest) -> Self {
286 self.0.borrow().platform.set_initial_windowing(request);
287 self
288 }
289
290 pub fn run<F>(self, on_finish_launching: F)
296 where
297 F: 'static + FnOnce(&mut App),
298 {
299 let this = self.0.clone();
300 let platform = self.0.borrow().platform.clone();
301 platform.run(Box::new(move || {
302 let cx = &mut *this.borrow_mut();
303 cx.displays_changed();
305 #[cfg(gpui_hot_reload)]
306 crate::hot_reload::init(cx);
307 on_finish_launching(cx);
308 }));
309
310 #[cfg(target_family = "wasm")]
311 std::mem::forget(self);
312 }
313
314 pub fn run_embedded<F>(self, on_finish_launching: F) -> ApplicationHandle
324 where
325 F: 'static + FnOnce(&mut App),
326 {
327 let this = self.0.clone();
328 let platform = self.0.borrow().platform.clone();
329 platform.run(Box::new(move || {
330 let cx = &mut *this.borrow_mut();
331 cx.displays_changed();
333 #[cfg(gpui_hot_reload)]
334 crate::hot_reload::init(cx);
335 on_finish_launching(cx);
336 }));
337 ApplicationHandle { app: self.0 }
338 }
339
340 pub fn on_open_urls<F>(&self, callback: F) -> &Self
343 where
344 F: 'static + FnMut(Vec<String>),
345 {
346 self.0.borrow().platform.on_open_urls(Box::new(callback));
347 self
348 }
349
350 pub fn on_reopen<F>(&self, mut callback: F) -> &Self
353 where
354 F: 'static + FnMut(&mut App),
355 {
356 let this = Rc::downgrade(&self.0);
357 self.0.borrow_mut().platform.on_reopen(Box::new(move || {
358 if let Some(app) = this.upgrade() {
359 callback(&mut app.borrow_mut());
360 }
361 }));
362 self
363 }
364
365 pub fn background_executor(&self) -> BackgroundExecutor {
367 self.0.borrow().background_executor.clone()
368 }
369
370 pub fn foreground_executor(&self) -> ForegroundExecutor {
372 self.0.borrow().foreground_executor.clone()
373 }
374
375 pub fn text_system(&self) -> Arc<TextSystem> {
377 self.0.borrow().text_system.clone()
378 }
379
380 pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
382 self.0.borrow().path_for_auxiliary_executable(name)
383 }
384}
385
386type Handler = Box<dyn FnMut(&mut App) -> bool + 'static>;
387type AppLifecycleHandler = Box<dyn FnMut(AppLifecyclePhase, &mut App) -> bool + 'static>;
388type DisplayHandler = Box<dyn FnMut(DisplayEvent, &mut App) -> bool + 'static>;
389type GpuDeviceLostHandler = Box<dyn FnMut(&GpuDeviceLost, &mut App) -> bool + 'static>;
390
391#[derive(Clone, Copy, PartialEq)]
393struct DisplayState {
394 bounds: Bounds<Pixels>,
395 refresh_interval: Option<Duration>,
396}
397
398impl DisplayState {
399 fn changes_from(&self, previous: &DisplayState) -> DisplayChanges {
400 let mut changes = DisplayChanges::empty();
401 changes.set(DisplayChanges::BOUNDS, self.bounds != previous.bounds);
402 changes.set(
403 DisplayChanges::REFRESH_INTERVAL,
404 self.refresh_interval != previous.refresh_interval,
405 );
406 changes
407 }
408}
409
410fn read_displays(platform: &dyn Platform) -> HashMap<DisplayId, DisplayState> {
411 platform
412 .displays()
413 .into_iter()
414 .map(|display| {
415 let state = DisplayState {
416 bounds: display.bounds(),
417 refresh_interval: display.refresh_interval(),
418 };
419 (display.id(), state)
420 })
421 .collect()
422}
423
424fn display_events(
426 previous: &HashMap<DisplayId, DisplayState>,
427 current: &HashMap<DisplayId, DisplayState>,
428) -> Vec<DisplayEvent> {
429 let removed = previous
430 .keys()
431 .filter(|id| !current.contains_key(id))
432 .map(|id| DisplayEvent::Removed(*id));
433 let added_or_changed = current.iter().filter_map(|(id, state)| {
434 let Some(previous_state) = previous.get(id) else {
435 return Some(DisplayEvent::Added(*id));
436 };
437 let changes = state.changes_from(previous_state);
438 (!changes.is_empty()).then_some(DisplayEvent::Changed { id: *id, changes })
439 });
440 removed.chain(added_or_changed).collect()
441}
442type Listener = Box<dyn FnMut(&dyn Any, &mut App) -> bool + 'static>;
443type MissingGlyphCallback = Box<dyn FnMut(&[MissingGlyph], &mut App) + 'static>;
444pub(crate) type KeystrokeObserver =
445 Box<dyn FnMut(&KeystrokeEvent, &mut Window, &mut App) -> bool + 'static>;
446type QuitHandler = Box<dyn FnOnce(&mut App) -> LocalBoxFuture<'static, ()> + 'static>;
447type WindowClosedHandler = Box<dyn FnMut(&mut App, WindowId)>;
448type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut App) + 'static>;
449type NewEntityListener = Box<dyn FnMut(AnyEntity, &mut Option<&mut Window>, &mut App) + 'static>;
450
451struct MissingGlyphCallbackEntry {
452 registration: Rc<()>,
453 callback: Option<MissingGlyphCallback>,
454}
455
456#[derive(Default)]
457struct MissingGlyphCallbackSlot {
458 entry: RefCell<Option<MissingGlyphCallbackEntry>>,
459}
460
461impl MissingGlyphCallbackSlot {
462 fn replace(&self, callback: MissingGlyphCallback) -> Rc<()> {
463 let registration = Rc::new(());
464 self.entry.borrow_mut().replace(MissingGlyphCallbackEntry {
465 registration: registration.clone(),
466 callback: Some(callback),
467 });
468 registration
469 }
470
471 fn invoke(&self, missing_glyphs: &[MissingGlyph], cx: &mut App) {
472 let Some((registration, mut callback)) =
473 self.entry.borrow_mut().as_mut().and_then(|entry| {
474 entry
475 .callback
476 .take()
477 .map(|callback| (entry.registration.clone(), callback))
478 })
479 else {
480 return;
481 };
482 callback(missing_glyphs, cx);
483
484 let mut entry = self.entry.borrow_mut();
485 let is_current = entry
486 .as_ref()
487 .is_some_and(|entry| Rc::ptr_eq(&entry.registration, ®istration));
488 if is_current {
489 let Some(entry) = entry.as_mut() else {
490 return;
491 };
492 entry.callback = Some(callback);
493 }
494 }
495
496 fn remove(&self, registration: &Rc<()>) -> bool {
497 let mut entry = self.entry.borrow_mut();
498 let is_current = entry
499 .as_ref()
500 .is_some_and(|entry| Rc::ptr_eq(&entry.registration, registration));
501 if is_current {
502 entry.take();
503 }
504 is_current
505 }
506}
507
508#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
510pub enum QuitMode {
511 #[default]
513 Default,
514 LastWindowClosed,
516 Explicit,
518}
519
520#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
525pub enum CursorHideMode {
526 Never,
528 OnTyping,
530 #[default]
533 OnTypingAndAction,
534}
535
536#[doc(hidden)]
537#[derive(Clone, PartialEq, Eq)]
538pub struct SystemWindowTab {
539 pub id: WindowId,
540 pub title: SharedString,
541 pub handle: AnyWindowHandle,
542 pub last_active_at: Instant,
543}
544
545impl SystemWindowTab {
546 pub fn new(title: SharedString, handle: AnyWindowHandle) -> Self {
548 Self {
549 id: handle.id,
550 title,
551 handle,
552 last_active_at: Instant::now(),
553 }
554 }
555}
556
557#[derive(Default)]
559pub struct SystemWindowTabController {
560 visible: Option<bool>,
561 tab_groups: FxHashMap<usize, Vec<SystemWindowTab>>,
562}
563
564impl Global for SystemWindowTabController {}
565
566impl SystemWindowTabController {
567 pub fn new() -> Self {
569 Self {
570 visible: None,
571 tab_groups: FxHashMap::default(),
572 }
573 }
574
575 pub fn init(cx: &mut App) {
577 cx.set_global(SystemWindowTabController::new());
578 }
579
580 pub fn tab_groups(&self) -> &FxHashMap<usize, Vec<SystemWindowTab>> {
582 &self.tab_groups
583 }
584
585 pub fn get_next_tab_group_window(cx: &mut App, id: WindowId) -> Option<&AnyWindowHandle> {
587 let controller = cx.global::<SystemWindowTabController>();
588 let current_group = controller
589 .tab_groups
590 .iter()
591 .find_map(|(group, tabs)| tabs.iter().find(|tab| tab.id == id).map(|_| group));
592
593 let current_group = current_group?;
594 let group_ids: Vec<_> = controller.tab_groups.keys().collect();
596 let idx = group_ids.iter().position(|g| *g == current_group)?;
597 let next_idx = (idx + 1) % group_ids.len();
598
599 controller
600 .tab_groups
601 .get(group_ids[next_idx])
602 .and_then(|tabs| {
603 tabs.iter()
604 .max_by_key(|tab| tab.last_active_at)
605 .or_else(|| tabs.first())
606 .map(|tab| &tab.handle)
607 })
608 }
609
610 pub fn get_prev_tab_group_window(cx: &mut App, id: WindowId) -> Option<&AnyWindowHandle> {
612 let controller = cx.global::<SystemWindowTabController>();
613 let current_group = controller
614 .tab_groups
615 .iter()
616 .find_map(|(group, tabs)| tabs.iter().find(|tab| tab.id == id).map(|_| group));
617
618 let current_group = current_group?;
619 let group_ids: Vec<_> = controller.tab_groups.keys().collect();
621 let idx = group_ids.iter().position(|g| *g == current_group)?;
622 let prev_idx = if idx == 0 {
623 group_ids.len() - 1
624 } else {
625 idx - 1
626 };
627
628 controller
629 .tab_groups
630 .get(group_ids[prev_idx])
631 .and_then(|tabs| {
632 tabs.iter()
633 .max_by_key(|tab| tab.last_active_at)
634 .or_else(|| tabs.first())
635 .map(|tab| &tab.handle)
636 })
637 }
638
639 pub fn tabs(&self, id: WindowId) -> Option<&Vec<SystemWindowTab>> {
641 self.tab_groups
642 .values()
643 .find(|tabs| tabs.iter().any(|tab| tab.id == id))
644 }
645
646 pub fn init_visible(cx: &mut App, visible: bool) {
648 let controller = cx.global_mut::<SystemWindowTabController>();
649 if controller.visible.is_none() {
650 controller.visible = Some(visible);
651 }
652 }
653
654 pub fn is_visible(&self) -> bool {
656 self.visible.unwrap_or(false)
657 }
658
659 pub fn set_visible(cx: &mut App, visible: bool) {
661 let controller = cx.global_mut::<SystemWindowTabController>();
662 controller.visible = Some(visible);
663 }
664
665 pub fn update_last_active(cx: &mut App, id: WindowId) {
667 let controller = cx.global_mut::<SystemWindowTabController>();
668 for windows in controller.tab_groups.values_mut() {
669 for tab in windows.iter_mut() {
670 if tab.id == id {
671 tab.last_active_at = Instant::now();
672 }
673 }
674 }
675 }
676
677 pub fn update_tab_position(cx: &mut App, id: WindowId, ix: usize) {
679 let controller = cx.global_mut::<SystemWindowTabController>();
680 for (_, windows) in controller.tab_groups.iter_mut() {
681 if let Some(current_pos) = windows.iter().position(|tab| tab.id == id) {
682 if ix < windows.len() && current_pos != ix {
683 let window_tab = windows.remove(current_pos);
684 windows.insert(ix, window_tab);
685 }
686 break;
687 }
688 }
689 }
690
691 pub fn update_tab_title(cx: &mut App, id: WindowId, title: SharedString) {
693 let controller = cx.global::<SystemWindowTabController>();
694 let tab = controller
695 .tab_groups
696 .values()
697 .flat_map(|windows| windows.iter())
698 .find(|tab| tab.id == id);
699
700 if tab.map_or(true, |t| t.title == title) {
701 return;
702 }
703
704 let controller = cx.global_mut::<SystemWindowTabController>();
705 for windows in controller.tab_groups.values_mut() {
706 for tab in windows.iter_mut() {
707 if tab.id == id {
708 tab.title = title;
709 return;
710 }
711 }
712 }
713 }
714
715 pub fn add_tab(cx: &mut App, id: WindowId, tabs: Vec<SystemWindowTab>) {
717 let controller = cx.global_mut::<SystemWindowTabController>();
718 let Some(tab) = tabs.iter().find(|tab| tab.id == id).cloned() else {
719 return;
720 };
721
722 let expected_tab_ids: Vec<_> = tabs
723 .iter()
724 .filter(|tab| tab.id != id)
725 .map(|tab| tab.id)
726 .sorted()
727 .collect();
728
729 let mut tab_group_id = None;
730 for (group_id, group_tabs) in &controller.tab_groups {
731 let tab_ids: Vec<_> = group_tabs.iter().map(|tab| tab.id).sorted().collect();
732 if tab_ids == expected_tab_ids {
733 tab_group_id = Some(*group_id);
734 break;
735 }
736 }
737
738 if let Some(tab_group_id) = tab_group_id {
739 if let Some(tabs) = controller.tab_groups.get_mut(&tab_group_id) {
740 tabs.push(tab);
741 }
742 } else {
743 let new_group_id = controller.tab_groups.len();
744 controller.tab_groups.insert(new_group_id, tabs);
745 }
746 }
747
748 pub fn remove_tab(cx: &mut App, id: WindowId) -> Option<SystemWindowTab> {
750 let controller = cx.global_mut::<SystemWindowTabController>();
751 let mut removed_tab = None;
752
753 controller.tab_groups.retain(|_, tabs| {
754 if let Some(pos) = tabs.iter().position(|tab| tab.id == id) {
755 removed_tab = Some(tabs.remove(pos));
756 }
757 !tabs.is_empty()
758 });
759
760 removed_tab
761 }
762
763 pub fn move_tab_to_new_window(cx: &mut App, id: WindowId) {
765 let removed_tab = Self::remove_tab(cx, id);
766 let controller = cx.global_mut::<SystemWindowTabController>();
767
768 if let Some(tab) = removed_tab {
769 let new_group_id = controller.tab_groups.keys().max().map_or(0, |k| k + 1);
770 controller.tab_groups.insert(new_group_id, vec![tab]);
771 }
772 }
773
774 pub fn merge_all_windows(cx: &mut App, id: WindowId) {
776 let controller = cx.global_mut::<SystemWindowTabController>();
777 let Some(initial_tabs) = controller.tabs(id) else {
778 return;
779 };
780
781 let initial_tabs_len = initial_tabs.len();
782 let mut all_tabs = initial_tabs.clone();
783
784 for (_, mut tabs) in controller.tab_groups.drain() {
785 tabs.retain(|tab| !all_tabs[..initial_tabs_len].contains(tab));
786 all_tabs.extend(tabs);
787 }
788
789 controller.tab_groups.insert(0, all_tabs);
790 }
791
792 pub fn select_next_tab(cx: &mut App, id: WindowId) {
794 let controller = cx.global_mut::<SystemWindowTabController>();
795 let Some(tabs) = controller.tabs(id) else {
796 return;
797 };
798
799 let current_index = tabs.iter().position(|tab| tab.id == id).unwrap();
800 let next_index = (current_index + 1) % tabs.len();
801
802 let _ = &tabs[next_index].handle.update(cx, |_, window, _| {
803 window.activate_window();
804 });
805 }
806
807 pub fn select_previous_tab(cx: &mut App, id: WindowId) {
809 let controller = cx.global_mut::<SystemWindowTabController>();
810 let Some(tabs) = controller.tabs(id) else {
811 return;
812 };
813
814 let current_index = tabs.iter().position(|tab| tab.id == id).unwrap();
815 let previous_index = if current_index == 0 {
816 tabs.len() - 1
817 } else {
818 current_index - 1
819 };
820
821 let _ = &tabs[previous_index].handle.update(cx, |_, window, _| {
822 window.activate_window();
823 });
824 }
825}
826
827pub(crate) enum GpuiMode {
828 #[cfg(any(test, feature = "test-support"))]
829 Test {
830 skip_drawing: bool,
831 },
832 Production,
833}
834
835impl GpuiMode {
836 #[cfg(any(test, feature = "test-support"))]
837 pub fn test() -> Self {
838 GpuiMode::Test {
839 skip_drawing: false,
840 }
841 }
842
843 #[inline]
844 pub(crate) fn skip_drawing(&self) -> bool {
845 match self {
846 #[cfg(any(test, feature = "test-support"))]
847 GpuiMode::Test { skip_drawing } => *skip_drawing,
848 GpuiMode::Production => false,
849 }
850 }
851}
852
853struct PlatformOwnedDrag {
854 source_window: WindowId,
855 state: PlatformOwnedDragState,
856}
857
858enum PlatformOwnedDragState {
859 Suspended(AnyDrag),
860 RestoredInSourceWindow,
863}
864
865pub struct App {
869 pub(crate) this: Weak<AppCell>,
870 pub(crate) platform: Rc<dyn Platform>,
871 text_system: Arc<TextSystem>,
872
873 pub(crate) actions: Rc<ActionRegistry>,
874 pub(crate) active_drag: Option<AnyDrag>,
875 platform_owned_drag: Option<PlatformOwnedDrag>,
876 pub(crate) background_executor: BackgroundExecutor,
877 pub(crate) foreground_executor: ForegroundExecutor,
878 #[cfg(feature = "profiler")]
879 foreground_journal: crate::profiler::journal::ForegroundJournal,
880 #[cfg(all(feature = "profiler", not(target_family = "wasm")))]
881 hang_monitor: Option<crate::profiler::hang::HangMonitor>,
882 pub(crate) entities: EntityMap,
883 pub(crate) new_entity_observers: SubscriberSet<TypeId, NewEntityListener>,
884 pub(crate) windows: SlotMap<WindowId, Option<Box<Window>>>,
885 pub(crate) window_handles: FxHashMap<WindowId, AnyWindowHandle>,
886 pub(crate) focus_handles: Arc<RwLock<FocusMap>>,
887 pub(crate) keymap: Rc<RefCell<Keymap>>,
888 pub(crate) keyboard_layout: Box<dyn PlatformKeyboardLayout>,
889 pub(crate) keyboard_mapper: Rc<dyn PlatformKeyboardMapper>,
890 pub(crate) global_action_listeners:
891 TypeIdHashMap<Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
892 pending_effects: VecDeque<Effect>,
893
894 pub(crate) observers: SubscriberSet<EntityId, Handler>,
895 pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>,
896 pub(crate) keystroke_observers: SingleSubscriberSet<KeystrokeObserver>,
897 pub(crate) keystroke_interceptors: SingleSubscriberSet<KeystrokeObserver>,
898 pub(crate) keyboard_layout_observers: SingleSubscriberSet<Handler>,
899 missing_glyph_callback: Rc<MissingGlyphCallbackSlot>,
900 pub(crate) thermal_state_observers: SingleSubscriberSet<Handler>,
901 desktop_preferences_observers: SingleSubscriberSet<Handler>,
902 pub(crate) display_observers: SingleSubscriberSet<DisplayHandler>,
903 gpu_device_lost_observers: SingleSubscriberSet<GpuDeviceLostHandler>,
904 displays: HashMap<DisplayId, DisplayState>,
907 pub(crate) system_sleep_observers: SingleSubscriberSet<Handler>,
908 pub(crate) system_wake_observers: SingleSubscriberSet<Handler>,
909 app_lifecycle_observers: SingleSubscriberSet<AppLifecycleHandler>,
910 memory_warning_observers: SingleSubscriberSet<Handler>,
911 pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>,
912 pub(crate) global_observers: SubscriberSet<TypeId, Handler>,
913 pub(crate) quit_observers: SingleSubscriberSet<QuitHandler>,
914 pub(crate) restart_observers: SingleSubscriberSet<Handler>,
915 pub(crate) window_closed_observers: SingleSubscriberSet<WindowClosedHandler>,
916
917 pub(crate) element_arena: RefCell<Arena>,
920 pub(crate) event_arena: Arena,
922
923 pub(crate) globals_by_type: TypeIdHashMap<Box<dyn Any>>,
928
929 pub(crate) loading_assets: FxHashMap<(TypeId, u64), Box<dyn Any>>,
931 asset_source: Arc<dyn AssetSource>,
932 pub(crate) svg_renderer: SvgRenderer,
933 http_client: Arc<dyn HttpClient>,
934
935 pub(crate) pending_notifications: FxHashSet<EntityId>,
937 pub(crate) pending_global_notifications: TypeIdHashSet,
938 pub(crate) restart_path: Option<PathBuf>,
939 pub(crate) restart_arguments: Vec<OsString>,
940 pub(crate) layout_id_buffer: Vec<LayoutId>, pub(crate) propagate_event: bool,
942 pub(crate) prompt_builder: Option<PromptBuilder>,
943 pub(crate) window_invalidators_by_entity:
944 FxHashMap<EntityId, FxHashMap<WindowId, WindowInvalidator>>,
945 pub(crate) tracked_entities: FxHashMap<WindowId, FxHashSet<EntityId>>,
946 pub(crate) current_window_by_entity: FxHashMap<EntityId, WindowId>,
947 #[cfg(debug_assertions)]
948 pub(crate) inspector_renderer: Option<crate::InspectorRenderer>,
949 #[cfg(debug_assertions)]
950 pub(crate) inspector_element_registry: InspectorElementRegistry,
951 #[cfg(any(test, feature = "test-support", debug_assertions))]
952 pub(crate) name: Option<&'static str>,
953 pub(crate) text_rendering_mode: Rc<Cell<TextRenderingMode>>,
954
955 pub(crate) window_update_stack: Vec<WindowId>,
956 pub(crate) mode: GpuiMode,
957 pub(crate) cursor_hide_mode: CursorHideMode,
958 pub(crate) reduce_motion: bool,
959 pub(crate) synced_animation_epoch: Instant,
961 power_mode: PowerMode,
962 pub(crate) accessibility_force_disabled: bool,
965 pending_updates: usize,
966 quit_mode: QuitMode,
967 quitting: bool,
968
969 #[cfg(any(test, gpui_leak_detection))]
972 _ref_counts: Arc<RwLock<EntityRefCounts>>,
973}
974
975impl App {
976 #[allow(clippy::new_ret_no_self)]
977 pub(crate) fn new_app(
978 platform: Rc<dyn Platform>,
979 asset_source: Arc<dyn AssetSource>,
980 http_client: Arc<dyn HttpClient>,
981 ) -> Rc<AppCell> {
982 Self::new_app_with_text_system(platform, asset_source, http_client, None)
983 }
984
985 fn new_app_with_text_system(
986 platform: Rc<dyn Platform>,
987 asset_source: Arc<dyn AssetSource>,
988 http_client: Arc<dyn HttpClient>,
989 text_override: Option<Arc<dyn PlatformTextSystem>>,
990 ) -> Rc<AppCell> {
991 let background_executor = platform.background_executor();
992 let foreground_executor = platform.foreground_executor();
993 assert!(
994 background_executor.is_main_thread(),
995 "must construct App on main thread"
996 );
997 #[cfg(feature = "profiler")]
998 let foreground_journal = crate::profiler::journal::install_foreground_journal();
999 let synced_animation_epoch = background_executor.now();
1000
1001 let backend = text_override.unwrap_or_else(|| platform.text_system());
1002 let text_system = Arc::new(TextSystem::new(backend));
1003 let entities = EntityMap::new();
1004 let keyboard_layout = platform.keyboard_layout();
1005 let keyboard_mapper = platform.keyboard_mapper();
1006 let power_mode = platform.power_mode();
1007
1008 #[cfg(any(test, gpui_leak_detection))]
1009 let _ref_counts = entities.ref_counts_drop_handle();
1010
1011 let app = Rc::new_cyclic(|this| AppCell {
1012 borrow_origin: Cell::new(None),
1013 app: RefCell::new(App {
1014 this: this.clone(),
1015 platform: platform.clone(),
1016 text_system,
1017 text_rendering_mode: Rc::new(Cell::new(TextRenderingMode::default())),
1018 mode: GpuiMode::Production,
1019 actions: Rc::new(ActionRegistry::default()),
1020 pending_updates: 0,
1021 active_drag: None,
1022 platform_owned_drag: None,
1023 background_executor,
1024 foreground_executor,
1025 #[cfg(feature = "profiler")]
1026 foreground_journal,
1027 #[cfg(all(feature = "profiler", not(target_family = "wasm")))]
1028 hang_monitor: None,
1029 svg_renderer: SvgRenderer::new(asset_source.clone()),
1030 loading_assets: Default::default(),
1031 asset_source,
1032 http_client,
1033 globals_by_type: Default::default(),
1034 entities,
1035 new_entity_observers: SubscriberSet::new(),
1036 windows: SlotMap::with_key(),
1037 window_update_stack: Vec::new(),
1038 window_handles: FxHashMap::default(),
1039 focus_handles: Arc::new(RwLock::new(FocusMap::default())),
1040 keymap: Rc::new(RefCell::new(Keymap::default())),
1041 keyboard_layout,
1042 keyboard_mapper,
1043 global_action_listeners: Default::default(),
1044 pending_effects: VecDeque::new(),
1045 pending_notifications: FxHashSet::default(),
1046 pending_global_notifications: Default::default(),
1047 observers: SubscriberSet::new(),
1048 tracked_entities: FxHashMap::default(),
1049 window_invalidators_by_entity: FxHashMap::default(),
1050 current_window_by_entity: FxHashMap::default(),
1051 event_listeners: SubscriberSet::new(),
1052 release_listeners: SubscriberSet::new(),
1053 keystroke_observers: SingleSubscriberSet::new(),
1054 keystroke_interceptors: SingleSubscriberSet::new(),
1055 keyboard_layout_observers: SingleSubscriberSet::new(),
1056 missing_glyph_callback: Rc::default(),
1057 thermal_state_observers: SingleSubscriberSet::new(),
1058 desktop_preferences_observers: SingleSubscriberSet::new(),
1059 display_observers: SingleSubscriberSet::new(),
1060 gpu_device_lost_observers: SingleSubscriberSet::new(),
1061 displays: read_displays(platform.as_ref()),
1062 system_sleep_observers: SingleSubscriberSet::new(),
1063 system_wake_observers: SingleSubscriberSet::new(),
1064 app_lifecycle_observers: SingleSubscriberSet::new(),
1065 memory_warning_observers: SingleSubscriberSet::new(),
1066 global_observers: SubscriberSet::new(),
1067 quit_observers: SingleSubscriberSet::new(),
1068 restart_observers: SingleSubscriberSet::new(),
1069 restart_path: None,
1070 restart_arguments: Vec::new(),
1071 window_closed_observers: SingleSubscriberSet::new(),
1072 layout_id_buffer: Default::default(),
1073 propagate_event: true,
1074 prompt_builder: Some(PromptBuilder::Default),
1075 #[cfg(debug_assertions)]
1076 inspector_renderer: None,
1077 #[cfg(debug_assertions)]
1078 inspector_element_registry: InspectorElementRegistry::default(),
1079 quit_mode: QuitMode::default(),
1080 quitting: false,
1081 cursor_hide_mode: CursorHideMode::default(),
1082 reduce_motion: false,
1083 synced_animation_epoch,
1084 power_mode,
1085 accessibility_force_disabled: false,
1086
1087 #[cfg(any(test, feature = "test-support", debug_assertions))]
1088 name: None,
1089 element_arena: RefCell::new(Arena::new(1024 * 1024)),
1090 event_arena: Arena::new(1024 * 1024),
1091
1092 #[cfg(any(test, gpui_leak_detection))]
1093 _ref_counts,
1094 }),
1095 });
1096
1097 init_app_menus(platform.as_ref(), &app.borrow());
1098 SystemWindowTabController::init(&mut app.borrow_mut());
1099 #[cfg(feature = "profiler")]
1100 crate::profiler::journal::observe_power(&app.borrow());
1101
1102 platform.on_keyboard_layout_change(Box::new({
1103 let app = Rc::downgrade(&app);
1104 move || {
1105 if let Some(app) = app.upgrade() {
1106 let cx = &mut app.borrow_mut();
1107 cx.keyboard_layout = cx.platform.keyboard_layout();
1108 cx.keyboard_mapper = cx.platform.keyboard_mapper();
1109 cx.keyboard_layout_observers
1110 .clone()
1111 .retain(move |callback| (callback)(cx));
1112 }
1113 }
1114 }));
1115
1116 platform.on_thermal_state_change(Box::new({
1117 let app = Rc::downgrade(&app);
1118 move || {
1119 if let Some(app) = app.upgrade() {
1120 let cx = &mut app.borrow_mut();
1121 cx.thermal_state_observers
1122 .clone()
1123 .retain(move |callback| (callback)(cx));
1124 }
1125 }
1126 }));
1127
1128 platform.on_desktop_preferences_changed(Box::new({
1129 let app = Rc::downgrade(&app);
1130 move || {
1131 if let Some(app) = app.upgrade() {
1132 app.borrow_mut().update(|cx| {
1133 cx.desktop_preferences_observers
1134 .clone()
1135 .retain(|callback| callback(cx));
1136 });
1137 }
1138 }
1139 }));
1140
1141 platform.on_displays_changed(Box::new({
1142 let app = Rc::downgrade(&app);
1143 move || {
1144 if let Some(app) = app.upgrade() {
1145 app.borrow_mut().displays_changed();
1146 }
1147 }
1148 }));
1149
1150 platform.on_system_sleep(Box::new({
1151 let app = Rc::downgrade(&app);
1152 move || {
1153 if let Some(app) = app.upgrade() {
1154 let cx = &mut app.borrow_mut();
1155 cx.system_sleep_observers
1156 .clone()
1157 .retain(move |callback| (callback)(cx));
1158 }
1159 }
1160 }));
1161
1162 platform.on_system_wake(Box::new({
1163 let app = Rc::downgrade(&app);
1164 move || {
1165 if let Some(app) = app.upgrade() {
1166 let cx = &mut app.borrow_mut();
1167 cx.system_wake_observers
1168 .clone()
1169 .retain(move |callback| (callback)(cx));
1170 }
1171 }
1172 }));
1173
1174 platform.on_app_lifecycle(Box::new({
1175 let app = Rc::downgrade(&app);
1176 move |phase| {
1177 if let Some(app) = app.upgrade() {
1178 app.borrow_mut().update(|cx| {
1179 cx.app_lifecycle_observers
1180 .clone()
1181 .retain(|callback| callback(phase, cx));
1182 });
1183 }
1184 }
1185 }));
1186
1187 platform.on_memory_warning(Box::new({
1188 let app = Rc::downgrade(&app);
1189 move || {
1190 if let Some(app) = app.upgrade() {
1191 app.borrow_mut().update(|cx| {
1192 cx.memory_warning_observers
1193 .clone()
1194 .retain(|callback| callback(cx));
1195 });
1196 }
1197 }
1198 }));
1199
1200 platform.on_gpu_device_lost(Box::new({
1201 let app = Rc::downgrade(&app);
1202 move |lost| {
1203 if let Some(app) = app.upgrade() {
1204 log::error!("GPU device lost and not recovered: {}", lost.reason);
1205 let cx = &mut app.borrow_mut();
1206 cx.gpu_device_lost_observers
1207 .clone()
1208 .retain(|callback| callback(&lost, cx));
1209 }
1210 }
1211 }));
1212
1213 platform.on_power_mode_change(Box::new({
1214 let app = Rc::downgrade(&app);
1215 move || {
1216 if let Some(app) = app.upgrade() {
1217 let power_mode = app.borrow().platform.power_mode();
1218 app.borrow_mut().power_mode = power_mode;
1219 }
1220 }
1221 }));
1222
1223 platform.on_quit(Box::new({
1224 let cx = Rc::downgrade(&app);
1225 move || {
1226 let Some(cx) = cx.upgrade() else {
1227 return true;
1228 };
1229 match cx.try_borrow_mut() {
1230 Ok(mut cx) => {
1231 cx.shutdown();
1232 true
1233 }
1234 Err(_) => {
1235 false
1238 }
1239 }
1240 }
1241 }));
1242
1243 app
1244 }
1245
1246 #[doc(hidden)]
1247 pub fn ref_counts_drop_handle(&self) -> impl Sized + use<> {
1248 self.entities.ref_counts_drop_handle()
1249 }
1250
1251 #[cfg(any(test, gpui_leak_detection))]
1257 pub fn leak_detector_snapshot(&self) -> LeakDetectorSnapshot {
1258 self.entities.leak_detector_snapshot()
1259 }
1260
1261 #[cfg(any(test, gpui_leak_detection))]
1273 pub fn assert_no_new_leaks(&self, snapshot: &LeakDetectorSnapshot) {
1274 self.entities.assert_no_new_leaks(snapshot)
1275 }
1276
1277 #[cfg(all(feature = "test-support", not(any(test, gpui_leak_detection))))]
1279 pub fn leak_detector_snapshot(&self) -> LeakDetectorSnapshot {
1280 LeakDetectorSnapshot::default()
1281 }
1282
1283 #[cfg(all(feature = "test-support", not(any(test, gpui_leak_detection))))]
1286 pub fn assert_no_new_leaks(&self, _snapshot: &LeakDetectorSnapshot) {}
1287
1288 pub fn shutdown(&mut self) {
1294 #[cfg(all(feature = "profiler", not(target_family = "wasm")))]
1297 let hang_monitor_flush = self
1298 .hang_monitor
1299 .as_ref()
1300 .and_then(|hang_monitor| hang_monitor.request_flush());
1301
1302 let mut futures: Vec<LocalBoxFuture<'static, ()>> = Vec::new();
1303
1304 for observer in self.quit_observers.remove() {
1305 futures.push(observer(self));
1306 }
1307
1308 #[cfg(all(feature = "profiler", not(target_family = "wasm")))]
1309 if let Some(flushed) = hang_monitor_flush {
1310 futures.push(
1311 async move {
1312 if flushed.await.is_err() {
1313 log::warn!("hang monitor exited before flushing");
1314 }
1315 }
1316 .boxed_local(),
1317 );
1318 }
1319
1320 self.windows.clear();
1321 self.window_handles.clear();
1322 self.flush_effects();
1323 self.quitting = true;
1324
1325 let futures = futures::future::join_all(futures);
1326 #[cfg(not(target_family = "wasm"))]
1327 if self
1328 .foreground_executor
1329 .block_with_timeout(SHUTDOWN_TIMEOUT, futures)
1330 .is_err()
1331 {
1332 log::error!("timed out waiting on app_will_quit");
1333 }
1334 #[cfg(target_family = "wasm")]
1335 self.foreground_executor.spawn(futures).detach();
1336
1337 self.quitting = false;
1338 }
1339
1340 pub fn keyboard_layout(&self) -> &dyn PlatformKeyboardLayout {
1342 self.keyboard_layout.as_ref()
1343 }
1344
1345 pub fn keyboard_mapper(&self) -> &Rc<dyn PlatformKeyboardMapper> {
1347 &self.keyboard_mapper
1348 }
1349
1350 pub fn on_keyboard_layout_change<F>(&self, mut callback: F) -> Subscription
1352 where
1353 F: 'static + FnMut(&mut App),
1354 {
1355 self.keyboard_layout_observers.insert(Box::new(move |cx| {
1356 callback(cx);
1357 true
1358 }))
1359 }
1360
1361 pub fn quit(&self) {
1363 self.platform.quit();
1364 }
1365
1366 pub fn request_windowing(&self, request: WindowingRequest) -> Task<anyhow::Result<()>> {
1380 if !self.windows.is_empty() {
1381 return Task::ready(Err(anyhow::anyhow!(
1382 "cannot switch windowing modes while windows are open"
1383 )));
1384 }
1385 self.platform.request_windowing(request)
1386 }
1387
1388 pub fn graphical_environment(&self) -> Option<crate::GraphicalEnvironment> {
1398 self.platform.graphical_environment()
1399 }
1400
1401 pub fn cursor_hide_mode(&self) -> CursorHideMode {
1404 self.cursor_hide_mode
1405 }
1406
1407 pub fn set_cursor_hide_mode(&mut self, mode: CursorHideMode) {
1410 self.cursor_hide_mode = mode;
1411 }
1412
1413 pub fn is_cursor_visible(&self) -> bool {
1419 self.platform.is_cursor_visible()
1420 }
1421
1422 pub fn reduce_motion(&self) -> bool {
1425 self.reduce_motion
1426 }
1427
1428 pub fn set_reduce_motion(&mut self, reduce_motion: bool) {
1431 if self.reduce_motion != reduce_motion {
1432 self.reduce_motion = reduce_motion;
1433 self.refresh_windows();
1434 }
1435 }
1436
1437 pub fn refresh_windows(&mut self) {
1440 self.pending_effects.push_back(Effect::RefreshWindows);
1441 }
1442
1443 #[inline(always)]
1444 pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
1445 self.start_update();
1446 let result = update(self);
1447 self.finish_update();
1448 result
1449 }
1450
1451 pub(crate) fn start_update(&mut self) {
1452 self.pending_updates += 1;
1453 }
1454
1455 #[inline(never)]
1456 pub(crate) fn finish_update(&mut self) {
1457 if self.pending_updates == 1 {
1458 self.flush_effects();
1459 }
1460 self.pending_updates -= 1;
1461 }
1462
1463 pub fn observe<W>(
1465 &mut self,
1466 entity: &Entity<W>,
1467 mut on_notify: impl FnMut(Entity<W>, &mut App) + 'static,
1468 ) -> Subscription
1469 where
1470 W: 'static,
1471 {
1472 self.observe_internal(entity, move |e, cx| {
1473 on_notify(e, cx);
1474 true
1475 })
1476 }
1477
1478 pub(crate) fn detect_accessed_entities<R>(
1479 &mut self,
1480 callback: impl FnOnce(&mut App) -> R,
1481 ) -> (R, FxHashSet<EntityId>) {
1482 let accessed_entities_start = self.entities.accessed_entities.get_mut().clone();
1483 let result = callback(self);
1484 let entities_accessed_in_callback = self
1485 .entities
1486 .accessed_entities
1487 .get_mut()
1488 .difference(&accessed_entities_start)
1489 .copied()
1490 .collect::<FxHashSet<EntityId>>();
1491 (result, entities_accessed_in_callback)
1492 }
1493
1494 pub(crate) fn record_entities_accessed(
1495 &mut self,
1496 window_handle: AnyWindowHandle,
1497 invalidator: WindowInvalidator,
1498 entities: &FxHashSet<EntityId>,
1499 ) {
1500 let mut tracked_entities =
1501 std::mem::take(self.tracked_entities.entry(window_handle.id).or_default());
1502 for entity in tracked_entities.iter() {
1503 self.window_invalidators_by_entity
1504 .entry(*entity)
1505 .and_modify(|windows| {
1506 windows.remove(&window_handle.id);
1507 });
1508 }
1509 for entity in entities.iter() {
1510 self.window_invalidators_by_entity
1511 .entry(*entity)
1512 .or_default()
1513 .insert(window_handle.id, invalidator.clone());
1514 self.current_window_by_entity
1515 .insert(*entity, window_handle.id);
1516 }
1517 tracked_entities.clear();
1518 tracked_entities.extend(entities.iter().copied());
1519 self.tracked_entities
1520 .insert(window_handle.id, tracked_entities);
1521 }
1522
1523 pub(crate) fn new_observer(&mut self, key: EntityId, value: Handler) -> Subscription {
1524 let (subscription, activate) = self.observers.insert_pending(key, value);
1525 self.defer(move |_| activate());
1526 subscription
1527 }
1528
1529 pub(crate) fn observe_internal<W>(
1530 &mut self,
1531 entity: &Entity<W>,
1532 mut on_notify: impl FnMut(Entity<W>, &mut App) -> bool + 'static,
1533 ) -> Subscription
1534 where
1535 W: 'static,
1536 {
1537 let entity_id = entity.entity_id();
1538 let handle = entity.downgrade();
1539 self.new_observer(
1540 entity_id,
1541 Box::new(move |cx| {
1542 if let Some(entity) = handle.upgrade() {
1543 on_notify(entity, cx)
1544 } else {
1545 false
1546 }
1547 }),
1548 )
1549 }
1550
1551 pub fn subscribe<T, Event>(
1554 &mut self,
1555 entity: &Entity<T>,
1556 mut on_event: impl FnMut(Entity<T>, &Event, &mut App) + 'static,
1557 ) -> Subscription
1558 where
1559 T: 'static + EventEmitter<Event>,
1560 Event: 'static,
1561 {
1562 self.subscribe_internal(entity, move |entity, event, cx| {
1563 on_event(entity, event, cx);
1564 true
1565 })
1566 }
1567
1568 pub(crate) fn new_subscription(
1569 &mut self,
1570 key: EntityId,
1571 value: (TypeId, Listener),
1572 ) -> Subscription {
1573 let (subscription, activate) = self.event_listeners.insert_pending(key, value);
1574 self.defer(move |_| activate());
1575 subscription
1576 }
1577 pub(crate) fn subscribe_internal<T, Evt>(
1578 &mut self,
1579 entity: &Entity<T>,
1580 mut on_event: impl FnMut(Entity<T>, &Evt, &mut App) -> bool + 'static,
1581 ) -> Subscription
1582 where
1583 T: 'static + EventEmitter<Evt>,
1584 Evt: 'static,
1585 {
1586 let entity_id = entity.entity_id();
1587 let handle = entity.downgrade();
1588 self.new_subscription(
1589 entity_id,
1590 (
1591 TypeId::of::<Evt>(),
1592 Box::new(move |event, cx| {
1593 let event: &Evt = event.downcast_ref().expect("invalid event type");
1594 if let Some(entity) = handle.upgrade() {
1595 on_event(entity, event, cx)
1596 } else {
1597 false
1598 }
1599 }),
1600 ),
1601 )
1602 }
1603
1604 pub fn windows(&self) -> Vec<AnyWindowHandle> {
1608 self.windows
1609 .keys()
1610 .flat_map(|window_id| self.window_handles.get(&window_id).copied())
1611 .collect()
1612 }
1613
1614 pub fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
1620 self.platform.window_stack()
1621 }
1622
1623 pub fn active_window(&self) -> Option<AnyWindowHandle> {
1625 self.platform.active_window()
1626 }
1627
1628 pub fn open_window<V: 'static + Render>(
1632 &mut self,
1633 options: crate::WindowOptions,
1634 build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity<V>,
1635 ) -> anyhow::Result<WindowHandle<V>> {
1636 self.update(|cx| {
1637 let id = cx.windows.insert(None);
1638 let handle = WindowHandle::new(id);
1639 match Window::new(handle.into(), options, cx) {
1640 Ok(mut window) => {
1641 cx.window_update_stack.push(id);
1642 let root_view = build_root_view(&mut window, cx);
1643 cx.window_update_stack.pop();
1644 window.root.replace(root_view.into());
1645 window.defer(cx, |window: &mut Window, cx| window.appearance_changed(cx));
1646
1647 let clear = window.draw(cx);
1652 clear.clear(cx);
1653
1654 cx.window_handles.insert(id, window.handle);
1655 cx.windows.get_mut(id).unwrap().replace(Box::new(window));
1656 Ok(handle)
1657 }
1658 Err(e) => {
1659 cx.windows.remove(id);
1660 Err(e)
1661 }
1662 }
1663 })
1664 }
1665
1666 pub fn activate(&self, ignoring_other_apps: bool) {
1668 self.platform.activate(ignoring_other_apps);
1669 }
1670
1671 pub fn hide(&self) {
1673 self.platform.hide();
1674 }
1675
1676 pub fn hide_other_apps(&self) {
1678 self.platform.hide_other_apps();
1679 }
1680
1681 pub fn unhide_other_apps(&self) {
1683 self.platform.unhide_other_apps();
1684 }
1685
1686 pub fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
1688 self.platform.displays()
1689 }
1690
1691 pub fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
1693 self.platform.primary_display()
1694 }
1695
1696 pub fn is_screen_capture_supported(&self) -> bool {
1698 self.platform.is_screen_capture_supported()
1699 }
1700
1701 pub fn screen_capture_sources(
1703 &self,
1704 ) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
1705 self.platform.screen_capture_sources()
1706 }
1707
1708 pub fn find_display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
1710 self.displays()
1711 .iter()
1712 .find(|display| display.id() == id)
1713 .cloned()
1714 }
1715
1716 pub fn thermal_state(&self) -> ThermalState {
1718 self.platform.thermal_state()
1719 }
1720
1721 pub fn prevent_idle_sleep(&self, reason: &str) -> Task<Result<ActivityGuard>> {
1723 self.platform.prevent_idle_sleep(reason)
1724 }
1725
1726 pub fn on_thermal_state_change<F>(&self, mut callback: F) -> Subscription
1728 where
1729 F: 'static + FnMut(&mut App),
1730 {
1731 self.thermal_state_observers.insert(Box::new(move |cx| {
1732 callback(cx);
1733 true
1734 }))
1735 }
1736
1737 pub fn on_system_sleep<F>(&self, mut callback: F) -> Subscription
1742 where
1743 F: 'static + FnMut(&mut App),
1744 {
1745 self.system_sleep_observers.insert(Box::new(move |cx| {
1746 callback(cx);
1747 true
1748 }))
1749 }
1750
1751 pub(crate) fn displays_changed(&mut self) {
1752 let current = read_displays(self.platform.as_ref());
1753 let events = display_events(&self.displays, ¤t);
1754 self.displays = current;
1755 for event in &events {
1756 let DisplayEvent::Changed { id, .. } = *event else {
1757 continue;
1758 };
1759 for handle in self.windows() {
1760 self.update_window(handle, |_, window, cx| {
1761 if window.display_id == Some(id) {
1762 window.notify_display_observers(cx);
1763 }
1764 })
1765 .log_err();
1766 }
1767 }
1768 for event in events {
1769 self.display_observers
1770 .clone()
1771 .retain(|callback| (callback)(event, self));
1772 }
1773 }
1774
1775 pub(crate) fn knows_display(&self, id: DisplayId) -> bool {
1776 self.displays.contains_key(&id)
1777 }
1778
1779 pub(crate) fn display_refresh_interval(&self, id: DisplayId) -> Option<Duration> {
1782 self.displays.get(&id)?.refresh_interval
1783 }
1784
1785 pub fn observe_displays<F>(&self, mut callback: F) -> Subscription
1788 where
1789 F: 'static + FnMut(DisplayEvent, &mut App),
1790 {
1791 self.display_observers.insert(Box::new(move |event, cx| {
1792 callback(event, cx);
1793 true
1794 }))
1795 }
1796
1797 pub fn on_gpu_device_lost<F>(&self, mut callback: F) -> Subscription
1804 where
1805 F: 'static + FnMut(&GpuDeviceLost, &mut App),
1806 {
1807 self.gpu_device_lost_observers
1808 .insert(Box::new(move |lost, cx| {
1809 callback(lost, cx);
1810 true
1811 }))
1812 }
1813
1814 pub fn on_system_wake<F>(&self, mut callback: F) -> Subscription
1816 where
1817 F: 'static + FnMut(&mut App),
1818 {
1819 self.system_wake_observers.insert(Box::new(move |cx| {
1820 callback(cx);
1821 true
1822 }))
1823 }
1824
1825 pub fn on_app_lifecycle(
1833 &self,
1834 mut callback: impl FnMut(AppLifecyclePhase, &mut App) + 'static,
1835 ) -> Subscription {
1836 self.app_lifecycle_observers
1837 .insert(Box::new(move |phase, cx| {
1838 callback(phase, cx);
1839 true
1840 }))
1841 }
1842
1843 pub fn on_memory_warning(&self, mut callback: impl FnMut(&mut App) + 'static) -> Subscription {
1846 self.memory_warning_observers.insert(Box::new(move |cx| {
1847 callback(cx);
1848 true
1849 }))
1850 }
1851
1852 pub fn window_appearance(&self) -> WindowAppearance {
1854 self.platform.window_appearance()
1855 }
1856
1857 pub fn set_window_appearance(&self, appearance: Option<WindowAppearance>) {
1868 self.platform.set_window_appearance(appearance);
1869 }
1870
1871 pub fn button_layout(&self) -> Option<WindowButtonLayout> {
1873 self.platform.button_layout()
1874 }
1875
1876 pub fn desktop_preferences(&self) -> crate::DesktopPreferences {
1878 self.platform.desktop_preferences()
1879 }
1880
1881 pub fn observe_desktop_preferences(
1884 &self,
1885 mut callback: impl FnMut(&mut App) + 'static,
1886 ) -> Subscription {
1887 self.desktop_preferences_observers
1888 .insert(Box::new(move |cx| {
1889 callback(cx);
1890 true
1891 }))
1892 }
1893
1894 pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
1896 self.platform.read_from_clipboard()
1897 }
1898
1899 pub fn read_from_clipboard_async(
1907 &self,
1908 ) -> Task<Result<Option<ClipboardItem>, ClipboardReadError>> {
1909 self.platform.read_from_clipboard_async()
1910 }
1911
1912 pub fn set_text_rendering_mode(&mut self, mode: TextRenderingMode) {
1914 self.text_rendering_mode.set(mode);
1915 }
1916
1917 pub fn text_rendering_mode(&self) -> TextRenderingMode {
1919 self.text_rendering_mode.get()
1920 }
1921
1922 pub fn power_mode(&self) -> PowerMode {
1925 self.power_mode
1926 }
1927
1928 pub fn write_to_clipboard(&self, item: ClipboardItem) {
1930 self.platform.write_to_clipboard(item)
1931 }
1932
1933 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1936 pub fn read_from_primary(&self) -> Option<ClipboardItem> {
1937 self.platform.read_from_primary()
1938 }
1939
1940 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1943 pub fn write_to_primary(&self, item: ClipboardItem) {
1944 self.platform.write_to_primary(item)
1945 }
1946
1947 #[cfg(target_os = "macos")]
1953 pub fn read_from_find_pasteboard(&self) -> Option<ClipboardItem> {
1954 self.platform.read_from_find_pasteboard()
1955 }
1956
1957 #[cfg(target_os = "macos")]
1963 pub fn write_to_find_pasteboard(&self, item: ClipboardItem) {
1964 self.platform.write_to_find_pasteboard(item)
1965 }
1966
1967 pub fn write_credentials(
1969 &self,
1970 url: &str,
1971 username: &str,
1972 password: &[u8],
1973 ) -> Task<Result<()>> {
1974 self.platform.write_credentials(url, username, password)
1975 }
1976
1977 #[cfg(target_os = "macos")]
1985 pub fn write_credentials_if_not_exists(
1986 &self,
1987 url: &str,
1988 username: &str,
1989 password: &[u8],
1990 ) -> Task<Result<bool>> {
1991 self.platform
1992 .write_credentials_if_not_exists(url, username, password)
1993 }
1994
1995 pub fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
1997 self.platform.read_credentials(url)
1998 }
1999
2000 pub fn delete_credentials(&self, url: &str) -> Task<Result<()>> {
2002 self.platform.delete_credentials(url)
2003 }
2004
2005 pub fn open_url(&self, url: &str) {
2007 self.platform.open_url(url);
2008 }
2009
2010 pub fn register_url_scheme(&self, scheme: &str) -> Task<Result<()>> {
2017 self.platform.register_url_scheme(scheme)
2018 }
2019
2020 pub fn set_app_identity(&self, identifier: &str, name: &str) {
2027 self.platform.set_app_identity(identifier, name);
2028 }
2029
2030 pub fn show_system_notification(&self, notification: SystemNotification) {
2037 self.platform.show_system_notification(notification);
2038 }
2039
2040 pub fn dismiss_system_notification(&self, tag: &str) {
2045 self.platform.dismiss_system_notification(tag);
2046 }
2047
2048 pub fn on_system_notification_response<F>(&self, mut callback: F)
2052 where
2053 F: 'static + FnMut(SystemNotificationResponse, &mut App),
2054 {
2055 let this = self.this.clone();
2056 self.platform
2057 .on_system_notification_response(Box::new(move |response| {
2058 if let Some(app) = this.upgrade() {
2059 callback(response, &mut app.borrow_mut());
2060 }
2061 }));
2062 }
2063
2064 pub fn app_path(&self) -> Result<PathBuf> {
2068 self.platform.app_path()
2069 }
2070
2071 pub fn compositor_name(&self) -> &'static str {
2075 self.platform.compositor_name()
2076 }
2077
2078 pub fn platform_info(&self) -> crate::PlatformInfo {
2080 self.platform.platform_info()
2081 }
2082
2083 pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
2085 self.platform.path_for_auxiliary_executable(name)
2086 }
2087
2088 pub fn prompt_for_paths(
2094 &self,
2095 options: PathPromptOptions,
2096 ) -> oneshot::Receiver<Result<Option<Vec<PathBuf>>>> {
2097 self.platform.prompt_for_paths(options)
2098 }
2099
2100 pub fn prompt_for_new_path(
2107 &self,
2108 directory: &Path,
2109 suggested_name: Option<&str>,
2110 ) -> oneshot::Receiver<Result<Option<PathBuf>>> {
2111 self.platform.prompt_for_new_path(directory, suggested_name)
2112 }
2113
2114 pub fn reveal_path(&self, path: &Path) {
2116 self.platform.reveal_path(path)
2117 }
2118
2119 pub fn open_with_system(&self, path: &Path) {
2121 self.platform.open_with_system(path)
2122 }
2123
2124 pub fn should_auto_hide_scrollbars(&self) -> bool {
2126 self.platform.should_auto_hide_scrollbars()
2127 }
2128
2129 pub fn restart(&mut self) {
2131 self.restart_observers
2132 .clone()
2133 .retain(|observer| observer(self));
2134 self.platform.restart(
2135 self.restart_path.take(),
2136 std::mem::take(&mut self.restart_arguments),
2137 )
2138 }
2139
2140 pub fn set_restart_path(&mut self, path: PathBuf) {
2142 self.restart_path = Some(path);
2143 }
2144
2145 pub fn http_client(&self) -> Arc<dyn HttpClient> {
2147 self.http_client.clone()
2148 }
2149
2150 pub fn set_http_client(&mut self, new_client: Arc<dyn HttpClient>) {
2152 self.http_client = new_client;
2153 }
2154
2155 pub fn set_quit_mode(&mut self, mode: QuitMode) {
2158 self.quit_mode = mode;
2159 }
2160
2161 pub fn set_activation_policy(&mut self, policy: ActivationPolicy) {
2170 self.platform.set_activation_policy(policy);
2171 }
2172
2173 pub fn svg_renderer(&self) -> SvgRenderer {
2175 self.svg_renderer.clone()
2176 }
2177
2178 pub(crate) fn push_effect(&mut self, effect: Effect) {
2179 match &effect {
2180 Effect::Notify { emitter } => {
2181 if !self.pending_notifications.insert(*emitter) {
2182 return;
2183 }
2184 }
2185 Effect::NotifyGlobalObservers { global_type } => {
2186 if !self.pending_global_notifications.insert(*global_type) {
2187 return;
2188 }
2189 }
2190 _ => {}
2191 };
2192
2193 self.pending_effects.push_back(effect);
2194 }
2195
2196 fn flush_effects(&mut self) {
2200 loop {
2201 self.release_dropped_entities();
2202 self.release_dropped_focus_handles();
2203 if let Some(effect) = self.pending_effects.pop_front() {
2204 match effect {
2205 Effect::Notify { emitter } => {
2206 self.apply_notify_effect(emitter);
2207 }
2208
2209 Effect::Emit {
2210 emitter,
2211 event_type,
2212 event,
2213 } => self.apply_emit_effect(emitter, event_type, &*event),
2214
2215 Effect::RefreshWindows => {
2216 self.apply_refresh_effect();
2217 }
2218
2219 Effect::NotifyGlobalObservers { global_type } => {
2220 self.apply_notify_global_observers_effect(global_type);
2221 }
2222
2223 Effect::Defer { callback } => {
2224 self.apply_defer_effect(callback);
2225 }
2226 Effect::EntityCreated {
2227 entity,
2228 tid,
2229 window,
2230 } => {
2231 self.apply_entity_created_effect(entity, tid, window);
2232 }
2233 }
2234 } else {
2235 #[cfg(any(test, feature = "test-support"))]
2236 if matches!(self.mode, GpuiMode::Test { .. }) {
2237 for window in self
2238 .windows
2239 .values()
2240 .filter_map(|window| {
2241 let window = window.as_deref()?;
2242 window.invalidator.is_dirty().then_some(window.handle)
2243 })
2244 .collect::<Vec<_>>()
2245 {
2246 self.update_window(window, |_, window, cx| window.draw(cx).clear(cx))
2247 .unwrap();
2248 }
2249 }
2250
2251 if self.pending_effects.is_empty() {
2252 for window in self.windows.values().filter_map(|window| window.as_deref()) {
2253 if window.invalidator.is_dirty()
2254 || window.needs_present.get()
2255 || !window.next_frame_callbacks.borrow().is_empty()
2256 {
2257 window.platform_window.schedule_frame();
2258 }
2259 }
2260
2261 self.event_arena.clear();
2262 break;
2263 }
2264 }
2265 }
2266 }
2267
2268 fn release_dropped_entities(&mut self) {
2272 loop {
2273 let dropped = self.entities.take_dropped();
2274 if dropped.is_empty() {
2275 break;
2276 }
2277
2278 for (entity_id, mut entity) in dropped {
2279 self.observers.remove(&entity_id);
2280 self.event_listeners.remove(&entity_id);
2281 self.window_invalidators_by_entity.remove(&entity_id);
2282 self.current_window_by_entity.remove(&entity_id);
2283 for release_callback in self.release_listeners.remove(&entity_id) {
2284 release_callback(entity.as_mut(), self);
2285 }
2286 }
2287 }
2288 }
2289
2290 fn release_dropped_focus_handles(&mut self) {
2292 let focus_handles = self.focus_handles.clone();
2293 let mut focus_handles = focus_handles.write();
2294 let FocusMap {
2295 handles,
2296 dropped_handles,
2297 } = &mut *focus_handles;
2298 if dropped_handles.is_empty() {
2299 return;
2300 }
2301
2302 let windows = self
2303 .windows
2304 .values()
2305 .filter_map(|window| {
2306 let window = window.as_ref()?;
2307 dropped_handles
2308 .contains(&window.focus?)
2309 .then_some(window.handle)
2310 })
2311 .collect::<SmallVec<[_; 1]>>();
2312 for handle_id in dropped_handles.drain(..) {
2313 handles.remove(handle_id);
2314 }
2315 drop(focus_handles);
2316
2317 for window_handle in windows {
2318 window_handle
2319 .update(self, |_, window, cx| window.blur(cx))
2320 .ok();
2321 }
2322 }
2323
2324 fn apply_notify_effect(&mut self, emitter: EntityId) {
2325 self.pending_notifications.remove(&emitter);
2326
2327 self.observers
2328 .clone()
2329 .retain(&emitter, |handler| handler(self));
2330 }
2331
2332 fn apply_emit_effect(&mut self, emitter: EntityId, event_type: TypeId, event: &dyn Any) {
2333 self.event_listeners
2334 .clone()
2335 .retain(&emitter, |(stored_type, handler)| {
2336 if *stored_type == event_type {
2337 handler(event, self)
2338 } else {
2339 true
2340 }
2341 });
2342 }
2343
2344 fn apply_refresh_effect(&mut self) {
2345 for window in self.windows.values_mut() {
2346 if let Some(window) = window.as_deref_mut() {
2347 window.refreshing = true;
2348 window.invalidator.set_dirty(true);
2349 }
2350 }
2351 }
2352
2353 fn apply_notify_global_observers_effect(&mut self, type_id: TypeId) {
2354 self.pending_global_notifications.remove(&type_id);
2355 self.global_observers
2356 .clone()
2357 .retain(&type_id, |observer| observer(self));
2358 }
2359
2360 fn apply_defer_effect(&mut self, callback: Box<dyn FnOnce(&mut Self) + 'static>) {
2361 callback(self);
2362 }
2363
2364 fn apply_entity_created_effect(
2365 &mut self,
2366 entity: AnyEntity,
2367 tid: TypeId,
2368 window: Option<WindowId>,
2369 ) {
2370 if let Some(id) = window {
2374 self.current_window_by_entity.insert(entity.entity_id(), id);
2375 }
2376
2377 self.new_entity_observers.clone().retain(&tid, |observer| {
2378 if let Some(id) = window {
2379 self.update_window_id(id, {
2380 let entity = entity.clone();
2381 |_, window, cx| (observer)(entity, &mut Some(window), cx)
2382 })
2383 .expect("All windows should be off the stack when flushing effects");
2384 } else {
2385 (observer)(entity.clone(), &mut None, self)
2386 }
2387 true
2388 });
2389 }
2390
2391 #[inline(always)]
2397 pub fn with_window<R>(
2398 &mut self,
2399 entity_id: EntityId,
2400 f: impl FnOnce(&mut Window, &mut App) -> R,
2401 ) -> Option<R> {
2402 let window_id = *self.current_window_by_entity.get(&entity_id)?;
2403 self.update_window_id(window_id, |_, window, cx| f(window, cx))
2404 .ok()
2405 }
2406
2407 fn ensure_window(&mut self, entity_id: EntityId, window: WindowId) {
2408 self.current_window_by_entity
2409 .entry(entity_id)
2410 .or_insert(window);
2411 }
2412
2413 #[inline(always)]
2414 pub(crate) fn update_window_id<T, F>(&mut self, id: WindowId, update: F) -> Result<T>
2415 where
2416 F: FnOnce(AnyView, &mut Window, &mut App) -> T,
2417 {
2418 let mut update = Some(update);
2419 let mut result = None;
2420 self.update_window_erased(id, &mut |arguments| {
2421 if let Some((root_view, window, cx)) = arguments {
2422 result = Some(update.take().unwrap()(root_view, window, cx));
2423 } else {
2424 drop(update.take());
2425 drop(result.take());
2426 }
2427 });
2428 result.context("window not found")
2429 }
2430
2431 pub fn to_async(&self) -> AsyncApp {
2434 AsyncApp {
2435 app: self.this.clone(),
2436 background_executor: self.background_executor.clone(),
2437 foreground_executor: self.foreground_executor.clone(),
2438 }
2439 }
2440
2441 pub fn background_executor(&self) -> &BackgroundExecutor {
2443 &self.background_executor
2444 }
2445
2446 pub fn is_test(&self) -> bool {
2449 self.background_executor.is_test()
2450 }
2451
2452 pub fn foreground_executor(&self) -> &ForegroundExecutor {
2454 if self.quitting {
2455 panic!("Can't spawn on main thread after on_app_quit")
2456 };
2457 &self.foreground_executor
2458 }
2459
2460 #[cfg(feature = "profiler")]
2463 pub fn foreground_journal(&self) -> crate::profiler::journal::ForegroundJournal {
2464 self.foreground_journal.clone()
2465 }
2466
2467 #[cfg(all(feature = "profiler", not(target_family = "wasm")))]
2484 pub fn start_hang_monitor(
2485 &mut self,
2486 config: crate::profiler::hang::HangMonitorConfig,
2487 on_poll: impl FnMut(crate::profiler::hang::HangMonitorPoll) + Send + 'static,
2488 ) -> Result<(), crate::profiler::hang::HangMonitorError> {
2489 use crate::profiler::hang::{HangDetector, HangMonitor, HangMonitorError};
2490
2491 if self.hang_monitor.is_some() {
2492 debug_assert!(false, "the hang monitor was started twice");
2493 return Err(HangMonitorError::AlreadyStarted);
2494 }
2495 let detector = HangDetector::new(
2496 self.foreground_journal(),
2497 config.threshold,
2498 config.frame_budget,
2499 );
2500 let monitor = HangMonitor::spawn(detector, config.interval, on_poll)
2501 .map_err(HangMonitorError::Spawn)?;
2502 self.hang_monitor = Some(monitor);
2503 Ok(())
2504 }
2505
2506 #[track_caller]
2509 #[inline(always)]
2510 pub fn spawn<AsyncFn, R>(&self, f: AsyncFn) -> Task<R>
2511 where
2512 AsyncFn: AsyncFnOnce(&mut AsyncApp) -> R + 'static,
2513 R: 'static,
2514 {
2515 let mut cx = self.prepare_spawn();
2516
2517 self.foreground_executor
2518 .spawn(async move { f(&mut cx).await }.boxed_local())
2519 }
2520
2521 pub fn spawn_with_priority<AsyncFn, R>(&self, priority: Priority, f: AsyncFn) -> Task<R>
2525 where
2526 AsyncFn: AsyncFnOnce(&mut AsyncApp) -> R + 'static,
2527 R: 'static,
2528 {
2529 if self.quitting {
2530 debug_panic!("Can't spawn on main thread after on_app_quit")
2531 };
2532
2533 let mut cx = self.to_async();
2534
2535 self.foreground_executor
2536 .spawn_with_priority(priority, async move { f(&mut cx).await }.boxed_local())
2537 }
2538
2539 pub fn defer(&mut self, f: impl FnOnce(&mut App) + 'static) {
2542 self.push_effect(Effect::Defer {
2543 callback: Box::new(f),
2544 });
2545 }
2546
2547 pub fn asset_source(&self) -> &Arc<dyn AssetSource> {
2549 &self.asset_source
2550 }
2551
2552 pub fn text_system(&self) -> &Arc<TextSystem> {
2554 &self.text_system
2555 }
2556
2557 pub fn on_missing_glyphs(
2568 &self,
2569 callback: impl FnMut(&[MissingGlyph], &mut App) + 'static,
2570 ) -> Subscription {
2571 let registration = self.missing_glyph_callback.replace(Box::new(callback));
2572
2573 if let Some(mut receiver) = self.text_system.take_missing_glyph_receiver() {
2574 let callback = self.missing_glyph_callback.clone();
2575 self.spawn(async move |cx| {
2576 while let Ok(missing_glyphs) = receiver.recv().await {
2577 cx.update(|cx| callback.invoke(&missing_glyphs, cx));
2578 }
2579 })
2580 .detach();
2581 }
2582 self.text_system.enable_missing_glyph_reporting();
2583
2584 let callback = self.missing_glyph_callback.clone();
2585 let text_system = self.text_system.clone();
2586 Subscription::new(move || {
2587 if callback.remove(®istration) {
2588 text_system.disable_missing_glyph_reporting();
2589 }
2590 })
2591 }
2592
2593 pub fn has_global<G: Global>(&self) -> bool {
2595 self.globals_by_type.contains_key(&TypeId::of::<G>())
2596 }
2597
2598 #[track_caller]
2600 pub fn global<G: Global>(&self) -> &G {
2601 self.globals_by_type
2602 .get(&TypeId::of::<G>())
2603 .map(|any_state| any_state.downcast_ref::<G>().unwrap())
2604 .unwrap_or_else(|| panic!("no state of type {} exists", type_name::<G>()))
2605 }
2606
2607 pub fn try_global<G: Global>(&self) -> Option<&G> {
2609 self.globals_by_type
2610 .get(&TypeId::of::<G>())
2611 .map(|any_state| any_state.downcast_ref::<G>().unwrap())
2612 }
2613
2614 #[track_caller]
2616 pub fn global_mut<G: Global>(&mut self) -> &mut G {
2617 let global_type = TypeId::of::<G>();
2618 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2619 self.globals_by_type
2620 .get_mut(&global_type)
2621 .and_then(|any_state| any_state.downcast_mut::<G>())
2622 .unwrap_or_else(|| panic!("no state of type {} exists", type_name::<G>()))
2623 }
2624
2625 pub fn default_global<G: Global + Default>(&mut self) -> &mut G {
2628 let global_type = TypeId::of::<G>();
2629 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2630 self.globals_by_type
2631 .entry(global_type)
2632 .or_insert_with(|| Box::<G>::default())
2633 .downcast_mut::<G>()
2634 .unwrap()
2635 }
2636
2637 pub fn set_global<G: Global>(&mut self, global: G) {
2639 let global_type = TypeId::of::<G>();
2640 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2641 self.globals_by_type.insert(global_type, Box::new(global));
2642 }
2643
2644 #[cfg(any(test, feature = "test-support"))]
2646 pub fn clear_globals(&mut self) {
2647 self.globals_by_type.drain();
2648 }
2649
2650 pub fn remove_global<G: Global>(&mut self) -> G {
2652 let global_type = TypeId::of::<G>();
2653 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2654 *self
2655 .globals_by_type
2656 .remove(&global_type)
2657 .unwrap_or_else(|| panic!("no global added for {}", type_name::<G>()))
2658 .downcast()
2659 .unwrap()
2660 }
2661
2662 pub fn observe_global<G: Global>(
2664 &mut self,
2665 mut f: impl FnMut(&mut Self) + 'static,
2666 ) -> Subscription {
2667 let (subscription, activate) = self.global_observers.insert_pending(
2668 TypeId::of::<G>(),
2669 Box::new(move |cx| {
2670 f(cx);
2671 true
2672 }),
2673 );
2674 self.defer(move |_| activate());
2675 subscription
2676 }
2677
2678 #[track_caller]
2680 pub(crate) fn lease_global<G: Global>(&mut self) -> GlobalLease<G> {
2681 GlobalLease::new(
2682 self.globals_by_type
2683 .remove(&TypeId::of::<G>())
2684 .with_context(|| format!("no global registered of type {}", type_name::<G>()))
2685 .unwrap(),
2686 )
2687 }
2688
2689 pub(crate) fn end_global_lease<G: Global>(&mut self, lease: GlobalLease<G>) {
2691 let global_type = TypeId::of::<G>();
2692
2693 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2694 self.globals_by_type.insert(global_type, lease.global);
2695 }
2696
2697 pub(crate) fn new_entity_observer(
2698 &self,
2699 key: TypeId,
2700 value: NewEntityListener,
2701 ) -> Subscription {
2702 self.new_entity_observers.insert(key, value)
2703 }
2704
2705 pub fn observe_new<T: 'static>(
2708 &self,
2709 on_new: impl 'static + Fn(&mut T, Option<&mut Window>, &mut Context<T>),
2710 ) -> Subscription {
2711 self.new_entity_observer(
2712 TypeId::of::<T>(),
2713 Box::new(
2714 move |any_entity: AnyEntity, window: &mut Option<&mut Window>, cx: &mut App| {
2715 any_entity
2716 .downcast::<T>()
2717 .unwrap()
2718 .update(cx, |entity_state, cx| {
2719 on_new(entity_state, window.as_deref_mut(), cx)
2720 })
2721 },
2722 ),
2723 )
2724 }
2725
2726 pub fn observe_release<T>(
2729 &self,
2730 handle: &Entity<T>,
2731 on_release: impl FnOnce(&mut T, &mut App) + 'static,
2732 ) -> Subscription
2733 where
2734 T: 'static,
2735 {
2736 self.release_listeners.insert(
2737 handle.entity_id(),
2738 Box::new(move |entity, cx| {
2739 let entity = entity.downcast_mut().expect("invalid entity type");
2740 on_release(entity, cx)
2741 }),
2742 )
2743 }
2744
2745 pub fn observe_release_in<T>(
2748 &self,
2749 handle: &Entity<T>,
2750 window: &Window,
2751 on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static,
2752 ) -> Subscription
2753 where
2754 T: 'static,
2755 {
2756 let window_handle = window.handle;
2757 self.observe_release(handle, move |entity, cx| {
2758 let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx));
2759 })
2760 }
2761
2762 pub fn observe_keystrokes(
2767 &mut self,
2768 mut f: impl FnMut(&KeystrokeEvent, &mut Window, &mut App) + 'static,
2769 ) -> Subscription {
2770 self.keystroke_observers
2771 .insert(Box::new(move |event, window, cx| {
2772 f(event, window, cx);
2773 true
2774 }))
2775 }
2776
2777 pub fn intercept_keystrokes(
2782 &mut self,
2783 mut f: impl FnMut(&KeystrokeEvent, &mut Window, &mut App) + 'static,
2784 ) -> Subscription {
2785 self.keystroke_interceptors
2786 .insert(Box::new(move |event, window, cx| {
2787 f(event, window, cx);
2788 true
2789 }))
2790 }
2791
2792 pub fn bind_keys(&mut self, bindings: impl IntoIterator<Item = KeyBinding>) {
2794 self.keymap.borrow_mut().add_bindings(bindings);
2795 self.pending_effects.push_back(Effect::RefreshWindows);
2796 }
2797
2798 pub fn clear_key_bindings(&mut self) {
2800 self.keymap.borrow_mut().clear();
2801 self.pending_effects.push_back(Effect::RefreshWindows);
2802 }
2803
2804 pub fn key_bindings(&self) -> Rc<RefCell<Keymap>> {
2806 self.keymap.clone()
2807 }
2808
2809 pub fn on_action<A: Action>(
2813 &mut self,
2814 listener: impl Fn(&A, &mut Self) + 'static,
2815 ) -> &mut Self {
2816 self.global_action_listeners
2817 .entry(TypeId::of::<A>())
2818 .or_default()
2819 .push(Rc::new(move |action, phase, cx| {
2820 if phase == DispatchPhase::Bubble {
2821 let action = action.downcast_ref().unwrap();
2822 listener(action, cx)
2823 }
2824 }));
2825 self
2826 }
2827
2828 pub fn stop_propagation(&mut self) {
2833 self.propagate_event = false;
2834 }
2835
2836 pub fn propagate(&mut self) {
2841 self.propagate_event = true;
2842 }
2843
2844 pub fn build_action(
2846 &self,
2847 name: &str,
2848 data: Option<serde_json::Value>,
2849 ) -> std::result::Result<Box<dyn Action>, ActionBuildError> {
2850 self.actions.build_action(name, data)
2851 }
2852
2853 pub fn all_action_names(&self) -> &[&'static str] {
2856 self.actions.all_action_names()
2857 }
2858
2859 pub fn all_bindings_for_input(&self, input: &[Keystroke]) -> Vec<KeyBinding> {
2863 RefCell::borrow(&self.keymap).all_bindings_for_input(input)
2864 }
2865
2866 pub fn action_schemas(
2868 &self,
2869 generator: &mut schemars::SchemaGenerator,
2870 ) -> Vec<(&'static str, Option<schemars::Schema>)> {
2871 self.actions.action_schemas(generator)
2872 }
2873
2874 pub fn action_schema_by_name(
2879 &self,
2880 name: &str,
2881 generator: &mut schemars::SchemaGenerator,
2882 ) -> Option<Option<schemars::Schema>> {
2883 self.actions.action_schema_by_name(name, generator)
2884 }
2885
2886 pub fn deprecated_actions_to_preferred_actions(&self) -> &HashMap<&'static str, &'static str> {
2888 self.actions.deprecated_aliases()
2889 }
2890
2891 pub fn action_deprecation_messages(&self) -> &HashMap<&'static str, &'static str> {
2893 self.actions.deprecation_messages()
2894 }
2895
2896 pub fn action_documentation(&self) -> &HashMap<&'static str, &'static str> {
2898 self.actions.documentation()
2899 }
2900
2901 pub fn on_app_quit<Fut>(
2904 &self,
2905 mut on_quit: impl FnMut(&mut App) -> Fut + 'static,
2906 ) -> Subscription
2907 where
2908 Fut: 'static + Future<Output = ()>,
2909 {
2910 self.quit_observers.insert(Box::new(move |cx| {
2911 let future = on_quit(cx);
2912 future.boxed_local()
2913 }))
2914 }
2915
2916 pub fn on_app_restart(&self, mut on_restart: impl 'static + FnMut(&mut App)) -> Subscription {
2920 self.restart_observers.insert(Box::new(move |cx| {
2921 on_restart(cx);
2922 true
2923 }))
2924 }
2925
2926 pub fn on_window_closed(
2929 &self,
2930 on_closed: impl FnMut(&mut App, WindowId) + 'static,
2931 ) -> Subscription {
2932 self.window_closed_observers.insert(Box::new(on_closed))
2933 }
2934
2935 pub(crate) fn clear_pending_keystrokes(&mut self) {
2936 for window in self.windows() {
2937 window
2938 .update(self, |_, window, cx| {
2939 window.clear_pending_keystrokes(cx);
2940 })
2941 .ok();
2942 }
2943 }
2944
2945 pub fn is_action_available(&mut self, action: &dyn Action) -> bool {
2948 let mut action_available = false;
2949 if let Some(window) = self.active_window()
2950 && let Ok(window_action_available) =
2951 window.update(self, |_, window, cx| window.is_action_available(action, cx))
2952 {
2953 action_available = window_action_available;
2954 }
2955
2956 action_available
2957 || self
2958 .global_action_listeners
2959 .contains_key(&action.as_any().type_id())
2960 }
2961
2962 pub fn set_menus(&self, menus: impl IntoIterator<Item = Menu>) {
2964 let menus: Vec<Menu> = menus.into_iter().collect();
2965 self.platform.set_menus(menus, &self.keymap.borrow());
2966 }
2967
2968 pub fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
2970 self.platform.get_menus()
2971 }
2972
2973 pub fn set_dock_menu(&self, menus: Vec<MenuItem>) {
2975 self.platform.set_dock_menu(menus, &self.keymap.borrow())
2976 }
2977
2978 pub fn perform_dock_menu_action(&self, action: usize) {
2980 self.platform.perform_dock_menu_action(action);
2981 }
2982
2983 pub fn add_recent_document(&self, path: &Path) {
2988 self.platform.add_recent_document(path);
2989 }
2990
2991 pub fn update_jump_list(
2994 &self,
2995 menus: Vec<MenuItem>,
2996 entries: Vec<SmallVec<[PathBuf; 2]>>,
2997 ) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
2998 self.platform.update_jump_list(menus, entries)
2999 }
3000
3001 pub fn dispatch_action(&mut self, action: &dyn Action) {
3004 if let Some(active_window) = self.active_window() {
3005 active_window
3006 .update(self, |_, window, cx| {
3007 window.dispatch_action(action.boxed_clone(), cx)
3008 })
3009 .log_err();
3010 } else {
3011 self.dispatch_global_action(action);
3012 }
3013 }
3014
3015 fn dispatch_global_action(&mut self, action: &dyn Action) {
3016 self.propagate_event = true;
3017
3018 if let Some(mut global_listeners) = self
3019 .global_action_listeners
3020 .remove(&action.as_any().type_id())
3021 {
3022 for listener in &global_listeners {
3023 listener(action.as_any(), DispatchPhase::Capture, self);
3024 if !self.propagate_event {
3025 break;
3026 }
3027 }
3028
3029 global_listeners.extend(
3030 self.global_action_listeners
3031 .remove(&action.as_any().type_id())
3032 .unwrap_or_default(),
3033 );
3034
3035 self.global_action_listeners
3036 .insert(action.as_any().type_id(), global_listeners);
3037 }
3038
3039 if self.propagate_event
3040 && let Some(mut global_listeners) = self
3041 .global_action_listeners
3042 .remove(&action.as_any().type_id())
3043 {
3044 for listener in global_listeners.iter().rev() {
3045 listener(action.as_any(), DispatchPhase::Bubble, self);
3046 if !self.propagate_event {
3047 break;
3048 }
3049 }
3050
3051 global_listeners.extend(
3052 self.global_action_listeners
3053 .remove(&action.as_any().type_id())
3054 .unwrap_or_default(),
3055 );
3056
3057 self.global_action_listeners
3058 .insert(action.as_any().type_id(), global_listeners);
3059 }
3060 }
3061
3062 pub fn has_active_drag(&self) -> bool {
3064 self.active_drag.is_some()
3065 }
3066
3067 pub fn active_drag_cursor_style(&self) -> Option<CursorStyle> {
3069 self.active_drag.as_ref().and_then(|drag| drag.cursor_style)
3070 }
3071
3072 pub fn stop_active_drag(&mut self, window: &mut Window) -> bool {
3074 if self.active_drag.is_some() {
3075 self.active_drag = None;
3076 if self.platform_owned_drag.as_ref().is_some_and(|drag| {
3077 drag.source_window == window.window_handle().window_id()
3078 && matches!(&drag.state, PlatformOwnedDragState::RestoredInSourceWindow)
3079 }) {
3080 self.platform_owned_drag = None;
3081 }
3082 window.refresh();
3083 true
3084 } else {
3085 false
3086 }
3087 }
3088
3089 pub(crate) fn hand_active_drag_to_platform(&mut self, source_window: WindowId) -> bool {
3090 let Some(drag) = self.active_drag.take() else {
3091 return false;
3092 };
3093 self.platform_owned_drag = Some(PlatformOwnedDrag {
3094 source_window,
3095 state: PlatformOwnedDragState::Suspended(drag),
3096 });
3097 true
3098 }
3099
3100 pub(crate) fn restore_platform_drag(&mut self, source_window: WindowId) -> bool {
3101 let Some(platform_drag) = self
3102 .platform_owned_drag
3103 .as_mut()
3104 .filter(|drag| drag.source_window == source_window)
3105 else {
3106 return false;
3107 };
3108 let state = std::mem::replace(
3109 &mut platform_drag.state,
3110 PlatformOwnedDragState::RestoredInSourceWindow,
3111 );
3112 let PlatformOwnedDragState::Suspended(drag) = state else {
3113 return false;
3114 };
3115 self.active_drag = Some(drag);
3116 true
3117 }
3118
3119 pub(crate) fn hand_restored_drag_to_platform(&mut self, source_window: WindowId) -> bool {
3120 let Some(platform_drag) = self.platform_owned_drag.as_mut().filter(|drag| {
3121 drag.source_window == source_window
3122 && matches!(&drag.state, PlatformOwnedDragState::RestoredInSourceWindow)
3123 }) else {
3124 return false;
3125 };
3126 let Some(drag) = self.active_drag.take() else {
3127 return false;
3128 };
3129 platform_drag.state = PlatformOwnedDragState::Suspended(drag);
3130 true
3131 }
3132
3133 pub(crate) fn end_platform_drag(&mut self, source_window: WindowId) -> bool {
3134 if !self
3135 .platform_owned_drag
3136 .as_ref()
3137 .is_some_and(|drag| drag.source_window == source_window)
3138 {
3139 return false;
3140 }
3141 self.platform_owned_drag = None;
3142 self.active_drag = None;
3143 true
3144 }
3145
3146 pub fn set_active_drag_cursor_style(
3148 &mut self,
3149 cursor_style: CursorStyle,
3150 window: &mut Window,
3151 ) -> bool {
3152 if let Some(ref mut drag) = self.active_drag {
3153 drag.cursor_style = Some(cursor_style);
3154 window.refresh();
3155 true
3156 } else {
3157 false
3158 }
3159 }
3160
3161 pub fn set_prompt_builder(
3164 &mut self,
3165 renderer: impl Fn(
3166 PromptLevel,
3167 &str,
3168 Option<&str>,
3169 &[PromptButton],
3170 PromptHandle,
3171 &mut Window,
3172 &mut App,
3173 ) -> RenderablePromptHandle
3174 + 'static,
3175 ) {
3176 self.prompt_builder = Some(PromptBuilder::Custom(Box::new(renderer)));
3177 }
3178
3179 pub fn reset_prompt_builder(&mut self) {
3181 self.prompt_builder = Some(PromptBuilder::Default);
3182 }
3183
3184 pub fn remove_asset<A: Asset>(&mut self, source: &A::Source) {
3186 let asset_id = (TypeId::of::<A>(), hash(source));
3187 self.loading_assets.remove(&asset_id);
3188 }
3189
3190 #[cfg(any(test, feature = "test-support"))]
3193 pub fn has_asset<A: Asset>(&self, source: &A::Source) -> bool {
3194 let asset_id = (TypeId::of::<A>(), hash(source));
3195 self.loading_assets.contains_key(&asset_id)
3196 }
3197
3198 pub fn fetch_asset<A: Asset>(&mut self, source: &A::Source) -> Option<A::Output> {
3203 self.asset_entry::<A>(source).get()
3204 }
3205
3206 pub(crate) fn asset_entry<A: Asset>(&mut self, source: &A::Source) -> &CachedLoad<A::Output> {
3207 let asset_id = (TypeId::of::<A>(), hash(source));
3208 if !self.loading_assets.contains_key(&asset_id) {
3209 let future = A::load(source.clone(), self);
3210 let entry = CachedLoad::new(future, self);
3211 self.loading_assets.insert(asset_id, Box::new(entry));
3212 }
3213 self.loading_assets
3214 .get(&asset_id)
3215 .and_then(|entry| entry.downcast_ref())
3216 .expect("asset cache entries are keyed by their asset type")
3217 }
3218
3219 #[track_caller]
3222 pub fn focus_handle(&self) -> FocusHandle {
3223 FocusHandle::new(&self.focus_handles)
3224 }
3225
3226 pub fn notify(&mut self, entity_id: EntityId) {
3228 let window_invalidators = mem::take(
3229 self.window_invalidators_by_entity
3230 .entry(entity_id)
3231 .or_default(),
3232 );
3233
3234 let live_invalidators: SmallVec<[WindowInvalidator; 2]> = window_invalidators
3239 .iter()
3240 .filter(|(window_id, _)| {
3241 self.tracked_entities
3242 .get(window_id)
3243 .is_some_and(|set| set.contains(&entity_id))
3244 })
3245 .map(|(_, invalidator)| invalidator.clone())
3246 .collect();
3247
3248 if live_invalidators.is_empty() {
3249 if self.pending_notifications.insert(entity_id) {
3250 self.pending_effects
3251 .push_back(Effect::Notify { emitter: entity_id });
3252 }
3253 } else {
3254 for invalidator in &live_invalidators {
3255 invalidator.invalidate_view(entity_id, self);
3256 }
3257 }
3258
3259 self.window_invalidators_by_entity
3260 .insert(entity_id, window_invalidators);
3261 }
3262
3263 #[cfg(any(test, feature = "test-support", debug_assertions))]
3265 pub fn get_name(&self) -> Option<&'static str> {
3266 self.name
3267 }
3268
3269 pub fn can_select_mixed_files_and_dirs(&self) -> bool {
3271 self.platform.can_select_mixed_files_and_dirs()
3272 }
3273
3274 pub fn drop_image(&mut self, image: Arc<RenderImage>, current_window: Option<&mut Window>) {
3279 for window in self.windows.values_mut().flatten() {
3281 _ = window.drop_image(image.clone());
3282 }
3283
3284 if let Some(window) = current_window {
3286 _ = window.drop_image(image);
3287 }
3288 }
3289
3290 #[cfg(debug_assertions)]
3292 pub fn set_inspector_renderer(&mut self, f: crate::InspectorRenderer) {
3293 self.inspector_renderer = Some(f);
3294 }
3295
3296 #[cfg(debug_assertions)]
3298 pub fn register_inspector_element<T: 'static, R: crate::IntoElement, F>(
3299 &mut self,
3300 factory: impl 'static + Fn(&mut Window, &mut App) -> F,
3301 ) where
3302 F: 'static + FnMut(crate::InspectorElementId, &T, &mut Window, &mut App) -> R,
3303 {
3304 self.inspector_element_registry.register(factory);
3305 }
3306
3307 pub fn init_colors(&mut self) {
3311 self.set_global(GlobalColors(Arc::new(Colors::default())));
3312 }
3313
3314 #[inline(never)]
3315 fn update_window_erased(
3316 &mut self,
3317 window_id: WindowId,
3318 update: &mut dyn FnMut(Option<(AnyView, &mut Window, &mut App)>),
3319 ) {
3320 self.update(|cx| {
3321 let Some(mut window) = cx.windows.get_mut(window_id).and_then(Option::take) else {
3322 update(None);
3323 return;
3324 };
3325
3326 let root_view = window.root.clone().unwrap();
3327
3328 cx.window_update_stack.push(window.handle.id);
3329 update(Some((root_view, &mut window, cx)));
3330 fn trail(window_id: WindowId, window: Box<Window>, cx: &mut App) -> Option<()> {
3331 cx.window_update_stack.pop();
3332
3333 if window.removed {
3334 cx.end_platform_drag(window_id);
3335 cx.window_handles.remove(&window_id);
3336 cx.windows.remove(window_id);
3337 if let Some(tracked) = cx.tracked_entities.remove(&window_id) {
3338 for entity_id in tracked {
3339 if let Some(windows) =
3340 cx.window_invalidators_by_entity.get_mut(&entity_id)
3341 {
3342 windows.remove(&window_id);
3343 }
3344 if cx.current_window_by_entity.get(&entity_id) == Some(&window_id) {
3345 cx.current_window_by_entity.remove(&entity_id);
3346 }
3347 }
3348 }
3349
3350 cx.window_closed_observers.clone().retain(|callback| {
3351 callback(cx, window_id);
3352 true
3353 });
3354
3355 let quit_on_empty = match cx.quit_mode {
3356 QuitMode::Explicit => false,
3357 QuitMode::LastWindowClosed => true,
3358 QuitMode::Default => cfg!(not(target_os = "macos")),
3359 };
3360
3361 if quit_on_empty && cx.windows.is_empty() {
3362 cx.quit();
3363 }
3364 } else {
3365 cx.windows.get_mut(window_id)?.replace(window);
3366 }
3367 Some(())
3368 }
3369 if trail(window_id, window, cx).is_none() {
3370 update(None);
3371 }
3372 });
3373 }
3374
3375 #[inline(never)]
3376 fn update_entity_erased(
3377 &mut self,
3378 handle: &AnyEntity,
3379 entity_type: &str,
3380 update: &mut dyn FnMut(&mut dyn Any, &mut App),
3381 ) {
3382 self.update(|cx| {
3383 let mut lease = cx.entities.lease_erased(handle, entity_type);
3384 update(lease.entity.as_deref_mut().unwrap(), cx);
3385 cx.entities.end_lease_erased(handle.entity_id, lease);
3386 });
3387 }
3388
3389 #[inline(never)]
3390 #[track_caller]
3391 fn prepare_spawn(&self) -> AsyncApp {
3392 if self.quitting {
3393 debug_panic!("Can't spawn on main thread after on_app_quit")
3394 };
3395 self.to_async()
3396 }
3397}
3398
3399impl AppContext for App {
3400 fn new<T: 'static>(&mut self, build_entity: impl FnOnce(&mut Context<T>) -> T) -> Entity<T> {
3405 self.update(|cx| {
3406 let slot = cx.entities.reserve();
3407 let handle = slot.clone();
3408 let entity = build_entity(&mut Context::new_context(cx, slot.downgrade()));
3409
3410 cx.push_effect(Effect::EntityCreated {
3411 entity: handle.into_any(),
3412 tid: TypeId::of::<T>(),
3413 window: cx.window_update_stack.last().cloned(),
3414 });
3415
3416 cx.entities.insert(slot, entity)
3417 })
3418 }
3419
3420 fn reserve_entity<T: 'static>(&mut self) -> Reservation<T> {
3421 Reservation(self.entities.reserve())
3422 }
3423
3424 fn insert_entity<T: 'static>(
3425 &mut self,
3426 reservation: Reservation<T>,
3427 build_entity: impl FnOnce(&mut Context<T>) -> T,
3428 ) -> Entity<T> {
3429 self.update(|cx| {
3430 let slot = reservation.0;
3431 let entity = build_entity(&mut Context::new_context(cx, slot.downgrade()));
3432 cx.entities.insert(slot, entity)
3433 })
3434 }
3435
3436 #[inline(always)]
3439 fn update_entity<T: 'static, R>(
3440 &mut self,
3441 handle: &Entity<T>,
3442 update: impl FnOnce(&mut T, &mut Context<T>) -> R,
3443 ) -> R {
3444 let mut update = Some(update);
3445 let mut result = None;
3446 self.update_entity_erased(handle, type_name::<T>(), &mut |entity, cx| {
3447 let value = update.take().unwrap()(
3448 entity.downcast_mut::<T>().unwrap(),
3449 &mut Context::new_context(cx, handle.downgrade()),
3450 );
3451 result = Some(value);
3452 });
3453 result.unwrap()
3454 }
3455
3456 fn as_mut<'a, T>(&'a mut self, handle: &Entity<T>) -> GpuiBorrow<'a, T>
3457 where
3458 T: 'static,
3459 {
3460 GpuiBorrow::new(handle.clone(), self)
3461 }
3462
3463 #[inline(always)]
3464 fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
3465 where
3466 T: 'static,
3467 {
3468 let entity = self.entities.read(handle);
3469 read(entity, self)
3470 }
3471
3472 fn update_window<T, F>(&mut self, handle: AnyWindowHandle, update: F) -> Result<T>
3473 where
3474 F: FnOnce(AnyView, &mut Window, &mut App) -> T,
3475 {
3476 self.update_window_id(handle.id, update)
3477 }
3478
3479 fn with_window<R>(
3480 &mut self,
3481 entity_id: EntityId,
3482 f: impl FnOnce(&mut Window, &mut App) -> R,
3483 ) -> Option<R> {
3484 App::with_window(self, entity_id, f)
3485 }
3486
3487 fn read_window<T, R>(
3488 &self,
3489 window: &WindowHandle<T>,
3490 read: impl FnOnce(Entity<T>, &App) -> R,
3491 ) -> Result<R>
3492 where
3493 T: 'static,
3494 {
3495 let window = self
3496 .windows
3497 .get(window.id)
3498 .context("window not found")?
3499 .as_deref()
3500 .expect("attempted to read a window that is already on the stack");
3501
3502 let root_view = window.root.clone().unwrap();
3503 let view = root_view
3504 .downcast::<T>()
3505 .map_err(|_| anyhow!("root view's type has changed"))?;
3506
3507 Ok(read(view, self))
3508 }
3509
3510 fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
3511 where
3512 R: Send + 'static,
3513 {
3514 self.background_executor.spawn(future)
3515 }
3516
3517 fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
3518 where
3519 G: Global,
3520 {
3521 let g = self.global::<G>();
3522 callback(g, self)
3523 }
3524}
3525
3526pub(crate) enum Effect {
3528 Notify {
3529 emitter: EntityId,
3530 },
3531 Emit {
3532 emitter: EntityId,
3533 event_type: TypeId,
3534 event: ArenaBox<dyn Any>,
3535 },
3536 RefreshWindows,
3537 NotifyGlobalObservers {
3538 global_type: TypeId,
3539 },
3540 Defer {
3541 callback: Box<dyn FnOnce(&mut App) + 'static>,
3542 },
3543 EntityCreated {
3544 entity: AnyEntity,
3545 tid: TypeId,
3546 window: Option<WindowId>,
3547 },
3548}
3549
3550impl std::fmt::Debug for Effect {
3551 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3552 match self {
3553 Effect::Notify { emitter } => write!(f, "Notify({})", emitter),
3554 Effect::Emit { emitter, .. } => write!(f, "Emit({:?})", emitter),
3555 Effect::RefreshWindows => write!(f, "RefreshWindows"),
3556 Effect::NotifyGlobalObservers { global_type } => {
3557 write!(f, "NotifyGlobalObservers({:?})", global_type)
3558 }
3559 Effect::Defer { .. } => write!(f, "Defer(..)"),
3560 Effect::EntityCreated { entity, .. } => write!(f, "EntityCreated({:?})", entity),
3561 }
3562 }
3563}
3564
3565pub(crate) struct GlobalLease<G: Global> {
3567 global: Box<dyn Any>,
3568 global_type: PhantomData<G>,
3569}
3570
3571impl<G: Global> GlobalLease<G> {
3572 fn new(global: Box<dyn Any>) -> Self {
3573 GlobalLease {
3574 global,
3575 global_type: PhantomData,
3576 }
3577 }
3578}
3579
3580impl<G: Global> Deref for GlobalLease<G> {
3581 type Target = G;
3582
3583 fn deref(&self) -> &Self::Target {
3584 self.global.downcast_ref().unwrap()
3585 }
3586}
3587
3588impl<G: Global> DerefMut for GlobalLease<G> {
3589 fn deref_mut(&mut self) -> &mut Self::Target {
3590 self.global.downcast_mut().unwrap()
3591 }
3592}
3593
3594pub struct AnyDrag {
3597 pub view: AnyView,
3599
3600 pub value: Arc<dyn Any>,
3602
3603 pub cursor_offset: Point<Pixels>,
3606
3607 pub cursor_style: Option<CursorStyle>,
3609
3610 pub external_payload_source: Option<ExternalDragPayloadSource>,
3613}
3614
3615pub type ExternalDragPayloadSource =
3618 Box<dyn FnOnce(&mut Window, &mut App) -> Option<ExternalDragPayload> + 'static>;
3619
3620#[derive(Clone)]
3623pub struct AnyTooltip {
3624 pub view: AnyView,
3626
3627 pub mouse_position: Point<Pixels>,
3629
3630 pub check_visible_and_update: Rc<dyn Fn(Bounds<Pixels>, &mut Window, &mut App) -> bool>,
3634}
3635
3636#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3638pub enum InputPreference {
3639 CharacterInput,
3641 KeyBindings,
3643}
3644
3645#[derive(Debug)]
3647pub struct KeystrokeEvent {
3648 pub keystroke: Keystroke,
3650
3651 pub input_preference: InputPreference,
3659
3660 pub action: Option<Box<dyn Action>>,
3662
3663 pub context_stack: Vec<KeyContext>,
3665}
3666
3667struct NullHttpClient;
3668
3669impl HttpClient for NullHttpClient {
3670 fn send(
3671 &self,
3672 _req: http_client::Request<http_client::AsyncBody>,
3673 ) -> futures::future::BoxFuture<
3674 'static,
3675 anyhow::Result<http_client::Response<http_client::AsyncBody>>,
3676 > {
3677 async move {
3678 anyhow::bail!("No HttpClient available");
3679 }
3680 .boxed()
3681 }
3682
3683 fn user_agent(&self) -> Option<&http_client::http::HeaderValue> {
3684 None
3685 }
3686
3687 fn proxy(&self) -> Option<&Url> {
3688 None
3689 }
3690}
3691
3692pub struct GpuiBorrow<'a, T> {
3694 inner: Option<Lease<T>>,
3695 app: &'a mut App,
3696}
3697
3698impl<'a, T: 'static> GpuiBorrow<'a, T> {
3699 fn new(inner: Entity<T>, app: &'a mut App) -> Self {
3700 app.start_update();
3701 let lease = app.entities.lease(&inner);
3702 Self {
3703 inner: Some(lease),
3704 app,
3705 }
3706 }
3707}
3708
3709impl<'a, T: 'static> std::borrow::Borrow<T> for GpuiBorrow<'a, T> {
3710 fn borrow(&self) -> &T {
3711 self.inner.as_ref().unwrap().borrow()
3712 }
3713}
3714
3715impl<'a, T: 'static> std::borrow::BorrowMut<T> for GpuiBorrow<'a, T> {
3716 fn borrow_mut(&mut self) -> &mut T {
3717 self.inner.as_mut().unwrap().borrow_mut()
3718 }
3719}
3720
3721impl<'a, T: 'static> std::ops::Deref for GpuiBorrow<'a, T> {
3722 type Target = T;
3723
3724 fn deref(&self) -> &Self::Target {
3725 self.inner.as_ref().unwrap()
3726 }
3727}
3728
3729impl<'a, T: 'static> std::ops::DerefMut for GpuiBorrow<'a, T> {
3730 fn deref_mut(&mut self) -> &mut T {
3731 self.inner.as_mut().unwrap()
3732 }
3733}
3734
3735impl<'a, T> Drop for GpuiBorrow<'a, T> {
3736 fn drop(&mut self) {
3737 let lease = self.inner.take().unwrap();
3738 self.app.notify(lease.id);
3739 self.app.entities.end_lease(lease);
3740 self.app.finish_update();
3741 }
3742}
3743
3744#[cfg(test)]
3745mod test {
3746 use std::{
3747 cell::{Cell, RefCell},
3748 ffi::OsString,
3749 path::PathBuf,
3750 rc::Rc,
3751 };
3752
3753 #[cfg(unix)]
3754 use std::os::unix::ffi::OsStringExt;
3755
3756 use crate::{
3757 AppContext, Context, Empty, FallbackFontClass, IntoElement, MissingGlyph, Render,
3758 TestAppContext, Window,
3759 };
3760
3761 #[crate::test]
3762 fn gpu_device_lost_reaches_subscribers_until_dropped(cx: &mut TestAppContext) {
3763 let reasons = Rc::new(RefCell::new(Vec::new()));
3764 let subscription = cx.update(|app| {
3765 let reasons = reasons.clone();
3766 app.on_gpu_device_lost(move |lost, _| {
3767 reasons.borrow_mut().push(lost.reason.clone());
3768 })
3769 });
3770 cx.test_platform()
3771 .simulate_gpu_device_lost(crate::GpuDeviceLost::new("device removed"));
3772 assert_eq!(*reasons.borrow(), ["device removed"]);
3773 drop(subscription);
3774 cx.test_platform()
3775 .simulate_gpu_device_lost(crate::GpuDeviceLost::new("again"));
3776 assert_eq!(reasons.borrow().len(), 1);
3777 }
3778
3779 fn construction_platform() -> Rc<crate::TestPlatform> {
3780 let dispatcher = std::sync::Arc::new(crate::TestDispatcher::new(0));
3781 crate::TestPlatform::new(
3782 crate::BackgroundExecutor::new(dispatcher.clone()),
3783 crate::ForegroundExecutor::new(dispatcher),
3784 )
3785 }
3786
3787 #[test]
3788 fn application_construction_uses_text_override_without_querying_platform() {
3789 use std::sync::Arc;
3790
3791 let platform = construction_platform();
3792 let backend: Arc<dyn crate::PlatformTextSystem> = Arc::new(crate::NoopTextSystem);
3793 let native_references = Arc::strong_count(&platform.text_system);
3794 let app =
3795 super::Application::with_platform_and_text_system(platform.clone(), backend.clone());
3796
3797 assert_eq!(platform.text_system_calls.get(), 0);
3798 assert_eq!(Arc::strong_count(&platform.text_system), native_references);
3799 assert_eq!(Arc::strong_count(&backend), 2);
3800 drop(app);
3801 assert_eq!(Arc::strong_count(&backend), 1);
3802 }
3803
3804 #[test]
3805 fn application_construction_uses_platform_text_by_default() {
3806 use std::sync::Arc;
3807
3808 let platform = construction_platform();
3809 let native_references = Arc::strong_count(&platform.text_system);
3810 let app = super::Application::with_platform(platform.clone());
3811
3812 assert_eq!(platform.text_system_calls.get(), 1);
3813 assert_eq!(
3814 Arc::strong_count(&platform.text_system),
3815 native_references + 1
3816 );
3817 drop(app);
3818 assert_eq!(Arc::strong_count(&platform.text_system), native_references);
3819 }
3820
3821 #[gpui::test]
3822 fn app_borrow_diagnostics(cx: &mut TestAppContext) {
3823 use std::panic::{AssertUnwindSafe, catch_unwind};
3824
3825 let app = cx.app.clone();
3826 let first_line = line!() + 1;
3827 let first = app.borrow();
3828 let second = app.borrow();
3829 drop(first);
3830 assert!(app.try_borrow_mut().is_err());
3831 let attempted_line = line!() + 1;
3832 let panic = catch_unwind(AssertUnwindSafe(|| drop(app.borrow_mut())));
3833 let message = *panic
3834 .expect_err("conflicting borrow")
3835 .downcast::<String>()
3836 .unwrap();
3837 assert!(message.contains(&format!(
3838 "mutable App borrow at {}:{attempted_line}:",
3839 file!()
3840 )));
3841 assert!(message.contains(&format!(
3842 "shared borrow group began at {}:{first_line}:",
3843 file!()
3844 )));
3845 drop(second);
3846
3847 let origin_line = line!() + 2;
3848 let panic = catch_unwind(AssertUnwindSafe(|| {
3849 let _borrow = app.try_borrow_mut().expect("initial borrow");
3850 drop(app.borrow());
3851 }));
3852 let message = *panic
3853 .expect_err("conflicting borrow")
3854 .downcast::<String>()
3855 .unwrap();
3856 assert!(message.contains("attempted shared App borrow"));
3857 assert!(message.contains(&format!(
3858 "mutable borrow group began at {}:{origin_line}:",
3859 file!()
3860 )));
3861
3862 let next_line = line!() + 1;
3863 let _borrow = app.borrow();
3864 assert_eq!(
3865 app.borrow_origin.get().expect("new origin").0.line(),
3866 next_line
3867 );
3868 }
3869
3870 struct RenderCounter(Rc<Cell<usize>>);
3871
3872 impl Render for RenderCounter {
3873 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
3874 self.0.set(self.0.get() + 1);
3875 Empty
3876 }
3877 }
3878
3879 #[gpui::test]
3880 fn async_app_refresh_flushes_refresh_effect(cx: &mut TestAppContext) {
3881 let render_count = Rc::new(Cell::new(0));
3882
3883 let _window = cx.add_window({
3884 let render_count = render_count.clone();
3885 move |_, _| RenderCounter(render_count)
3886 });
3887
3888 cx.run_until_parked();
3889 let render_count_before_refresh = render_count.get();
3890
3891 cx.to_async().refresh();
3892
3893 assert_eq!(render_count.get(), render_count_before_refresh + 1);
3894 }
3895
3896 #[gpui::test]
3897 fn missing_glyph_callbacks_follow_subscription_lifetime(cx: &mut TestAppContext) {
3898 let observed = Rc::new(RefCell::new(Vec::new()));
3899 let subscription = cx.update(|cx| {
3900 let observed = observed.clone();
3901 cx.on_missing_glyphs(move |missing_glyphs, _| {
3902 observed.borrow_mut().extend_from_slice(missing_glyphs);
3903 })
3904 });
3905 cx.update(|cx| {
3906 cx.text_system()
3907 .report_missing_glyphs_in_test(vec![missing_glyph("active")]);
3908 });
3909 cx.run_until_parked();
3910 assert_eq!(observed.borrow().as_slice(), &[missing_glyph("active")]);
3911
3912 let second_observed = Rc::new(RefCell::new(Vec::new()));
3913 let second_subscription = cx.update(|cx| {
3914 let second_observed = second_observed.clone();
3915 cx.on_missing_glyphs(move |missing_glyphs, _| {
3916 second_observed
3917 .borrow_mut()
3918 .extend_from_slice(missing_glyphs);
3919 })
3920 });
3921 cx.update(|cx| {
3922 cx.text_system()
3923 .report_missing_glyphs_in_test(vec![missing_glyph("replacement")]);
3924 });
3925 cx.run_until_parked();
3926 assert_eq!(observed.borrow().as_slice(), &[missing_glyph("active")]);
3927 assert_eq!(
3928 second_observed.borrow().as_slice(),
3929 &[missing_glyph("replacement")]
3930 );
3931
3932 drop(subscription);
3933 cx.update(|cx| {
3934 cx.text_system()
3935 .report_missing_glyphs_in_test(vec![missing_glyph("after old drop")]);
3936 });
3937 cx.run_until_parked();
3938 assert_eq!(
3939 second_observed.borrow().as_slice(),
3940 &[
3941 missing_glyph("replacement"),
3942 missing_glyph("after old drop")
3943 ]
3944 );
3945
3946 drop(second_subscription);
3947 cx.update(|cx| {
3948 cx.text_system()
3949 .report_missing_glyphs_in_test(vec![missing_glyph("inactive")]);
3950 });
3951 cx.run_until_parked();
3952 assert_eq!(second_observed.borrow().len(), 2);
3953 }
3954
3955 #[test]
3956 fn test_gpui_borrow() {
3957 let cx = TestAppContext::single();
3958 let observation_count = Rc::new(RefCell::new(0));
3959
3960 let state = cx.update(|cx| {
3961 let state = cx.new(|_| false);
3962 cx.observe(&state, {
3963 let observation_count = observation_count.clone();
3964 move |_, _| {
3965 let mut count = observation_count.borrow_mut();
3966 *count += 1;
3967 }
3968 })
3969 .detach();
3970
3971 state
3972 });
3973
3974 cx.update(|cx| {
3975 *std::borrow::BorrowMut::borrow_mut(&mut state.as_mut(cx)) = true;
3977 });
3978
3979 cx.update(|cx| {
3980 state.write(cx, false);
3981 });
3982
3983 assert_eq!(*observation_count.borrow(), 2);
3984 }
3985
3986 #[gpui::test]
3987 async fn test_restart_preserves_path_and_arguments(cx: &mut TestAppContext) {
3988 #[cfg(unix)]
3989 let user_data_dir = OsString::from_vec(b"/tmp/zed data/\xff".to_vec());
3990 #[cfg(not(unix))]
3991 let user_data_dir = OsString::from("C:\\zed data");
3992 let arguments = vec![OsString::from("--user-data-dir"), user_data_dir];
3993 let restart_path = PathBuf::from("updated-zed");
3994 let _application =
3995 super::Application(cx.app.clone()).with_restart_arguments(arguments.clone());
3996 let restart = cx.expect_restart();
3997
3998 cx.update(|cx| {
3999 cx.set_restart_path(restart_path.clone());
4000 cx.restart();
4001 });
4002
4003 let (path, restart_arguments) = restart.await.expect("restart was not requested");
4004 assert_eq!(path, Some(restart_path));
4005 assert_eq!(restart_arguments, arguments);
4006 }
4007
4008 fn missing_glyph(grapheme: &'static str) -> MissingGlyph {
4009 MissingGlyph::new(grapheme.into(), FallbackFontClass::Proportional)
4010 }
4011}