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, atomic::Ordering::SeqCst},
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, MetricReport,
26 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(any(feature = "inspector", debug_assertions))]
44use crate::InspectorElementRegistry;
45use crate::asset_cache::CachedLoad;
46use crate::{
47 Action, ActionBuildError, ActionRegistry, ActivityGuard, Any, AnyView, AnyWindowHandle,
48 AppContext, Arena, ArenaBox, Asset, AssetSource, BackgroundExecutor, Bounds, ClipboardItem,
49 ClipboardReadError, CursorStyle, DispatchPhase, DisplayId, EventEmitter, ExternalDragPayload,
50 FocusHandle, FocusMap, ForegroundExecutor, Global, KeyBinding, KeyContext, Keymap, Keystroke,
51 LayoutId, Menu, MenuItem, MissingGlyph, OwnedMenu, PathPromptOptions, Pixels, Platform,
52 PlatformDisplay, PlatformKeyboardLayout, PlatformKeyboardMapper, Point, Priority,
53 PromptBuilder, PromptButton, PromptHandle, PromptLevel, Render, RenderImage,
54 RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString, SubscriberSet,
55 Subscription, SvgRenderer, SystemNotification, SystemNotificationResponse, Task,
56 TextRenderingMode, TextSystem, ThermalState, Window, WindowAppearance, WindowButtonLayout,
57 WindowHandle, WindowId, WindowInvalidator,
58 colors::{Colors, GlobalColors},
59 hash, init_app_menus,
60};
61
62mod async_context;
63#[cfg(feature = "bench-support")]
64mod bench_context;
65mod context;
66mod entity_map;
67#[cfg(any(test, feature = "test-support"))]
68mod headless_app_context;
69#[cfg(any(test, feature = "test-support"))]
70mod test_app;
71#[cfg(any(test, feature = "test-support"))]
72mod test_context;
73#[cfg(all(target_os = "macos", any(test, feature = "test-support")))]
74mod visual_test_context;
75
76pub const SHUTDOWN_TIMEOUT: Duration = Duration::from_millis(200);
79
80#[doc(hidden)]
83pub struct AppCell {
84 app: RefCell<App>,
85}
86
87impl AppCell {
88 #[doc(hidden)]
89 #[track_caller]
90 pub fn borrow(&self) -> AppRef<'_> {
91 if option_env!("TRACK_THREAD_BORROWS").is_some() {
92 let thread_id = std::thread::current().id();
93 eprintln!("borrowed {thread_id:?}");
94 }
95 AppRef(self.app.borrow())
96 }
97
98 #[doc(hidden)]
99 #[track_caller]
100 pub fn borrow_mut(&self) -> AppRefMut<'_> {
101 if option_env!("TRACK_THREAD_BORROWS").is_some() {
102 let thread_id = std::thread::current().id();
103 eprintln!("borrowed {thread_id:?}");
104 }
105 AppRefMut(self.app.borrow_mut())
106 }
107
108 #[doc(hidden)]
109 #[track_caller]
110 pub fn try_borrow_mut(&self) -> Result<AppRefMut<'_>, BorrowMutError> {
111 if option_env!("TRACK_THREAD_BORROWS").is_some() {
112 let thread_id = std::thread::current().id();
113 eprintln!("borrowed {thread_id:?}");
114 }
115 Ok(AppRefMut(self.app.try_borrow_mut()?))
116 }
117}
118
119#[doc(hidden)]
120#[derive(Deref, DerefMut)]
121pub struct AppRef<'a>(Ref<'a, App>);
122
123impl Drop for AppRef<'_> {
124 fn drop(&mut self) {
125 if option_env!("TRACK_THREAD_BORROWS").is_some() {
126 let thread_id = std::thread::current().id();
127 eprintln!("dropped borrow from {thread_id:?}");
128 }
129 }
130}
131
132#[doc(hidden)]
133#[derive(Deref, DerefMut)]
134pub struct AppRefMut<'a>(RefMut<'a, App>);
135
136impl Drop for AppRefMut<'_> {
137 fn drop(&mut self) {
138 if option_env!("TRACK_THREAD_BORROWS").is_some() {
139 let thread_id = std::thread::current().id();
140 eprintln!("dropped {thread_id:?}");
141 }
142 }
143}
144
145pub struct Application(Rc<AppCell>);
148
149pub struct ApplicationHandle {
155 app: Rc<AppCell>,
156}
157
158impl ApplicationHandle {
159 pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> R {
163 let cx = &mut *self.app.borrow_mut();
164 f(cx)
165 }
166
167 pub fn to_async(&self) -> AsyncApp {
170 self.update(|cx| cx.to_async())
171 }
172}
173
174impl Application {
177 pub fn with_platform(platform: Rc<dyn Platform>) -> Self {
179 Self(App::new_app(
180 platform,
181 Arc::new(()),
182 Arc::new(NullHttpClient),
183 ))
184 }
185
186 pub fn new_inaccessible(platform: Rc<dyn Platform>) -> Self {
196 let this = Self::with_platform(platform);
197 this.0.borrow_mut().accessibility_force_disabled = true;
198 this
199 }
200
201 pub fn with_assets(self, asset_source: impl AssetSource) -> Self {
203 let mut context_lock = self.0.borrow_mut();
204 let asset_source = Arc::new(asset_source);
205 context_lock.asset_source = asset_source.clone();
206 context_lock.svg_renderer = SvgRenderer::new(asset_source);
207 drop(context_lock);
208 self
209 }
210
211 pub fn with_restart_arguments(self, arguments: Vec<OsString>) -> Self {
213 self.0.borrow_mut().restart_arguments = arguments;
214 self
215 }
216
217 pub fn with_http_client(self, http_client: Arc<dyn HttpClient>) -> Self {
219 let mut context_lock = self.0.borrow_mut();
220 context_lock.http_client = http_client;
221 drop(context_lock);
222 self
223 }
224
225 pub fn with_quit_mode(self, mode: QuitMode) -> Self {
228 self.0.borrow_mut().quit_mode = mode;
229 self
230 }
231
232 pub fn run<F>(self, on_finish_launching: F)
238 where
239 F: 'static + FnOnce(&mut App),
240 {
241 let this = self.0.clone();
242 let platform = self.0.borrow().platform.clone();
243 platform.run(Box::new(move || {
244 let cx = &mut *this.borrow_mut();
245 on_finish_launching(cx);
246 }));
247
248 #[cfg(target_family = "wasm")]
249 std::mem::forget(self);
250 }
251
252 pub fn run_embedded<F>(self, on_finish_launching: F) -> ApplicationHandle
262 where
263 F: 'static + FnOnce(&mut App),
264 {
265 let this = self.0.clone();
266 let platform = self.0.borrow().platform.clone();
267 platform.run(Box::new(move || {
268 let cx = &mut *this.borrow_mut();
269 on_finish_launching(cx);
270 }));
271 ApplicationHandle { app: self.0 }
272 }
273
274 pub fn on_open_urls<F>(&self, mut callback: F) -> &Self
277 where
278 F: 'static + FnMut(Vec<String>),
279 {
280 self.0.borrow().platform.on_open_urls(Box::new(callback));
281 self
282 }
283
284 pub fn on_reopen<F>(&self, mut callback: F) -> &Self
287 where
288 F: 'static + FnMut(&mut App),
289 {
290 let this = Rc::downgrade(&self.0);
291 self.0.borrow_mut().platform.on_reopen(Box::new(move || {
292 if let Some(app) = this.upgrade() {
293 callback(&mut app.borrow_mut());
294 }
295 }));
296 self
297 }
298
299 pub fn background_executor(&self) -> BackgroundExecutor {
301 self.0.borrow().background_executor.clone()
302 }
303
304 pub fn foreground_executor(&self) -> ForegroundExecutor {
306 self.0.borrow().foreground_executor.clone()
307 }
308
309 pub fn text_system(&self) -> Arc<TextSystem> {
311 self.0.borrow().text_system.clone()
312 }
313
314 pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
316 self.0.borrow().path_for_auxiliary_executable(name)
317 }
318}
319
320type Handler = Box<dyn FnMut(&mut App) -> bool + 'static>;
321type Listener = Box<dyn FnMut(&dyn Any, &mut App) -> bool + 'static>;
322type MissingGlyphCallback = Box<dyn FnMut(&[MissingGlyph], &mut App) + 'static>;
323pub(crate) type KeystrokeObserver =
324 Box<dyn FnMut(&KeystrokeEvent, &mut Window, &mut App) -> bool + 'static>;
325type QuitHandler = Box<dyn FnOnce(&mut App) -> LocalBoxFuture<'static, ()> + 'static>;
326type WindowClosedHandler = Box<dyn FnMut(&mut App, WindowId)>;
327type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut App) + 'static>;
328type NewEntityListener = Box<dyn FnMut(AnyEntity, &mut Option<&mut Window>, &mut App) + 'static>;
329
330struct MissingGlyphCallbackEntry {
331 registration: Rc<()>,
332 callback: Option<MissingGlyphCallback>,
333}
334
335#[derive(Default)]
336struct MissingGlyphCallbackSlot {
337 entry: RefCell<Option<MissingGlyphCallbackEntry>>,
338}
339
340impl MissingGlyphCallbackSlot {
341 fn replace(&self, callback: MissingGlyphCallback) -> Rc<()> {
342 let registration = Rc::new(());
343 self.entry.borrow_mut().replace(MissingGlyphCallbackEntry {
344 registration: registration.clone(),
345 callback: Some(callback),
346 });
347 registration
348 }
349
350 fn invoke(&self, missing_glyphs: &[MissingGlyph], cx: &mut App) {
351 let Some((registration, mut callback)) =
352 self.entry.borrow_mut().as_mut().and_then(|entry| {
353 entry
354 .callback
355 .take()
356 .map(|callback| (entry.registration.clone(), callback))
357 })
358 else {
359 return;
360 };
361 callback(missing_glyphs, cx);
362
363 let mut entry = self.entry.borrow_mut();
364 let is_current = entry
365 .as_ref()
366 .is_some_and(|entry| Rc::ptr_eq(&entry.registration, ®istration));
367 if is_current {
368 let Some(entry) = entry.as_mut() else {
369 return;
370 };
371 entry.callback = Some(callback);
372 }
373 }
374
375 fn remove(&self, registration: &Rc<()>) -> bool {
376 let mut entry = self.entry.borrow_mut();
377 let is_current = entry
378 .as_ref()
379 .is_some_and(|entry| Rc::ptr_eq(&entry.registration, registration));
380 if is_current {
381 entry.take();
382 }
383 is_current
384 }
385}
386
387#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
389pub enum QuitMode {
390 #[default]
392 Default,
393 LastWindowClosed,
395 Explicit,
397}
398
399#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
404pub enum CursorHideMode {
405 Never,
407 OnTyping,
409 #[default]
412 OnTypingAndAction,
413}
414
415#[doc(hidden)]
416#[derive(Clone, PartialEq, Eq)]
417pub struct SystemWindowTab {
418 pub id: WindowId,
419 pub title: SharedString,
420 pub handle: AnyWindowHandle,
421 pub last_active_at: Instant,
422}
423
424impl SystemWindowTab {
425 pub fn new(title: SharedString, handle: AnyWindowHandle) -> Self {
427 Self {
428 id: handle.id,
429 title,
430 handle,
431 last_active_at: Instant::now(),
432 }
433 }
434}
435
436#[derive(Default)]
438pub struct SystemWindowTabController {
439 visible: Option<bool>,
440 tab_groups: FxHashMap<usize, Vec<SystemWindowTab>>,
441}
442
443impl Global for SystemWindowTabController {}
444
445impl SystemWindowTabController {
446 pub fn new() -> Self {
448 Self {
449 visible: None,
450 tab_groups: FxHashMap::default(),
451 }
452 }
453
454 pub fn init(cx: &mut App) {
456 cx.set_global(SystemWindowTabController::new());
457 }
458
459 pub fn tab_groups(&self) -> &FxHashMap<usize, Vec<SystemWindowTab>> {
461 &self.tab_groups
462 }
463
464 pub fn get_next_tab_group_window(cx: &mut App, id: WindowId) -> Option<&AnyWindowHandle> {
466 let controller = cx.global::<SystemWindowTabController>();
467 let current_group = controller
468 .tab_groups
469 .iter()
470 .find_map(|(group, tabs)| tabs.iter().find(|tab| tab.id == id).map(|_| group));
471
472 let current_group = current_group?;
473 let mut group_ids: Vec<_> = controller.tab_groups.keys().collect();
475 let idx = group_ids.iter().position(|g| *g == current_group)?;
476 let next_idx = (idx + 1) % group_ids.len();
477
478 controller
479 .tab_groups
480 .get(group_ids[next_idx])
481 .and_then(|tabs| {
482 tabs.iter()
483 .max_by_key(|tab| tab.last_active_at)
484 .or_else(|| tabs.first())
485 .map(|tab| &tab.handle)
486 })
487 }
488
489 pub fn get_prev_tab_group_window(cx: &mut App, id: WindowId) -> Option<&AnyWindowHandle> {
491 let controller = cx.global::<SystemWindowTabController>();
492 let current_group = controller
493 .tab_groups
494 .iter()
495 .find_map(|(group, tabs)| tabs.iter().find(|tab| tab.id == id).map(|_| group));
496
497 let current_group = current_group?;
498 let mut group_ids: Vec<_> = controller.tab_groups.keys().collect();
500 let idx = group_ids.iter().position(|g| *g == current_group)?;
501 let prev_idx = if idx == 0 {
502 group_ids.len() - 1
503 } else {
504 idx - 1
505 };
506
507 controller
508 .tab_groups
509 .get(group_ids[prev_idx])
510 .and_then(|tabs| {
511 tabs.iter()
512 .max_by_key(|tab| tab.last_active_at)
513 .or_else(|| tabs.first())
514 .map(|tab| &tab.handle)
515 })
516 }
517
518 pub fn tabs(&self, id: WindowId) -> Option<&Vec<SystemWindowTab>> {
520 self.tab_groups
521 .values()
522 .find(|tabs| tabs.iter().any(|tab| tab.id == id))
523 }
524
525 pub fn init_visible(cx: &mut App, visible: bool) {
527 let mut controller = cx.global_mut::<SystemWindowTabController>();
528 if controller.visible.is_none() {
529 controller.visible = Some(visible);
530 }
531 }
532
533 pub fn is_visible(&self) -> bool {
535 self.visible.unwrap_or(false)
536 }
537
538 pub fn set_visible(cx: &mut App, visible: bool) {
540 let mut controller = cx.global_mut::<SystemWindowTabController>();
541 controller.visible = Some(visible);
542 }
543
544 pub fn update_last_active(cx: &mut App, id: WindowId) {
546 let mut controller = cx.global_mut::<SystemWindowTabController>();
547 for windows in controller.tab_groups.values_mut() {
548 for tab in windows.iter_mut() {
549 if tab.id == id {
550 tab.last_active_at = Instant::now();
551 }
552 }
553 }
554 }
555
556 pub fn update_tab_position(cx: &mut App, id: WindowId, ix: usize) {
558 let mut controller = cx.global_mut::<SystemWindowTabController>();
559 for (_, windows) in controller.tab_groups.iter_mut() {
560 if let Some(current_pos) = windows.iter().position(|tab| tab.id == id) {
561 if ix < windows.len() && current_pos != ix {
562 let window_tab = windows.remove(current_pos);
563 windows.insert(ix, window_tab);
564 }
565 break;
566 }
567 }
568 }
569
570 pub fn update_tab_title(cx: &mut App, id: WindowId, title: SharedString) {
572 let controller = cx.global::<SystemWindowTabController>();
573 let tab = controller
574 .tab_groups
575 .values()
576 .flat_map(|windows| windows.iter())
577 .find(|tab| tab.id == id);
578
579 if tab.map_or(true, |t| t.title == title) {
580 return;
581 }
582
583 let mut controller = cx.global_mut::<SystemWindowTabController>();
584 for windows in controller.tab_groups.values_mut() {
585 for tab in windows.iter_mut() {
586 if tab.id == id {
587 tab.title = title;
588 return;
589 }
590 }
591 }
592 }
593
594 pub fn add_tab(cx: &mut App, id: WindowId, tabs: Vec<SystemWindowTab>) {
596 let mut controller = cx.global_mut::<SystemWindowTabController>();
597 let Some(tab) = tabs.iter().find(|tab| tab.id == id).cloned() else {
598 return;
599 };
600
601 let mut expected_tab_ids: Vec<_> = tabs
602 .iter()
603 .filter(|tab| tab.id != id)
604 .map(|tab| tab.id)
605 .sorted()
606 .collect();
607
608 let mut tab_group_id = None;
609 for (group_id, group_tabs) in &controller.tab_groups {
610 let tab_ids: Vec<_> = group_tabs.iter().map(|tab| tab.id).sorted().collect();
611 if tab_ids == expected_tab_ids {
612 tab_group_id = Some(*group_id);
613 break;
614 }
615 }
616
617 if let Some(tab_group_id) = tab_group_id {
618 if let Some(tabs) = controller.tab_groups.get_mut(&tab_group_id) {
619 tabs.push(tab);
620 }
621 } else {
622 let new_group_id = controller.tab_groups.len();
623 controller.tab_groups.insert(new_group_id, tabs);
624 }
625 }
626
627 pub fn remove_tab(cx: &mut App, id: WindowId) -> Option<SystemWindowTab> {
629 let mut controller = cx.global_mut::<SystemWindowTabController>();
630 let mut removed_tab = None;
631
632 controller.tab_groups.retain(|_, tabs| {
633 if let Some(pos) = tabs.iter().position(|tab| tab.id == id) {
634 removed_tab = Some(tabs.remove(pos));
635 }
636 !tabs.is_empty()
637 });
638
639 removed_tab
640 }
641
642 pub fn move_tab_to_new_window(cx: &mut App, id: WindowId) {
644 let mut removed_tab = Self::remove_tab(cx, id);
645 let mut controller = cx.global_mut::<SystemWindowTabController>();
646
647 if let Some(tab) = removed_tab {
648 let new_group_id = controller.tab_groups.keys().max().map_or(0, |k| k + 1);
649 controller.tab_groups.insert(new_group_id, vec![tab]);
650 }
651 }
652
653 pub fn merge_all_windows(cx: &mut App, id: WindowId) {
655 let mut controller = cx.global_mut::<SystemWindowTabController>();
656 let Some(initial_tabs) = controller.tabs(id) else {
657 return;
658 };
659
660 let initial_tabs_len = initial_tabs.len();
661 let mut all_tabs = initial_tabs.clone();
662
663 for (_, mut tabs) in controller.tab_groups.drain() {
664 tabs.retain(|tab| !all_tabs[..initial_tabs_len].contains(tab));
665 all_tabs.extend(tabs);
666 }
667
668 controller.tab_groups.insert(0, all_tabs);
669 }
670
671 pub fn select_next_tab(cx: &mut App, id: WindowId) {
673 let mut controller = cx.global_mut::<SystemWindowTabController>();
674 let Some(tabs) = controller.tabs(id) else {
675 return;
676 };
677
678 let current_index = tabs.iter().position(|tab| tab.id == id).unwrap();
679 let next_index = (current_index + 1) % tabs.len();
680
681 let _ = &tabs[next_index].handle.update(cx, |_, window, _| {
682 window.activate_window();
683 });
684 }
685
686 pub fn select_previous_tab(cx: &mut App, id: WindowId) {
688 let mut controller = cx.global_mut::<SystemWindowTabController>();
689 let Some(tabs) = controller.tabs(id) else {
690 return;
691 };
692
693 let current_index = tabs.iter().position(|tab| tab.id == id).unwrap();
694 let previous_index = if current_index == 0 {
695 tabs.len() - 1
696 } else {
697 current_index - 1
698 };
699
700 let _ = &tabs[previous_index].handle.update(cx, |_, window, _| {
701 window.activate_window();
702 });
703 }
704}
705
706pub(crate) enum GpuiMode {
707 #[cfg(any(test, feature = "test-support"))]
708 Test {
709 skip_drawing: bool,
710 },
711 Production,
712}
713
714impl GpuiMode {
715 #[cfg(any(test, feature = "test-support"))]
716 pub fn test() -> Self {
717 GpuiMode::Test {
718 skip_drawing: false,
719 }
720 }
721
722 #[inline]
723 pub(crate) fn skip_drawing(&self) -> bool {
724 match self {
725 #[cfg(any(test, feature = "test-support"))]
726 GpuiMode::Test { skip_drawing } => *skip_drawing,
727 GpuiMode::Production => false,
728 }
729 }
730}
731
732struct PlatformOwnedDrag {
733 source_window: WindowId,
734 state: PlatformOwnedDragState,
735}
736
737enum PlatformOwnedDragState {
738 Suspended(AnyDrag),
739 RestoredInSourceWindow,
742}
743
744pub struct App {
748 pub(crate) this: Weak<AppCell>,
749 pub(crate) platform: Rc<dyn Platform>,
750 text_system: Arc<TextSystem>,
751
752 pub(crate) actions: Rc<ActionRegistry>,
753 pub(crate) active_drag: Option<AnyDrag>,
754 platform_owned_drag: Option<PlatformOwnedDrag>,
755 pub(crate) background_executor: BackgroundExecutor,
756 pub(crate) foreground_executor: ForegroundExecutor,
757 #[cfg(feature = "profiler")]
758 foreground_journal: crate::profiler::journal::ForegroundJournal,
759 pub(crate) entities: EntityMap,
760 pub(crate) new_entity_observers: SubscriberSet<TypeId, NewEntityListener>,
761 pub(crate) windows: SlotMap<WindowId, Option<Box<Window>>>,
762 pub(crate) window_handles: FxHashMap<WindowId, AnyWindowHandle>,
763 pub(crate) focus_handles: Arc<FocusMap>,
764 pub(crate) keymap: Rc<RefCell<Keymap>>,
765 pub(crate) keyboard_layout: Box<dyn PlatformKeyboardLayout>,
766 pub(crate) keyboard_mapper: Rc<dyn PlatformKeyboardMapper>,
767 pub(crate) global_action_listeners:
768 TypeIdHashMap<Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
769 pending_effects: VecDeque<Effect>,
770
771 pub(crate) observers: SubscriberSet<EntityId, Handler>,
772 pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>,
773 pub(crate) keystroke_observers: SubscriberSet<(), KeystrokeObserver>,
774 pub(crate) keystroke_interceptors: SubscriberSet<(), KeystrokeObserver>,
775 pub(crate) keyboard_layout_observers: SubscriberSet<(), Handler>,
776 missing_glyph_callback: Rc<MissingGlyphCallbackSlot>,
777 pub(crate) thermal_state_observers: SubscriberSet<(), Handler>,
778 pub(crate) system_sleep_observers: SubscriberSet<(), Handler>,
779 pub(crate) system_wake_observers: SubscriberSet<(), Handler>,
780 pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>,
781 pub(crate) global_observers: SubscriberSet<TypeId, Handler>,
782 pub(crate) quit_observers: SubscriberSet<(), QuitHandler>,
783 pub(crate) restart_observers: SubscriberSet<(), Handler>,
784 pub(crate) window_closed_observers: SubscriberSet<(), WindowClosedHandler>,
785
786 pub(crate) element_arena: RefCell<Arena>,
789 pub(crate) event_arena: Arena,
791
792 pub(crate) globals_by_type: TypeIdHashMap<Box<dyn Any>>,
797
798 pub(crate) loading_assets: FxHashMap<(TypeId, u64), Box<dyn Any>>,
800 asset_source: Arc<dyn AssetSource>,
801 pub(crate) svg_renderer: SvgRenderer,
802 http_client: Arc<dyn HttpClient>,
803
804 pub(crate) pending_notifications: FxHashSet<EntityId>,
806 pub(crate) pending_global_notifications: TypeIdHashSet,
807 pub(crate) restart_path: Option<PathBuf>,
808 pub(crate) restart_arguments: Vec<OsString>,
809 pub(crate) layout_id_buffer: Vec<LayoutId>, pub(crate) propagate_event: bool,
811 pub(crate) prompt_builder: Option<PromptBuilder>,
812 pub(crate) window_invalidators_by_entity:
813 FxHashMap<EntityId, FxHashMap<WindowId, WindowInvalidator>>,
814 pub(crate) tracked_entities: FxHashMap<WindowId, FxHashSet<EntityId>>,
815 pub(crate) current_window_by_entity: FxHashMap<EntityId, WindowId>,
816 #[cfg(any(feature = "inspector", debug_assertions))]
817 pub(crate) inspector_renderer: Option<crate::InspectorRenderer>,
818 #[cfg(any(feature = "inspector", debug_assertions))]
819 pub(crate) inspector_element_registry: InspectorElementRegistry,
820 #[cfg(any(test, feature = "test-support", debug_assertions))]
821 pub(crate) name: Option<&'static str>,
822 pub(crate) text_rendering_mode: Rc<Cell<TextRenderingMode>>,
823
824 pub(crate) window_update_stack: Vec<WindowId>,
825 pub(crate) mode: GpuiMode,
826 pub(crate) cursor_hide_mode: CursorHideMode,
827 pub(crate) reduce_motion: bool,
828 pub(crate) synced_animation_epoch: Instant,
830 pub(crate) accessibility_force_disabled: bool,
833 flushing_effects: bool,
834 pending_updates: usize,
835 quit_mode: QuitMode,
836 quitting: bool,
837
838 #[cfg(any(test, feature = "leak-detection"))]
841 _ref_counts: Arc<RwLock<EntityRefCounts>>,
842}
843
844impl App {
845 #[allow(clippy::new_ret_no_self)]
846 pub(crate) fn new_app(
847 platform: Rc<dyn Platform>,
848 asset_source: Arc<dyn AssetSource>,
849 http_client: Arc<dyn HttpClient>,
850 ) -> Rc<AppCell> {
851 let background_executor = platform.background_executor();
852 let foreground_executor = platform.foreground_executor();
853 assert!(
854 background_executor.is_main_thread(),
855 "must construct App on main thread"
856 );
857 #[cfg(feature = "profiler")]
858 let foreground_journal = crate::profiler::journal::install_foreground_journal();
859 let synced_animation_epoch = background_executor.now();
860
861 let text_system = Arc::new(TextSystem::new(platform.text_system()));
862 let entities = EntityMap::new();
863 let keyboard_layout = platform.keyboard_layout();
864 let keyboard_mapper = platform.keyboard_mapper();
865
866 #[cfg(any(test, feature = "leak-detection"))]
867 let _ref_counts = entities.ref_counts_drop_handle();
868
869 let app = Rc::new_cyclic(|this| AppCell {
870 app: RefCell::new(App {
871 this: this.clone(),
872 platform: platform.clone(),
873 text_system,
874 text_rendering_mode: Rc::new(Cell::new(TextRenderingMode::default())),
875 mode: GpuiMode::Production,
876 actions: Rc::new(ActionRegistry::default()),
877 flushing_effects: false,
878 pending_updates: 0,
879 active_drag: None,
880 platform_owned_drag: None,
881 background_executor,
882 foreground_executor,
883 #[cfg(feature = "profiler")]
884 foreground_journal,
885 svg_renderer: SvgRenderer::new(asset_source.clone()),
886 loading_assets: Default::default(),
887 asset_source,
888 http_client,
889 globals_by_type: Default::default(),
890 entities,
891 new_entity_observers: SubscriberSet::new(),
892 windows: SlotMap::with_key(),
893 window_update_stack: Vec::new(),
894 window_handles: FxHashMap::default(),
895 focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
896 keymap: Rc::new(RefCell::new(Keymap::default())),
897 keyboard_layout,
898 keyboard_mapper,
899 global_action_listeners: Default::default(),
900 pending_effects: VecDeque::new(),
901 pending_notifications: FxHashSet::default(),
902 pending_global_notifications: Default::default(),
903 observers: SubscriberSet::new(),
904 tracked_entities: FxHashMap::default(),
905 window_invalidators_by_entity: FxHashMap::default(),
906 current_window_by_entity: FxHashMap::default(),
907 event_listeners: SubscriberSet::new(),
908 release_listeners: SubscriberSet::new(),
909 keystroke_observers: SubscriberSet::new(),
910 keystroke_interceptors: SubscriberSet::new(),
911 keyboard_layout_observers: SubscriberSet::new(),
912 missing_glyph_callback: Rc::default(),
913 thermal_state_observers: SubscriberSet::new(),
914 system_sleep_observers: SubscriberSet::new(),
915 system_wake_observers: SubscriberSet::new(),
916 global_observers: SubscriberSet::new(),
917 quit_observers: SubscriberSet::new(),
918 restart_observers: SubscriberSet::new(),
919 restart_path: None,
920 restart_arguments: Vec::new(),
921 window_closed_observers: SubscriberSet::new(),
922 layout_id_buffer: Default::default(),
923 propagate_event: true,
924 prompt_builder: Some(PromptBuilder::Default),
925 #[cfg(any(feature = "inspector", debug_assertions))]
926 inspector_renderer: None,
927 #[cfg(any(feature = "inspector", debug_assertions))]
928 inspector_element_registry: InspectorElementRegistry::default(),
929 quit_mode: QuitMode::default(),
930 quitting: false,
931 cursor_hide_mode: CursorHideMode::default(),
932 reduce_motion: false,
933 synced_animation_epoch,
934 accessibility_force_disabled: false,
935
936 #[cfg(any(test, feature = "test-support", debug_assertions))]
937 name: None,
938 element_arena: RefCell::new(Arena::new(1024 * 1024)),
939 event_arena: Arena::new(1024 * 1024),
940
941 #[cfg(any(test, feature = "leak-detection"))]
942 _ref_counts,
943 }),
944 });
945
946 init_app_menus(platform.as_ref(), &app.borrow());
947 SystemWindowTabController::init(&mut app.borrow_mut());
948 #[cfg(feature = "profiler")]
949 crate::profiler::journal::observe_power(&app.borrow());
950
951 platform.on_keyboard_layout_change(Box::new({
952 let app = Rc::downgrade(&app);
953 move || {
954 if let Some(app) = app.upgrade() {
955 let cx = &mut app.borrow_mut();
956 cx.keyboard_layout = cx.platform.keyboard_layout();
957 cx.keyboard_mapper = cx.platform.keyboard_mapper();
958 cx.keyboard_layout_observers
959 .clone()
960 .retain(&(), move |callback| (callback)(cx));
961 }
962 }
963 }));
964
965 platform.on_thermal_state_change(Box::new({
966 let app = Rc::downgrade(&app);
967 move || {
968 if let Some(app) = app.upgrade() {
969 let cx = &mut app.borrow_mut();
970 cx.thermal_state_observers
971 .clone()
972 .retain(&(), move |callback| (callback)(cx));
973 }
974 }
975 }));
976
977 platform.on_system_sleep(Box::new({
978 let app = Rc::downgrade(&app);
979 move || {
980 if let Some(app) = app.upgrade() {
981 let cx = &mut app.borrow_mut();
982 cx.system_sleep_observers
983 .clone()
984 .retain(&(), move |callback| (callback)(cx));
985 }
986 }
987 }));
988
989 platform.on_system_wake(Box::new({
990 let app = Rc::downgrade(&app);
991 move || {
992 if let Some(app) = app.upgrade() {
993 let cx = &mut app.borrow_mut();
994 cx.system_wake_observers
995 .clone()
996 .retain(&(), move |callback| (callback)(cx));
997 }
998 }
999 }));
1000
1001 platform.on_quit(Box::new({
1002 let cx = Rc::downgrade(&app);
1003 move || {
1004 let Some(cx) = cx.upgrade() else {
1005 return true;
1006 };
1007 match cx.try_borrow_mut() {
1008 Ok(mut cx) => {
1009 cx.shutdown();
1010 true
1011 }
1012 Err(_) => {
1013 false
1016 }
1017 }
1018 }
1019 }));
1020
1021 app
1022 }
1023
1024 #[doc(hidden)]
1025 pub fn ref_counts_drop_handle(&self) -> impl Sized + use<> {
1026 self.entities.ref_counts_drop_handle()
1027 }
1028
1029 #[cfg(any(test, feature = "leak-detection"))]
1035 pub fn leak_detector_snapshot(&self) -> LeakDetectorSnapshot {
1036 self.entities.leak_detector_snapshot()
1037 }
1038
1039 #[cfg(any(test, feature = "leak-detection"))]
1051 pub fn assert_no_new_leaks(&self, snapshot: &LeakDetectorSnapshot) {
1052 self.entities.assert_no_new_leaks(snapshot)
1053 }
1054
1055 pub fn shutdown(&mut self) {
1061 let mut futures = Vec::new();
1062
1063 for observer in self.quit_observers.remove(&()) {
1064 futures.push(observer(self));
1065 }
1066
1067 self.windows.clear();
1068 self.window_handles.clear();
1069 self.flush_effects();
1070 self.quitting = true;
1071
1072 let futures = futures::future::join_all(futures);
1073 #[cfg(not(target_family = "wasm"))]
1074 if self
1075 .foreground_executor
1076 .block_with_timeout(SHUTDOWN_TIMEOUT, futures)
1077 .is_err()
1078 {
1079 log::error!("timed out waiting on app_will_quit");
1080 }
1081 #[cfg(target_family = "wasm")]
1082 self.foreground_executor.spawn(futures).detach();
1083
1084 self.quitting = false;
1085 }
1086
1087 pub fn keyboard_layout(&self) -> &dyn PlatformKeyboardLayout {
1089 self.keyboard_layout.as_ref()
1090 }
1091
1092 pub fn keyboard_mapper(&self) -> &Rc<dyn PlatformKeyboardMapper> {
1094 &self.keyboard_mapper
1095 }
1096
1097 pub fn on_keyboard_layout_change<F>(&self, mut callback: F) -> Subscription
1099 where
1100 F: 'static + FnMut(&mut App),
1101 {
1102 let (subscription, activate) = self.keyboard_layout_observers.insert(
1103 (),
1104 Box::new(move |cx| {
1105 callback(cx);
1106 true
1107 }),
1108 );
1109 activate();
1110 subscription
1111 }
1112
1113 pub fn quit(&self) {
1115 self.platform.quit();
1116 }
1117
1118 pub fn cursor_hide_mode(&self) -> CursorHideMode {
1121 self.cursor_hide_mode
1122 }
1123
1124 pub fn set_cursor_hide_mode(&mut self, mode: CursorHideMode) {
1127 self.cursor_hide_mode = mode;
1128 }
1129
1130 pub fn is_cursor_visible(&self) -> bool {
1136 self.platform.is_cursor_visible()
1137 }
1138
1139 pub fn reduce_motion(&self) -> bool {
1142 self.reduce_motion
1143 }
1144
1145 pub fn set_reduce_motion(&mut self, reduce_motion: bool) {
1148 if self.reduce_motion != reduce_motion {
1149 self.reduce_motion = reduce_motion;
1150 self.refresh_windows();
1151 }
1152 }
1153
1154 pub fn refresh_windows(&mut self) {
1157 self.pending_effects.push_back(Effect::RefreshWindows);
1158 }
1159
1160 #[inline(always)]
1161 pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
1162 self.start_update();
1163 let result = update(self);
1164 self.finish_update();
1165 result
1166 }
1167
1168 pub(crate) fn start_update(&mut self) {
1169 self.pending_updates += 1;
1170 }
1171
1172 #[inline(never)]
1173 pub(crate) fn finish_update(&mut self) {
1174 if !self.flushing_effects && self.pending_updates == 1 {
1175 self.flushing_effects = true;
1176 self.flush_effects();
1177 self.flushing_effects = false;
1178 }
1179 self.pending_updates -= 1;
1180 }
1181
1182 pub fn observe<W>(
1184 &mut self,
1185 entity: &Entity<W>,
1186 mut on_notify: impl FnMut(Entity<W>, &mut App) + 'static,
1187 ) -> Subscription
1188 where
1189 W: 'static,
1190 {
1191 self.observe_internal(entity, move |e, cx| {
1192 on_notify(e, cx);
1193 true
1194 })
1195 }
1196
1197 pub(crate) fn detect_accessed_entities<R>(
1198 &mut self,
1199 callback: impl FnOnce(&mut App) -> R,
1200 ) -> (R, FxHashSet<EntityId>) {
1201 let accessed_entities_start = self.entities.accessed_entities.get_mut().clone();
1202 let result = callback(self);
1203 let entities_accessed_in_callback = self
1204 .entities
1205 .accessed_entities
1206 .get_mut()
1207 .difference(&accessed_entities_start)
1208 .copied()
1209 .collect::<FxHashSet<EntityId>>();
1210 (result, entities_accessed_in_callback)
1211 }
1212
1213 pub(crate) fn record_entities_accessed(
1214 &mut self,
1215 window_handle: AnyWindowHandle,
1216 invalidator: WindowInvalidator,
1217 entities: &FxHashSet<EntityId>,
1218 ) {
1219 let mut tracked_entities =
1220 std::mem::take(self.tracked_entities.entry(window_handle.id).or_default());
1221 for entity in tracked_entities.iter() {
1222 self.window_invalidators_by_entity
1223 .entry(*entity)
1224 .and_modify(|windows| {
1225 windows.remove(&window_handle.id);
1226 });
1227 }
1228 for entity in entities.iter() {
1229 self.window_invalidators_by_entity
1230 .entry(*entity)
1231 .or_default()
1232 .insert(window_handle.id, invalidator.clone());
1233 self.current_window_by_entity
1234 .insert(*entity, window_handle.id);
1235 }
1236 tracked_entities.clear();
1237 tracked_entities.extend(entities.iter().copied());
1238 self.tracked_entities
1239 .insert(window_handle.id, tracked_entities);
1240 }
1241
1242 pub(crate) fn new_observer(&mut self, key: EntityId, value: Handler) -> Subscription {
1243 let (subscription, activate) = self.observers.insert(key, value);
1244 self.defer(move |_| activate());
1245 subscription
1246 }
1247
1248 pub(crate) fn observe_internal<W>(
1249 &mut self,
1250 entity: &Entity<W>,
1251 mut on_notify: impl FnMut(Entity<W>, &mut App) -> bool + 'static,
1252 ) -> Subscription
1253 where
1254 W: 'static,
1255 {
1256 let entity_id = entity.entity_id();
1257 let handle = entity.downgrade();
1258 self.new_observer(
1259 entity_id,
1260 Box::new(move |cx| {
1261 if let Some(entity) = handle.upgrade() {
1262 on_notify(entity, cx)
1263 } else {
1264 false
1265 }
1266 }),
1267 )
1268 }
1269
1270 pub fn subscribe<T, Event>(
1273 &mut self,
1274 entity: &Entity<T>,
1275 mut on_event: impl FnMut(Entity<T>, &Event, &mut App) + 'static,
1276 ) -> Subscription
1277 where
1278 T: 'static + EventEmitter<Event>,
1279 Event: 'static,
1280 {
1281 self.subscribe_internal(entity, move |entity, event, cx| {
1282 on_event(entity, event, cx);
1283 true
1284 })
1285 }
1286
1287 pub(crate) fn new_subscription(
1288 &mut self,
1289 key: EntityId,
1290 value: (TypeId, Listener),
1291 ) -> Subscription {
1292 let (subscription, activate) = self.event_listeners.insert(key, value);
1293 self.defer(move |_| activate());
1294 subscription
1295 }
1296 pub(crate) fn subscribe_internal<T, Evt>(
1297 &mut self,
1298 entity: &Entity<T>,
1299 mut on_event: impl FnMut(Entity<T>, &Evt, &mut App) -> bool + 'static,
1300 ) -> Subscription
1301 where
1302 T: 'static + EventEmitter<Evt>,
1303 Evt: 'static,
1304 {
1305 let entity_id = entity.entity_id();
1306 let handle = entity.downgrade();
1307 self.new_subscription(
1308 entity_id,
1309 (
1310 TypeId::of::<Evt>(),
1311 Box::new(move |event, cx| {
1312 let event: &Evt = event.downcast_ref().expect("invalid event type");
1313 if let Some(entity) = handle.upgrade() {
1314 on_event(entity, event, cx)
1315 } else {
1316 false
1317 }
1318 }),
1319 ),
1320 )
1321 }
1322
1323 pub fn windows(&self) -> Vec<AnyWindowHandle> {
1327 self.windows
1328 .keys()
1329 .flat_map(|window_id| self.window_handles.get(&window_id).copied())
1330 .collect()
1331 }
1332
1333 pub fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
1339 self.platform.window_stack()
1340 }
1341
1342 pub fn active_window(&self) -> Option<AnyWindowHandle> {
1344 self.platform.active_window()
1345 }
1346
1347 pub fn open_window<V: 'static + Render>(
1351 &mut self,
1352 options: crate::WindowOptions,
1353 build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity<V>,
1354 ) -> anyhow::Result<WindowHandle<V>> {
1355 self.update(|cx| {
1356 let id = cx.windows.insert(None);
1357 let handle = WindowHandle::new(id);
1358 match Window::new(handle.into(), options, cx) {
1359 Ok(mut window) => {
1360 cx.window_update_stack.push(id);
1361 let root_view = build_root_view(&mut window, cx);
1362 cx.window_update_stack.pop();
1363 window.root.replace(root_view.into());
1364 window.defer(cx, |window: &mut Window, cx| window.appearance_changed(cx));
1365
1366 let clear = window.draw(cx);
1371 clear.clear(cx);
1372
1373 cx.window_handles.insert(id, window.handle);
1374 cx.windows.get_mut(id).unwrap().replace(Box::new(window));
1375 Ok(handle)
1376 }
1377 Err(e) => {
1378 cx.windows.remove(id);
1379 Err(e)
1380 }
1381 }
1382 })
1383 }
1384
1385 pub fn activate(&self, ignoring_other_apps: bool) {
1387 self.platform.activate(ignoring_other_apps);
1388 }
1389
1390 pub fn hide(&self) {
1392 self.platform.hide();
1393 }
1394
1395 pub fn hide_other_apps(&self) {
1397 self.platform.hide_other_apps();
1398 }
1399
1400 pub fn unhide_other_apps(&self) {
1402 self.platform.unhide_other_apps();
1403 }
1404
1405 pub fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
1407 self.platform.displays()
1408 }
1409
1410 pub fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
1412 self.platform.primary_display()
1413 }
1414
1415 pub fn is_screen_capture_supported(&self) -> bool {
1417 self.platform.is_screen_capture_supported()
1418 }
1419
1420 pub fn screen_capture_sources(
1422 &self,
1423 ) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
1424 self.platform.screen_capture_sources()
1425 }
1426
1427 pub fn find_display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
1429 self.displays()
1430 .iter()
1431 .find(|display| display.id() == id)
1432 .cloned()
1433 }
1434
1435 pub fn thermal_state(&self) -> ThermalState {
1437 self.platform.thermal_state()
1438 }
1439
1440 pub fn prevent_idle_sleep(&self, reason: &str) -> Task<Result<ActivityGuard>> {
1442 self.platform.prevent_idle_sleep(reason)
1443 }
1444
1445 pub fn on_thermal_state_change<F>(&self, mut callback: F) -> Subscription
1447 where
1448 F: 'static + FnMut(&mut App),
1449 {
1450 let (subscription, activate) = self.thermal_state_observers.insert(
1451 (),
1452 Box::new(move |cx| {
1453 callback(cx);
1454 true
1455 }),
1456 );
1457 activate();
1458 subscription
1459 }
1460
1461 pub fn on_system_sleep<F>(&self, mut callback: F) -> Subscription
1466 where
1467 F: 'static + FnMut(&mut App),
1468 {
1469 let (subscription, activate) = self.system_sleep_observers.insert(
1470 (),
1471 Box::new(move |cx| {
1472 callback(cx);
1473 true
1474 }),
1475 );
1476 activate();
1477 subscription
1478 }
1479
1480 pub fn on_system_wake<F>(&self, mut callback: F) -> Subscription
1482 where
1483 F: 'static + FnMut(&mut App),
1484 {
1485 let (subscription, activate) = self.system_wake_observers.insert(
1486 (),
1487 Box::new(move |cx| {
1488 callback(cx);
1489 true
1490 }),
1491 );
1492 activate();
1493 subscription
1494 }
1495
1496 pub fn window_appearance(&self) -> WindowAppearance {
1498 self.platform.window_appearance()
1499 }
1500
1501 pub fn set_window_appearance(&self, appearance: Option<WindowAppearance>) {
1512 self.platform.set_window_appearance(appearance);
1513 }
1514
1515 pub fn button_layout(&self) -> Option<WindowButtonLayout> {
1517 self.platform.button_layout()
1518 }
1519
1520 pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
1522 self.platform.read_from_clipboard()
1523 }
1524
1525 pub fn read_from_clipboard_async(
1533 &self,
1534 ) -> Task<Result<Option<ClipboardItem>, ClipboardReadError>> {
1535 self.platform.read_from_clipboard_async()
1536 }
1537
1538 pub fn set_text_rendering_mode(&mut self, mode: TextRenderingMode) {
1540 self.text_rendering_mode.set(mode);
1541 }
1542
1543 pub fn text_rendering_mode(&self) -> TextRenderingMode {
1545 self.text_rendering_mode.get()
1546 }
1547
1548 pub fn write_to_clipboard(&self, item: ClipboardItem) {
1550 self.platform.write_to_clipboard(item)
1551 }
1552
1553 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1556 pub fn read_from_primary(&self) -> Option<ClipboardItem> {
1557 self.platform.read_from_primary()
1558 }
1559
1560 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1563 pub fn write_to_primary(&self, item: ClipboardItem) {
1564 self.platform.write_to_primary(item)
1565 }
1566
1567 #[cfg(target_os = "macos")]
1573 pub fn read_from_find_pasteboard(&self) -> Option<ClipboardItem> {
1574 self.platform.read_from_find_pasteboard()
1575 }
1576
1577 #[cfg(target_os = "macos")]
1583 pub fn write_to_find_pasteboard(&self, item: ClipboardItem) {
1584 self.platform.write_to_find_pasteboard(item)
1585 }
1586
1587 pub fn write_credentials(
1589 &self,
1590 url: &str,
1591 username: &str,
1592 password: &[u8],
1593 ) -> Task<Result<()>> {
1594 self.platform.write_credentials(url, username, password)
1595 }
1596
1597 pub fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
1599 self.platform.read_credentials(url)
1600 }
1601
1602 pub fn delete_credentials(&self, url: &str) -> Task<Result<()>> {
1604 self.platform.delete_credentials(url)
1605 }
1606
1607 pub fn open_url(&self, url: &str) {
1609 self.platform.open_url(url);
1610 }
1611
1612 pub fn register_url_scheme(&self, scheme: &str) -> Task<Result<()>> {
1619 self.platform.register_url_scheme(scheme)
1620 }
1621
1622 pub fn set_app_identity(&self, identifier: &str, name: &str) {
1629 self.platform.set_app_identity(identifier, name);
1630 }
1631
1632 pub fn show_system_notification(&self, notification: SystemNotification) {
1639 self.platform.show_system_notification(notification);
1640 }
1641
1642 pub fn dismiss_system_notification(&self, tag: &str) {
1647 self.platform.dismiss_system_notification(tag);
1648 }
1649
1650 pub fn on_system_notification_response<F>(&self, mut callback: F)
1654 where
1655 F: 'static + FnMut(SystemNotificationResponse, &mut App),
1656 {
1657 let this = self.this.clone();
1658 self.platform
1659 .on_system_notification_response(Box::new(move |response| {
1660 if let Some(app) = this.upgrade() {
1661 callback(response, &mut app.borrow_mut());
1662 }
1663 }));
1664 }
1665
1666 pub fn app_path(&self) -> Result<PathBuf> {
1670 self.platform.app_path()
1671 }
1672
1673 pub fn compositor_name(&self) -> &'static str {
1677 self.platform.compositor_name()
1678 }
1679
1680 pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
1682 self.platform.path_for_auxiliary_executable(name)
1683 }
1684
1685 pub fn prompt_for_paths(
1691 &self,
1692 options: PathPromptOptions,
1693 ) -> oneshot::Receiver<Result<Option<Vec<PathBuf>>>> {
1694 self.platform.prompt_for_paths(options)
1695 }
1696
1697 pub fn prompt_for_new_path(
1704 &self,
1705 directory: &Path,
1706 suggested_name: Option<&str>,
1707 ) -> oneshot::Receiver<Result<Option<PathBuf>>> {
1708 self.platform.prompt_for_new_path(directory, suggested_name)
1709 }
1710
1711 pub fn reveal_path(&self, path: &Path) {
1713 self.platform.reveal_path(path)
1714 }
1715
1716 pub fn open_with_system(&self, path: &Path) {
1718 self.platform.open_with_system(path)
1719 }
1720
1721 pub fn should_auto_hide_scrollbars(&self) -> bool {
1723 self.platform.should_auto_hide_scrollbars()
1724 }
1725
1726 pub fn restart(&mut self) {
1728 self.restart_observers
1729 .clone()
1730 .retain(&(), |observer| observer(self));
1731 self.platform.restart(
1732 self.restart_path.take(),
1733 std::mem::take(&mut self.restart_arguments),
1734 )
1735 }
1736
1737 pub fn set_restart_path(&mut self, path: PathBuf) {
1739 self.restart_path = Some(path);
1740 }
1741
1742 pub fn http_client(&self) -> Arc<dyn HttpClient> {
1744 self.http_client.clone()
1745 }
1746
1747 pub fn set_http_client(&mut self, new_client: Arc<dyn HttpClient>) {
1749 self.http_client = new_client;
1750 }
1751
1752 pub fn set_quit_mode(&mut self, mode: QuitMode) {
1755 self.quit_mode = mode;
1756 }
1757
1758 pub fn svg_renderer(&self) -> SvgRenderer {
1760 self.svg_renderer.clone()
1761 }
1762
1763 pub(crate) fn push_effect(&mut self, effect: Effect) {
1764 match &effect {
1765 Effect::Notify { emitter } => {
1766 if !self.pending_notifications.insert(*emitter) {
1767 return;
1768 }
1769 }
1770 Effect::NotifyGlobalObservers { global_type } => {
1771 if !self.pending_global_notifications.insert(*global_type) {
1772 return;
1773 }
1774 }
1775 _ => {}
1776 };
1777
1778 self.pending_effects.push_back(effect);
1779 }
1780
1781 fn flush_effects(&mut self) {
1785 loop {
1786 self.release_dropped_entities();
1787 self.release_dropped_focus_handles();
1788 if let Some(effect) = self.pending_effects.pop_front() {
1789 match effect {
1790 Effect::Notify { emitter } => {
1791 self.apply_notify_effect(emitter);
1792 }
1793
1794 Effect::Emit {
1795 emitter,
1796 event_type,
1797 event,
1798 } => self.apply_emit_effect(emitter, event_type, &*event),
1799
1800 Effect::RefreshWindows => {
1801 self.apply_refresh_effect();
1802 }
1803
1804 Effect::NotifyGlobalObservers { global_type } => {
1805 self.apply_notify_global_observers_effect(global_type);
1806 }
1807
1808 Effect::Defer { callback } => {
1809 self.apply_defer_effect(callback);
1810 }
1811 Effect::EntityCreated {
1812 entity,
1813 tid,
1814 window,
1815 } => {
1816 self.apply_entity_created_effect(entity, tid, window);
1817 }
1818 }
1819 } else {
1820 #[cfg(any(test, feature = "test-support"))]
1821 if matches!(self.mode, GpuiMode::Test { .. }) {
1822 for window in self
1823 .windows
1824 .values()
1825 .filter_map(|window| {
1826 let window = window.as_deref()?;
1827 window.invalidator.is_dirty().then_some(window.handle)
1828 })
1829 .collect::<Vec<_>>()
1830 {
1831 self.update_window(window, |_, window, cx| window.draw(cx).clear(cx))
1832 .unwrap();
1833 }
1834 }
1835
1836 if self.pending_effects.is_empty() {
1837 for window in self.windows.values().filter_map(|window| window.as_deref()) {
1838 if window.invalidator.is_dirty()
1839 || window.needs_present.get()
1840 || !window.next_frame_callbacks.borrow().is_empty()
1841 {
1842 window.platform_window.schedule_frame();
1843 }
1844 }
1845
1846 self.event_arena.clear();
1847 break;
1848 }
1849 }
1850 }
1851 }
1852
1853 fn release_dropped_entities(&mut self) {
1857 loop {
1858 let dropped = self.entities.take_dropped();
1859 if dropped.is_empty() {
1860 break;
1861 }
1862
1863 for (entity_id, mut entity) in dropped {
1864 self.observers.remove(&entity_id);
1865 self.event_listeners.remove(&entity_id);
1866 self.window_invalidators_by_entity.remove(&entity_id);
1867 self.current_window_by_entity.remove(&entity_id);
1868 for release_callback in self.release_listeners.remove(&entity_id) {
1869 release_callback(entity.as_mut(), self);
1870 }
1871 }
1872 }
1873 }
1874
1875 fn release_dropped_focus_handles(&mut self) {
1877 self.focus_handles
1878 .clone()
1879 .write()
1880 .retain(|handle_id, focus| {
1881 if focus.ref_count.load(SeqCst) == 0 {
1882 for window_handle in self.windows() {
1883 window_handle
1884 .update(self, |_, window, cx| {
1885 if window.focus == Some(handle_id) {
1886 window.blur(cx);
1887 }
1888 })
1889 .unwrap();
1890 }
1891 false
1892 } else {
1893 true
1894 }
1895 });
1896 }
1897
1898 fn apply_notify_effect(&mut self, emitter: EntityId) {
1899 self.pending_notifications.remove(&emitter);
1900
1901 self.observers
1902 .clone()
1903 .retain(&emitter, |handler| handler(self));
1904 }
1905
1906 fn apply_emit_effect(&mut self, emitter: EntityId, event_type: TypeId, event: &dyn Any) {
1907 self.event_listeners
1908 .clone()
1909 .retain(&emitter, |(stored_type, handler)| {
1910 if *stored_type == event_type {
1911 handler(event, self)
1912 } else {
1913 true
1914 }
1915 });
1916 }
1917
1918 fn apply_refresh_effect(&mut self) {
1919 for window in self.windows.values_mut() {
1920 if let Some(window) = window.as_deref_mut() {
1921 window.refreshing = true;
1922 window.invalidator.set_dirty(true);
1923 }
1924 }
1925 }
1926
1927 fn apply_notify_global_observers_effect(&mut self, type_id: TypeId) {
1928 self.pending_global_notifications.remove(&type_id);
1929 self.global_observers
1930 .clone()
1931 .retain(&type_id, |observer| observer(self));
1932 }
1933
1934 fn apply_defer_effect(&mut self, callback: Box<dyn FnOnce(&mut Self) + 'static>) {
1935 callback(self);
1936 }
1937
1938 fn apply_entity_created_effect(
1939 &mut self,
1940 entity: AnyEntity,
1941 tid: TypeId,
1942 window: Option<WindowId>,
1943 ) {
1944 if let Some(id) = window {
1948 self.current_window_by_entity.insert(entity.entity_id(), id);
1949 }
1950
1951 self.new_entity_observers.clone().retain(&tid, |observer| {
1952 if let Some(id) = window {
1953 self.update_window_id(id, {
1954 let entity = entity.clone();
1955 |_, window, cx| (observer)(entity, &mut Some(window), cx)
1956 })
1957 .expect("All windows should be off the stack when flushing effects");
1958 } else {
1959 (observer)(entity.clone(), &mut None, self)
1960 }
1961 true
1962 });
1963 }
1964
1965 #[inline(always)]
1971 pub fn with_window<R>(
1972 &mut self,
1973 entity_id: EntityId,
1974 f: impl FnOnce(&mut Window, &mut App) -> R,
1975 ) -> Option<R> {
1976 let window_id = *self.current_window_by_entity.get(&entity_id)?;
1977 self.update_window_id(window_id, |_, window, cx| f(window, cx))
1978 .ok()
1979 }
1980
1981 fn ensure_window(&mut self, entity_id: EntityId, window: WindowId) {
1982 self.current_window_by_entity
1983 .entry(entity_id)
1984 .or_insert(window);
1985 }
1986
1987 #[inline(always)]
1988 pub(crate) fn update_window_id<T, F>(&mut self, id: WindowId, update: F) -> Result<T>
1989 where
1990 F: FnOnce(AnyView, &mut Window, &mut App) -> T,
1991 {
1992 let mut update = Some(update);
1993 let mut result = None;
1994 self.update_window_erased(id, &mut |arguments| {
1995 if let Some((root_view, window, cx)) = arguments {
1996 result = Some(update.take().unwrap()(root_view, window, cx));
1997 } else {
1998 drop(update.take());
1999 drop(result.take());
2000 }
2001 });
2002 result.context("window not found")
2003 }
2004
2005 pub fn to_async(&self) -> AsyncApp {
2008 AsyncApp {
2009 app: self.this.clone(),
2010 background_executor: self.background_executor.clone(),
2011 foreground_executor: self.foreground_executor.clone(),
2012 }
2013 }
2014
2015 pub fn background_executor(&self) -> &BackgroundExecutor {
2017 &self.background_executor
2018 }
2019
2020 pub fn foreground_executor(&self) -> &ForegroundExecutor {
2022 if self.quitting {
2023 panic!("Can't spawn on main thread after on_app_quit")
2024 };
2025 &self.foreground_executor
2026 }
2027
2028 #[cfg(feature = "profiler")]
2031 pub fn foreground_journal(&self) -> crate::profiler::journal::ForegroundJournal {
2032 self.foreground_journal.clone()
2033 }
2034
2035 #[track_caller]
2038 #[inline(always)]
2039 pub fn spawn<AsyncFn, R>(&self, f: AsyncFn) -> Task<R>
2040 where
2041 AsyncFn: AsyncFnOnce(&mut AsyncApp) -> R + 'static,
2042 R: 'static,
2043 {
2044 let mut cx = self.prepare_spawn();
2045
2046 self.foreground_executor
2047 .spawn(async move { f(&mut cx).await }.boxed_local())
2048 }
2049
2050 pub fn spawn_with_priority<AsyncFn, R>(&self, priority: Priority, f: AsyncFn) -> Task<R>
2054 where
2055 AsyncFn: AsyncFnOnce(&mut AsyncApp) -> R + 'static,
2056 R: 'static,
2057 {
2058 if self.quitting {
2059 debug_panic!("Can't spawn on main thread after on_app_quit")
2060 };
2061
2062 let mut cx = self.to_async();
2063
2064 self.foreground_executor
2065 .spawn_with_priority(priority, async move { f(&mut cx).await }.boxed_local())
2066 }
2067
2068 pub fn defer(&mut self, f: impl FnOnce(&mut App) + 'static) {
2071 self.push_effect(Effect::Defer {
2072 callback: Box::new(f),
2073 });
2074 }
2075
2076 pub fn asset_source(&self) -> &Arc<dyn AssetSource> {
2078 &self.asset_source
2079 }
2080
2081 pub fn text_system(&self) -> &Arc<TextSystem> {
2083 &self.text_system
2084 }
2085
2086 pub fn on_missing_glyphs(
2097 &self,
2098 callback: impl FnMut(&[MissingGlyph], &mut App) + 'static,
2099 ) -> Subscription {
2100 let registration = self.missing_glyph_callback.replace(Box::new(callback));
2101
2102 if let Some(mut receiver) = self.text_system.take_missing_glyph_receiver() {
2103 let callback = self.missing_glyph_callback.clone();
2104 self.spawn(async move |cx| {
2105 while let Ok(missing_glyphs) = receiver.recv().await {
2106 cx.update(|cx| callback.invoke(&missing_glyphs, cx));
2107 }
2108 })
2109 .detach();
2110 }
2111 self.text_system.enable_missing_glyph_reporting();
2112
2113 let callback = self.missing_glyph_callback.clone();
2114 let text_system = self.text_system.clone();
2115 Subscription::new(move || {
2116 if callback.remove(®istration) {
2117 text_system.disable_missing_glyph_reporting();
2118 }
2119 })
2120 }
2121
2122 pub fn has_global<G: Global>(&self) -> bool {
2124 self.globals_by_type.contains_key(&TypeId::of::<G>())
2125 }
2126
2127 #[track_caller]
2129 pub fn global<G: Global>(&self) -> &G {
2130 self.globals_by_type
2131 .get(&TypeId::of::<G>())
2132 .map(|any_state| any_state.downcast_ref::<G>().unwrap())
2133 .unwrap_or_else(|| panic!("no state of type {} exists", type_name::<G>()))
2134 }
2135
2136 pub fn try_global<G: Global>(&self) -> Option<&G> {
2138 self.globals_by_type
2139 .get(&TypeId::of::<G>())
2140 .map(|any_state| any_state.downcast_ref::<G>().unwrap())
2141 }
2142
2143 #[track_caller]
2145 pub fn global_mut<G: Global>(&mut self) -> &mut G {
2146 let global_type = TypeId::of::<G>();
2147 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2148 self.globals_by_type
2149 .get_mut(&global_type)
2150 .and_then(|any_state| any_state.downcast_mut::<G>())
2151 .unwrap_or_else(|| panic!("no state of type {} exists", type_name::<G>()))
2152 }
2153
2154 pub fn default_global<G: Global + Default>(&mut self) -> &mut G {
2157 let global_type = TypeId::of::<G>();
2158 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2159 self.globals_by_type
2160 .entry(global_type)
2161 .or_insert_with(|| Box::<G>::default())
2162 .downcast_mut::<G>()
2163 .unwrap()
2164 }
2165
2166 pub fn set_global<G: Global>(&mut self, global: G) {
2168 let global_type = TypeId::of::<G>();
2169 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2170 self.globals_by_type.insert(global_type, Box::new(global));
2171 }
2172
2173 #[cfg(any(test, feature = "test-support"))]
2175 pub fn clear_globals(&mut self) {
2176 self.globals_by_type.drain();
2177 }
2178
2179 pub fn remove_global<G: Global>(&mut self) -> G {
2181 let global_type = TypeId::of::<G>();
2182 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2183 *self
2184 .globals_by_type
2185 .remove(&global_type)
2186 .unwrap_or_else(|| panic!("no global added for {}", type_name::<G>()))
2187 .downcast()
2188 .unwrap()
2189 }
2190
2191 pub fn observe_global<G: Global>(
2193 &mut self,
2194 mut f: impl FnMut(&mut Self) + 'static,
2195 ) -> Subscription {
2196 let (subscription, activate) = self.global_observers.insert(
2197 TypeId::of::<G>(),
2198 Box::new(move |cx| {
2199 f(cx);
2200 true
2201 }),
2202 );
2203 self.defer(move |_| activate());
2204 subscription
2205 }
2206
2207 #[track_caller]
2209 pub(crate) fn lease_global<G: Global>(&mut self) -> GlobalLease<G> {
2210 GlobalLease::new(
2211 self.globals_by_type
2212 .remove(&TypeId::of::<G>())
2213 .with_context(|| format!("no global registered of type {}", type_name::<G>()))
2214 .unwrap(),
2215 )
2216 }
2217
2218 pub(crate) fn end_global_lease<G: Global>(&mut self, lease: GlobalLease<G>) {
2220 let global_type = TypeId::of::<G>();
2221
2222 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2223 self.globals_by_type.insert(global_type, lease.global);
2224 }
2225
2226 pub(crate) fn new_entity_observer(
2227 &self,
2228 key: TypeId,
2229 value: NewEntityListener,
2230 ) -> Subscription {
2231 let (subscription, activate) = self.new_entity_observers.insert(key, value);
2232 activate();
2233 subscription
2234 }
2235
2236 pub fn observe_new<T: 'static>(
2239 &self,
2240 on_new: impl 'static + Fn(&mut T, Option<&mut Window>, &mut Context<T>),
2241 ) -> Subscription {
2242 self.new_entity_observer(
2243 TypeId::of::<T>(),
2244 Box::new(
2245 move |any_entity: AnyEntity, window: &mut Option<&mut Window>, cx: &mut App| {
2246 any_entity
2247 .downcast::<T>()
2248 .unwrap()
2249 .update(cx, |entity_state, cx| {
2250 on_new(entity_state, window.as_deref_mut(), cx)
2251 })
2252 },
2253 ),
2254 )
2255 }
2256
2257 pub fn observe_release<T>(
2260 &self,
2261 handle: &Entity<T>,
2262 on_release: impl FnOnce(&mut T, &mut App) + 'static,
2263 ) -> Subscription
2264 where
2265 T: 'static,
2266 {
2267 let (subscription, activate) = self.release_listeners.insert(
2268 handle.entity_id(),
2269 Box::new(move |entity, cx| {
2270 let entity = entity.downcast_mut().expect("invalid entity type");
2271 on_release(entity, cx)
2272 }),
2273 );
2274 activate();
2275 subscription
2276 }
2277
2278 pub fn observe_release_in<T>(
2281 &self,
2282 handle: &Entity<T>,
2283 window: &Window,
2284 on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static,
2285 ) -> Subscription
2286 where
2287 T: 'static,
2288 {
2289 let window_handle = window.handle;
2290 self.observe_release(handle, move |entity, cx| {
2291 let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx));
2292 })
2293 }
2294
2295 pub fn observe_keystrokes(
2300 &mut self,
2301 mut f: impl FnMut(&KeystrokeEvent, &mut Window, &mut App) + 'static,
2302 ) -> Subscription {
2303 fn inner(
2304 keystroke_observers: &SubscriberSet<(), KeystrokeObserver>,
2305 handler: KeystrokeObserver,
2306 ) -> Subscription {
2307 let (subscription, activate) = keystroke_observers.insert((), handler);
2308 activate();
2309 subscription
2310 }
2311
2312 inner(
2313 &self.keystroke_observers,
2314 Box::new(move |event, window, cx| {
2315 f(event, window, cx);
2316 true
2317 }),
2318 )
2319 }
2320
2321 pub fn intercept_keystrokes(
2326 &mut self,
2327 mut f: impl FnMut(&KeystrokeEvent, &mut Window, &mut App) + 'static,
2328 ) -> Subscription {
2329 fn inner(
2330 keystroke_interceptors: &SubscriberSet<(), KeystrokeObserver>,
2331 handler: KeystrokeObserver,
2332 ) -> Subscription {
2333 let (subscription, activate) = keystroke_interceptors.insert((), handler);
2334 activate();
2335 subscription
2336 }
2337
2338 inner(
2339 &self.keystroke_interceptors,
2340 Box::new(move |event, window, cx| {
2341 f(event, window, cx);
2342 true
2343 }),
2344 )
2345 }
2346
2347 pub fn bind_keys(&mut self, bindings: impl IntoIterator<Item = KeyBinding>) {
2349 self.keymap.borrow_mut().add_bindings(bindings);
2350 self.pending_effects.push_back(Effect::RefreshWindows);
2351 }
2352
2353 pub fn clear_key_bindings(&mut self) {
2355 self.keymap.borrow_mut().clear();
2356 self.pending_effects.push_back(Effect::RefreshWindows);
2357 }
2358
2359 pub fn key_bindings(&self) -> Rc<RefCell<Keymap>> {
2361 self.keymap.clone()
2362 }
2363
2364 pub fn on_action<A: Action>(
2368 &mut self,
2369 listener: impl Fn(&A, &mut Self) + 'static,
2370 ) -> &mut Self {
2371 self.global_action_listeners
2372 .entry(TypeId::of::<A>())
2373 .or_default()
2374 .push(Rc::new(move |action, phase, cx| {
2375 if phase == DispatchPhase::Bubble {
2376 let action = action.downcast_ref().unwrap();
2377 listener(action, cx)
2378 }
2379 }));
2380 self
2381 }
2382
2383 pub fn stop_propagation(&mut self) {
2388 self.propagate_event = false;
2389 }
2390
2391 pub fn propagate(&mut self) {
2396 self.propagate_event = true;
2397 }
2398
2399 pub fn build_action(
2401 &self,
2402 name: &str,
2403 data: Option<serde_json::Value>,
2404 ) -> std::result::Result<Box<dyn Action>, ActionBuildError> {
2405 self.actions.build_action(name, data)
2406 }
2407
2408 pub fn all_action_names(&self) -> &[&'static str] {
2411 self.actions.all_action_names()
2412 }
2413
2414 pub fn all_bindings_for_input(&self, input: &[Keystroke]) -> Vec<KeyBinding> {
2418 RefCell::borrow(&self.keymap).all_bindings_for_input(input)
2419 }
2420
2421 pub fn action_schemas(
2423 &self,
2424 generator: &mut schemars::SchemaGenerator,
2425 ) -> Vec<(&'static str, Option<schemars::Schema>)> {
2426 self.actions.action_schemas(generator)
2427 }
2428
2429 pub fn action_schema_by_name(
2434 &self,
2435 name: &str,
2436 generator: &mut schemars::SchemaGenerator,
2437 ) -> Option<Option<schemars::Schema>> {
2438 self.actions.action_schema_by_name(name, generator)
2439 }
2440
2441 pub fn deprecated_actions_to_preferred_actions(&self) -> &HashMap<&'static str, &'static str> {
2443 self.actions.deprecated_aliases()
2444 }
2445
2446 pub fn action_deprecation_messages(&self) -> &HashMap<&'static str, &'static str> {
2448 self.actions.deprecation_messages()
2449 }
2450
2451 pub fn action_documentation(&self) -> &HashMap<&'static str, &'static str> {
2453 self.actions.documentation()
2454 }
2455
2456 pub fn on_app_quit<Fut>(
2459 &self,
2460 mut on_quit: impl FnMut(&mut App) -> Fut + 'static,
2461 ) -> Subscription
2462 where
2463 Fut: 'static + Future<Output = ()>,
2464 {
2465 let (subscription, activate) = self.quit_observers.insert(
2466 (),
2467 Box::new(move |cx| {
2468 let future = on_quit(cx);
2469 future.boxed_local()
2470 }),
2471 );
2472 activate();
2473 subscription
2474 }
2475
2476 pub fn on_app_restart(&self, mut on_restart: impl 'static + FnMut(&mut App)) -> Subscription {
2480 let (subscription, activate) = self.restart_observers.insert(
2481 (),
2482 Box::new(move |cx| {
2483 on_restart(cx);
2484 true
2485 }),
2486 );
2487 activate();
2488 subscription
2489 }
2490
2491 pub fn on_window_closed(
2494 &self,
2495 mut on_closed: impl FnMut(&mut App, WindowId) + 'static,
2496 ) -> Subscription {
2497 let (subscription, activate) = self.window_closed_observers.insert((), Box::new(on_closed));
2498 activate();
2499 subscription
2500 }
2501
2502 pub(crate) fn clear_pending_keystrokes(&mut self) {
2503 for window in self.windows() {
2504 window
2505 .update(self, |_, window, cx| {
2506 window.clear_pending_keystrokes(cx);
2507 })
2508 .ok();
2509 }
2510 }
2511
2512 pub fn is_action_available(&mut self, action: &dyn Action) -> bool {
2515 let mut action_available = false;
2516 if let Some(window) = self.active_window()
2517 && let Ok(window_action_available) =
2518 window.update(self, |_, window, cx| window.is_action_available(action, cx))
2519 {
2520 action_available = window_action_available;
2521 }
2522
2523 action_available
2524 || self
2525 .global_action_listeners
2526 .contains_key(&action.as_any().type_id())
2527 }
2528
2529 pub fn set_menus(&self, menus: impl IntoIterator<Item = Menu>) {
2531 let menus: Vec<Menu> = menus.into_iter().collect();
2532 self.platform.set_menus(menus, &self.keymap.borrow());
2533 }
2534
2535 pub fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
2537 self.platform.get_menus()
2538 }
2539
2540 pub fn set_dock_menu(&self, menus: Vec<MenuItem>) {
2542 self.platform.set_dock_menu(menus, &self.keymap.borrow())
2543 }
2544
2545 pub fn perform_dock_menu_action(&self, action: usize) {
2547 self.platform.perform_dock_menu_action(action);
2548 }
2549
2550 pub fn add_recent_document(&self, path: &Path) {
2555 self.platform.add_recent_document(path);
2556 }
2557
2558 pub fn update_jump_list(
2561 &self,
2562 menus: Vec<MenuItem>,
2563 entries: Vec<SmallVec<[PathBuf; 2]>>,
2564 ) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
2565 self.platform.update_jump_list(menus, entries)
2566 }
2567
2568 pub fn dispatch_action(&mut self, action: &dyn Action) {
2571 if let Some(active_window) = self.active_window() {
2572 active_window
2573 .update(self, |_, window, cx| {
2574 window.dispatch_action(action.boxed_clone(), cx)
2575 })
2576 .log_err();
2577 } else {
2578 self.dispatch_global_action(action);
2579 }
2580 }
2581
2582 fn dispatch_global_action(&mut self, action: &dyn Action) {
2583 self.propagate_event = true;
2584
2585 if let Some(mut global_listeners) = self
2586 .global_action_listeners
2587 .remove(&action.as_any().type_id())
2588 {
2589 for listener in &global_listeners {
2590 listener(action.as_any(), DispatchPhase::Capture, self);
2591 if !self.propagate_event {
2592 break;
2593 }
2594 }
2595
2596 global_listeners.extend(
2597 self.global_action_listeners
2598 .remove(&action.as_any().type_id())
2599 .unwrap_or_default(),
2600 );
2601
2602 self.global_action_listeners
2603 .insert(action.as_any().type_id(), global_listeners);
2604 }
2605
2606 if self.propagate_event
2607 && let Some(mut global_listeners) = self
2608 .global_action_listeners
2609 .remove(&action.as_any().type_id())
2610 {
2611 for listener in global_listeners.iter().rev() {
2612 listener(action.as_any(), DispatchPhase::Bubble, self);
2613 if !self.propagate_event {
2614 break;
2615 }
2616 }
2617
2618 global_listeners.extend(
2619 self.global_action_listeners
2620 .remove(&action.as_any().type_id())
2621 .unwrap_or_default(),
2622 );
2623
2624 self.global_action_listeners
2625 .insert(action.as_any().type_id(), global_listeners);
2626 }
2627 }
2628
2629 pub fn has_active_drag(&self) -> bool {
2631 self.active_drag.is_some()
2632 }
2633
2634 pub fn active_drag_cursor_style(&self) -> Option<CursorStyle> {
2636 self.active_drag.as_ref().and_then(|drag| drag.cursor_style)
2637 }
2638
2639 pub fn stop_active_drag(&mut self, window: &mut Window) -> bool {
2641 if self.active_drag.is_some() {
2642 self.active_drag = None;
2643 if self.platform_owned_drag.as_ref().is_some_and(|drag| {
2644 drag.source_window == window.window_handle().window_id()
2645 && matches!(&drag.state, PlatformOwnedDragState::RestoredInSourceWindow)
2646 }) {
2647 self.platform_owned_drag = None;
2648 }
2649 window.refresh();
2650 true
2651 } else {
2652 false
2653 }
2654 }
2655
2656 pub(crate) fn hand_active_drag_to_platform(&mut self, source_window: WindowId) -> bool {
2657 let Some(drag) = self.active_drag.take() else {
2658 return false;
2659 };
2660 self.platform_owned_drag = Some(PlatformOwnedDrag {
2661 source_window,
2662 state: PlatformOwnedDragState::Suspended(drag),
2663 });
2664 true
2665 }
2666
2667 pub(crate) fn restore_platform_drag(&mut self, source_window: WindowId) -> bool {
2668 let Some(platform_drag) = self
2669 .platform_owned_drag
2670 .as_mut()
2671 .filter(|drag| drag.source_window == source_window)
2672 else {
2673 return false;
2674 };
2675 let state = std::mem::replace(
2676 &mut platform_drag.state,
2677 PlatformOwnedDragState::RestoredInSourceWindow,
2678 );
2679 let PlatformOwnedDragState::Suspended(drag) = state else {
2680 return false;
2681 };
2682 self.active_drag = Some(drag);
2683 true
2684 }
2685
2686 pub(crate) fn hand_restored_drag_to_platform(&mut self, source_window: WindowId) -> bool {
2687 let Some(platform_drag) = self.platform_owned_drag.as_mut().filter(|drag| {
2688 drag.source_window == source_window
2689 && matches!(&drag.state, PlatformOwnedDragState::RestoredInSourceWindow)
2690 }) else {
2691 return false;
2692 };
2693 let Some(drag) = self.active_drag.take() else {
2694 return false;
2695 };
2696 platform_drag.state = PlatformOwnedDragState::Suspended(drag);
2697 true
2698 }
2699
2700 pub(crate) fn end_platform_drag(&mut self, source_window: WindowId) -> bool {
2701 if !self
2702 .platform_owned_drag
2703 .as_ref()
2704 .is_some_and(|drag| drag.source_window == source_window)
2705 {
2706 return false;
2707 }
2708 self.platform_owned_drag = None;
2709 self.active_drag = None;
2710 true
2711 }
2712
2713 pub fn set_active_drag_cursor_style(
2715 &mut self,
2716 cursor_style: CursorStyle,
2717 window: &mut Window,
2718 ) -> bool {
2719 if let Some(ref mut drag) = self.active_drag {
2720 drag.cursor_style = Some(cursor_style);
2721 window.refresh();
2722 true
2723 } else {
2724 false
2725 }
2726 }
2727
2728 pub fn set_prompt_builder(
2731 &mut self,
2732 renderer: impl Fn(
2733 PromptLevel,
2734 &str,
2735 Option<&str>,
2736 &[PromptButton],
2737 PromptHandle,
2738 &mut Window,
2739 &mut App,
2740 ) -> RenderablePromptHandle
2741 + 'static,
2742 ) {
2743 self.prompt_builder = Some(PromptBuilder::Custom(Box::new(renderer)));
2744 }
2745
2746 pub fn reset_prompt_builder(&mut self) {
2748 self.prompt_builder = Some(PromptBuilder::Default);
2749 }
2750
2751 pub fn remove_asset<A: Asset>(&mut self, source: &A::Source) {
2753 let asset_id = (TypeId::of::<A>(), hash(source));
2754 self.loading_assets.remove(&asset_id);
2755 }
2756
2757 #[cfg(any(test, feature = "test-support"))]
2760 pub fn has_asset<A: Asset>(&self, source: &A::Source) -> bool {
2761 let asset_id = (TypeId::of::<A>(), hash(source));
2762 self.loading_assets.contains_key(&asset_id)
2763 }
2764
2765 pub fn fetch_asset<A: Asset>(&mut self, source: &A::Source) -> Option<A::Output> {
2770 self.asset_entry::<A>(source).get()
2771 }
2772
2773 pub(crate) fn asset_entry<A: Asset>(&mut self, source: &A::Source) -> &CachedLoad<A::Output> {
2774 let asset_id = (TypeId::of::<A>(), hash(source));
2775 if !self.loading_assets.contains_key(&asset_id) {
2776 let future = A::load(source.clone(), self);
2777 let entry = CachedLoad::new(future, self);
2778 self.loading_assets.insert(asset_id, Box::new(entry));
2779 }
2780 self.loading_assets
2781 .get(&asset_id)
2782 .and_then(|entry| entry.downcast_ref())
2783 .expect("asset cache entries are keyed by their asset type")
2784 }
2785
2786 #[track_caller]
2789 pub fn focus_handle(&self) -> FocusHandle {
2790 FocusHandle::new(&self.focus_handles)
2791 }
2792
2793 pub fn notify(&mut self, entity_id: EntityId) {
2795 let window_invalidators = mem::take(
2796 self.window_invalidators_by_entity
2797 .entry(entity_id)
2798 .or_default(),
2799 );
2800
2801 let live_invalidators: SmallVec<[WindowInvalidator; 2]> = window_invalidators
2806 .iter()
2807 .filter(|(window_id, _)| {
2808 self.tracked_entities
2809 .get(window_id)
2810 .is_some_and(|set| set.contains(&entity_id))
2811 })
2812 .map(|(_, invalidator)| invalidator.clone())
2813 .collect();
2814
2815 if live_invalidators.is_empty() {
2816 if self.pending_notifications.insert(entity_id) {
2817 self.pending_effects
2818 .push_back(Effect::Notify { emitter: entity_id });
2819 }
2820 } else {
2821 for invalidator in &live_invalidators {
2822 invalidator.invalidate_view(entity_id, self);
2823 }
2824 }
2825
2826 self.window_invalidators_by_entity
2827 .insert(entity_id, window_invalidators);
2828 }
2829
2830 #[cfg(any(test, feature = "test-support", debug_assertions))]
2832 pub fn get_name(&self) -> Option<&'static str> {
2833 self.name
2834 }
2835
2836 pub fn can_select_mixed_files_and_dirs(&self) -> bool {
2838 self.platform.can_select_mixed_files_and_dirs()
2839 }
2840
2841 pub fn drop_image(&mut self, image: Arc<RenderImage>, current_window: Option<&mut Window>) {
2846 for window in self.windows.values_mut().flatten() {
2848 _ = window.drop_image(image.clone());
2849 }
2850
2851 if let Some(window) = current_window {
2853 _ = window.drop_image(image);
2854 }
2855 }
2856
2857 #[cfg(any(feature = "inspector", debug_assertions))]
2859 pub fn set_inspector_renderer(&mut self, f: crate::InspectorRenderer) {
2860 self.inspector_renderer = Some(f);
2861 }
2862
2863 #[cfg(any(feature = "inspector", debug_assertions))]
2865 pub fn register_inspector_element<T: 'static, R: crate::IntoElement, F>(
2866 &mut self,
2867 factory: impl 'static + Fn(&mut Window, &mut App) -> F,
2868 ) where
2869 F: 'static + FnMut(crate::InspectorElementId, &T, &mut Window, &mut App) -> R,
2870 {
2871 self.inspector_element_registry.register(factory);
2872 }
2873
2874 pub fn init_colors(&mut self) {
2878 self.set_global(GlobalColors(Arc::new(Colors::default())));
2879 }
2880
2881 #[inline(never)]
2882 fn update_window_erased(
2883 &mut self,
2884 window_id: WindowId,
2885 update: &mut dyn FnMut(Option<(AnyView, &mut Window, &mut App)>),
2886 ) {
2887 self.update(|cx| {
2888 let Some(mut window) = cx.windows.get_mut(window_id).and_then(Option::take) else {
2889 update(None);
2890 return;
2891 };
2892
2893 let root_view = window.root.clone().unwrap();
2894
2895 cx.window_update_stack.push(window.handle.id);
2896 update(Some((root_view, &mut window, cx)));
2897 fn trail(window_id: WindowId, window: Box<Window>, cx: &mut App) -> Option<()> {
2898 cx.window_update_stack.pop();
2899
2900 if window.removed {
2901 cx.end_platform_drag(window_id);
2902 cx.window_handles.remove(&window_id);
2903 cx.windows.remove(window_id);
2904 if let Some(tracked) = cx.tracked_entities.remove(&window_id) {
2905 for entity_id in tracked {
2906 if let Some(windows) =
2907 cx.window_invalidators_by_entity.get_mut(&entity_id)
2908 {
2909 windows.remove(&window_id);
2910 }
2911 if cx.current_window_by_entity.get(&entity_id) == Some(&window_id) {
2912 cx.current_window_by_entity.remove(&entity_id);
2913 }
2914 }
2915 }
2916
2917 cx.window_closed_observers.clone().retain(&(), |callback| {
2918 callback(cx, window_id);
2919 true
2920 });
2921
2922 let quit_on_empty = match cx.quit_mode {
2923 QuitMode::Explicit => false,
2924 QuitMode::LastWindowClosed => true,
2925 QuitMode::Default => cfg!(not(target_os = "macos")),
2926 };
2927
2928 if quit_on_empty && cx.windows.is_empty() {
2929 cx.quit();
2930 }
2931 } else {
2932 cx.windows.get_mut(window_id)?.replace(window);
2933 }
2934 Some(())
2935 }
2936 if trail(window_id, window, cx).is_none() {
2937 update(None);
2938 }
2939 });
2940 }
2941
2942 #[inline(never)]
2943 fn update_entity_erased(
2944 &mut self,
2945 handle: &AnyEntity,
2946 entity_type: &str,
2947 update: &mut dyn FnMut(&mut dyn Any, &mut App),
2948 ) {
2949 self.update(|cx| {
2950 let mut lease = cx.entities.lease_erased(handle, entity_type);
2951 update(lease.entity.as_deref_mut().unwrap(), cx);
2952 cx.entities.end_lease_erased(handle.entity_id, lease);
2953 });
2954 }
2955
2956 #[inline(never)]
2957 #[track_caller]
2958 fn prepare_spawn(&self) -> AsyncApp {
2959 if self.quitting {
2960 debug_panic!("Can't spawn on main thread after on_app_quit")
2961 };
2962 self.to_async()
2963 }
2964}
2965
2966impl AppContext for App {
2967 fn new<T: 'static>(&mut self, build_entity: impl FnOnce(&mut Context<T>) -> T) -> Entity<T> {
2972 self.update(|cx| {
2973 let slot = cx.entities.reserve();
2974 let handle = slot.clone();
2975 let entity = build_entity(&mut Context::new_context(cx, slot.downgrade()));
2976
2977 cx.push_effect(Effect::EntityCreated {
2978 entity: handle.into_any(),
2979 tid: TypeId::of::<T>(),
2980 window: cx.window_update_stack.last().cloned(),
2981 });
2982
2983 cx.entities.insert(slot, entity)
2984 })
2985 }
2986
2987 fn reserve_entity<T: 'static>(&mut self) -> Reservation<T> {
2988 Reservation(self.entities.reserve())
2989 }
2990
2991 fn insert_entity<T: 'static>(
2992 &mut self,
2993 reservation: Reservation<T>,
2994 build_entity: impl FnOnce(&mut Context<T>) -> T,
2995 ) -> Entity<T> {
2996 self.update(|cx| {
2997 let slot = reservation.0;
2998 let entity = build_entity(&mut Context::new_context(cx, slot.downgrade()));
2999 cx.entities.insert(slot, entity)
3000 })
3001 }
3002
3003 #[inline(always)]
3006 fn update_entity<T: 'static, R>(
3007 &mut self,
3008 handle: &Entity<T>,
3009 update: impl FnOnce(&mut T, &mut Context<T>) -> R,
3010 ) -> R {
3011 let mut update = Some(update);
3012 let mut result = None;
3013 self.update_entity_erased(handle, type_name::<T>(), &mut |entity, cx| {
3014 let value = update.take().unwrap()(
3015 entity.downcast_mut::<T>().unwrap(),
3016 &mut Context::new_context(cx, handle.downgrade()),
3017 );
3018 result = Some(value);
3019 });
3020 result.unwrap()
3021 }
3022
3023 fn as_mut<'a, T>(&'a mut self, handle: &Entity<T>) -> GpuiBorrow<'a, T>
3024 where
3025 T: 'static,
3026 {
3027 GpuiBorrow::new(handle.clone(), self)
3028 }
3029
3030 #[inline(always)]
3031 fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
3032 where
3033 T: 'static,
3034 {
3035 let entity = self.entities.read(handle);
3036 read(entity, self)
3037 }
3038
3039 fn update_window<T, F>(&mut self, handle: AnyWindowHandle, update: F) -> Result<T>
3040 where
3041 F: FnOnce(AnyView, &mut Window, &mut App) -> T,
3042 {
3043 self.update_window_id(handle.id, update)
3044 }
3045
3046 fn with_window<R>(
3047 &mut self,
3048 entity_id: EntityId,
3049 f: impl FnOnce(&mut Window, &mut App) -> R,
3050 ) -> Option<R> {
3051 App::with_window(self, entity_id, f)
3052 }
3053
3054 fn read_window<T, R>(
3055 &self,
3056 window: &WindowHandle<T>,
3057 read: impl FnOnce(Entity<T>, &App) -> R,
3058 ) -> Result<R>
3059 where
3060 T: 'static,
3061 {
3062 let window = self
3063 .windows
3064 .get(window.id)
3065 .context("window not found")?
3066 .as_deref()
3067 .expect("attempted to read a window that is already on the stack");
3068
3069 let root_view = window.root.clone().unwrap();
3070 let view = root_view
3071 .downcast::<T>()
3072 .map_err(|_| anyhow!("root view's type has changed"))?;
3073
3074 Ok(read(view, self))
3075 }
3076
3077 fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
3078 where
3079 R: Send + 'static,
3080 {
3081 self.background_executor.spawn(future)
3082 }
3083
3084 fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
3085 where
3086 G: Global,
3087 {
3088 let mut g = self.global::<G>();
3089 callback(g, self)
3090 }
3091}
3092
3093pub(crate) enum Effect {
3095 Notify {
3096 emitter: EntityId,
3097 },
3098 Emit {
3099 emitter: EntityId,
3100 event_type: TypeId,
3101 event: ArenaBox<dyn Any>,
3102 },
3103 RefreshWindows,
3104 NotifyGlobalObservers {
3105 global_type: TypeId,
3106 },
3107 Defer {
3108 callback: Box<dyn FnOnce(&mut App) + 'static>,
3109 },
3110 EntityCreated {
3111 entity: AnyEntity,
3112 tid: TypeId,
3113 window: Option<WindowId>,
3114 },
3115}
3116
3117impl std::fmt::Debug for Effect {
3118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3119 match self {
3120 Effect::Notify { emitter } => write!(f, "Notify({})", emitter),
3121 Effect::Emit { emitter, .. } => write!(f, "Emit({:?})", emitter),
3122 Effect::RefreshWindows => write!(f, "RefreshWindows"),
3123 Effect::NotifyGlobalObservers { global_type } => {
3124 write!(f, "NotifyGlobalObservers({:?})", global_type)
3125 }
3126 Effect::Defer { .. } => write!(f, "Defer(..)"),
3127 Effect::EntityCreated { entity, .. } => write!(f, "EntityCreated({:?})", entity),
3128 }
3129 }
3130}
3131
3132pub(crate) struct GlobalLease<G: Global> {
3134 global: Box<dyn Any>,
3135 global_type: PhantomData<G>,
3136}
3137
3138impl<G: Global> GlobalLease<G> {
3139 fn new(global: Box<dyn Any>) -> Self {
3140 GlobalLease {
3141 global,
3142 global_type: PhantomData,
3143 }
3144 }
3145}
3146
3147impl<G: Global> Deref for GlobalLease<G> {
3148 type Target = G;
3149
3150 fn deref(&self) -> &Self::Target {
3151 self.global.downcast_ref().unwrap()
3152 }
3153}
3154
3155impl<G: Global> DerefMut for GlobalLease<G> {
3156 fn deref_mut(&mut self) -> &mut Self::Target {
3157 self.global.downcast_mut().unwrap()
3158 }
3159}
3160
3161pub struct AnyDrag {
3164 pub view: AnyView,
3166
3167 pub value: Arc<dyn Any>,
3169
3170 pub cursor_offset: Point<Pixels>,
3173
3174 pub cursor_style: Option<CursorStyle>,
3176
3177 pub external_payload_source: Option<ExternalDragPayloadSource>,
3180}
3181
3182pub type ExternalDragPayloadSource =
3185 Box<dyn FnOnce(&mut Window, &mut App) -> Option<ExternalDragPayload> + 'static>;
3186
3187#[derive(Clone)]
3190pub struct AnyTooltip {
3191 pub view: AnyView,
3193
3194 pub mouse_position: Point<Pixels>,
3196
3197 pub check_visible_and_update: Rc<dyn Fn(Bounds<Pixels>, &mut Window, &mut App) -> bool>,
3201}
3202
3203#[derive(Debug)]
3205pub struct KeystrokeEvent {
3206 pub keystroke: Keystroke,
3208
3209 pub action: Option<Box<dyn Action>>,
3211
3212 pub context_stack: Vec<KeyContext>,
3214}
3215
3216struct NullHttpClient;
3217
3218impl HttpClient for NullHttpClient {
3219 fn send(
3220 &self,
3221 _req: http_client::Request<http_client::AsyncBody>,
3222 ) -> futures::future::BoxFuture<
3223 'static,
3224 anyhow::Result<http_client::Response<http_client::AsyncBody>>,
3225 > {
3226 async move {
3227 anyhow::bail!("No HttpClient available");
3228 }
3229 .boxed()
3230 }
3231
3232 fn user_agent(&self) -> Option<&http_client::http::HeaderValue> {
3233 None
3234 }
3235
3236 fn proxy(&self) -> Option<&Url> {
3237 None
3238 }
3239}
3240
3241pub struct GpuiBorrow<'a, T> {
3243 inner: Option<Lease<T>>,
3244 app: &'a mut App,
3245}
3246
3247impl<'a, T: 'static> GpuiBorrow<'a, T> {
3248 fn new(inner: Entity<T>, app: &'a mut App) -> Self {
3249 app.start_update();
3250 let lease = app.entities.lease(&inner);
3251 Self {
3252 inner: Some(lease),
3253 app,
3254 }
3255 }
3256}
3257
3258impl<'a, T: 'static> std::borrow::Borrow<T> for GpuiBorrow<'a, T> {
3259 fn borrow(&self) -> &T {
3260 self.inner.as_ref().unwrap().borrow()
3261 }
3262}
3263
3264impl<'a, T: 'static> std::borrow::BorrowMut<T> for GpuiBorrow<'a, T> {
3265 fn borrow_mut(&mut self) -> &mut T {
3266 self.inner.as_mut().unwrap().borrow_mut()
3267 }
3268}
3269
3270impl<'a, T: 'static> std::ops::Deref for GpuiBorrow<'a, T> {
3271 type Target = T;
3272
3273 fn deref(&self) -> &Self::Target {
3274 self.inner.as_ref().unwrap()
3275 }
3276}
3277
3278impl<'a, T: 'static> std::ops::DerefMut for GpuiBorrow<'a, T> {
3279 fn deref_mut(&mut self) -> &mut T {
3280 self.inner.as_mut().unwrap()
3281 }
3282}
3283
3284impl<'a, T> Drop for GpuiBorrow<'a, T> {
3285 fn drop(&mut self) {
3286 let lease = self.inner.take().unwrap();
3287 self.app.notify(lease.id);
3288 self.app.entities.end_lease(lease);
3289 self.app.finish_update();
3290 }
3291}
3292
3293#[cfg(test)]
3294mod test {
3295 use std::{
3296 cell::{Cell, RefCell},
3297 ffi::OsString,
3298 path::PathBuf,
3299 rc::Rc,
3300 };
3301
3302 #[cfg(unix)]
3303 use std::os::unix::ffi::OsStringExt;
3304
3305 use crate::{
3306 AppContext, Context, Empty, FallbackFontClass, IntoElement, MissingGlyph, Render,
3307 TestAppContext, Window,
3308 };
3309
3310 struct RenderCounter(Rc<Cell<usize>>);
3311
3312 impl Render for RenderCounter {
3313 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
3314 self.0.set(self.0.get() + 1);
3315 Empty
3316 }
3317 }
3318
3319 #[gpui::test]
3320 fn async_app_refresh_flushes_refresh_effect(cx: &mut TestAppContext) {
3321 let render_count = Rc::new(Cell::new(0));
3322
3323 let _window = cx.add_window({
3324 let render_count = render_count.clone();
3325 move |_, _| RenderCounter(render_count)
3326 });
3327
3328 cx.run_until_parked();
3329 let render_count_before_refresh = render_count.get();
3330
3331 cx.to_async().refresh();
3332
3333 assert_eq!(render_count.get(), render_count_before_refresh + 1);
3334 }
3335
3336 #[gpui::test]
3337 fn missing_glyph_callbacks_follow_subscription_lifetime(cx: &mut TestAppContext) {
3338 let observed = Rc::new(RefCell::new(Vec::new()));
3339 let subscription = cx.update(|cx| {
3340 let observed = observed.clone();
3341 cx.on_missing_glyphs(move |missing_glyphs, _| {
3342 observed.borrow_mut().extend_from_slice(missing_glyphs);
3343 })
3344 });
3345 cx.update(|cx| {
3346 cx.text_system()
3347 .report_missing_glyphs_in_test(vec![missing_glyph("active")]);
3348 });
3349 cx.run_until_parked();
3350 assert_eq!(observed.borrow().as_slice(), &[missing_glyph("active")]);
3351
3352 let second_observed = Rc::new(RefCell::new(Vec::new()));
3353 let second_subscription = cx.update(|cx| {
3354 let second_observed = second_observed.clone();
3355 cx.on_missing_glyphs(move |missing_glyphs, _| {
3356 second_observed
3357 .borrow_mut()
3358 .extend_from_slice(missing_glyphs);
3359 })
3360 });
3361 cx.update(|cx| {
3362 cx.text_system()
3363 .report_missing_glyphs_in_test(vec![missing_glyph("replacement")]);
3364 });
3365 cx.run_until_parked();
3366 assert_eq!(observed.borrow().as_slice(), &[missing_glyph("active")]);
3367 assert_eq!(
3368 second_observed.borrow().as_slice(),
3369 &[missing_glyph("replacement")]
3370 );
3371
3372 drop(subscription);
3373 cx.update(|cx| {
3374 cx.text_system()
3375 .report_missing_glyphs_in_test(vec![missing_glyph("after old drop")]);
3376 });
3377 cx.run_until_parked();
3378 assert_eq!(
3379 second_observed.borrow().as_slice(),
3380 &[
3381 missing_glyph("replacement"),
3382 missing_glyph("after old drop")
3383 ]
3384 );
3385
3386 drop(second_subscription);
3387 cx.update(|cx| {
3388 cx.text_system()
3389 .report_missing_glyphs_in_test(vec![missing_glyph("inactive")]);
3390 });
3391 cx.run_until_parked();
3392 assert_eq!(second_observed.borrow().len(), 2);
3393 }
3394
3395 #[test]
3396 fn test_gpui_borrow() {
3397 let cx = TestAppContext::single();
3398 let observation_count = Rc::new(RefCell::new(0));
3399
3400 let state = cx.update(|cx| {
3401 let state = cx.new(|_| false);
3402 cx.observe(&state, {
3403 let observation_count = observation_count.clone();
3404 move |_, _| {
3405 let mut count = observation_count.borrow_mut();
3406 *count += 1;
3407 }
3408 })
3409 .detach();
3410
3411 state
3412 });
3413
3414 cx.update(|cx| {
3415 *std::borrow::BorrowMut::borrow_mut(&mut state.as_mut(cx)) = true;
3417 });
3418
3419 cx.update(|cx| {
3420 state.write(cx, false);
3421 });
3422
3423 assert_eq!(*observation_count.borrow(), 2);
3424 }
3425
3426 #[gpui::test]
3427 async fn test_restart_preserves_path_and_arguments(cx: &mut TestAppContext) {
3428 #[cfg(unix)]
3429 let user_data_dir = OsString::from_vec(b"/tmp/zed data/\xff".to_vec());
3430 #[cfg(not(unix))]
3431 let user_data_dir = OsString::from("C:\\zed data");
3432 let arguments = vec![OsString::from("--user-data-dir"), user_data_dir];
3433 let restart_path = PathBuf::from("updated-zed");
3434 let _application =
3435 super::Application(cx.app.clone()).with_restart_arguments(arguments.clone());
3436 let restart = cx.expect_restart();
3437
3438 cx.update(|cx| {
3439 cx.set_restart_path(restart_path.clone());
3440 cx.restart();
3441 });
3442
3443 let (path, restart_arguments) = restart.await.expect("restart was not requested");
3444 assert_eq!(path, Some(restart_path));
3445 assert_eq!(restart_arguments, arguments);
3446 }
3447
3448 fn missing_glyph(grapheme: &'static str) -> MissingGlyph {
3449 MissingGlyph::new(grapheme.into(), FallbackFontClass::Proportional)
3450 }
3451}