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