1use std::{
2 any::{TypeId, type_name},
3 cell::{BorrowMutError, Ref, RefCell, RefMut},
4 ffi::OsString,
5 marker::PhantomData,
6 mem,
7 ops::{Deref, DerefMut},
8 path::{Path, PathBuf},
9 rc::{Rc, Weak},
10 sync::{Arc, atomic::Ordering::SeqCst},
11 time::{Duration, Instant},
12};
13
14use anyhow::{Context as _, Result, anyhow};
15use derive_more::{Deref, DerefMut};
16use futures::{Future, FutureExt, channel::oneshot, future::LocalBoxFuture};
17use itertools::Itertools;
18use parking_lot::RwLock;
19use slotmap::SlotMap;
20
21pub use async_context::*;
22use collections::{FxHashMap, FxHashSet, HashMap, VecDeque};
23pub use context::*;
24pub use entity_map::*;
25use http_client::{HttpClient, Url};
26use smallvec::SmallVec;
27#[cfg(any(test, feature = "test-support"))]
28pub use test_context::*;
29use util::{ResultExt, debug_panic};
30
31#[cfg(any(feature = "inspector", debug_assertions))]
32use crate::InspectorElementRegistry;
33use crate::asset_cache::CachedLoad;
34use crate::{
35 Action, ActionBuildError, ActionRegistry, Any, AnyView, AnyWindowHandle, AppContext,
36 AppResourceProfile, Asset, AssetSource, AttentionType, BackgroundExecutor, BiometricStatus,
37 Bounds, ClipboardItem, CrashReport, CursorStyle, DialogOptions, DispatchPhase, DisplayId,
38 EventEmitter, FocusHandle, FocusMap, FocusedWindowInfo, ForegroundExecutor, Global, KeyBinding,
39 KeyContext, Keymap, Keystroke, LayoutId, MediaKeyEvent, Menu, MenuItem, MissingGlyph,
40 NetworkStatus, OsInfo, OwnedMenu, PathPromptOptions, PermissionRequestStatus, PermissionStatus,
41 Pixels, Platform, PlatformDisplay, PlatformKeyboardLayout, PlatformKeyboardMapper, Point,
42 PowerSaveBlockerKind, PromptBuilder, PromptButton, PromptHandle, PromptLevel, Render,
43 RenderImage, RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString, Size,
44 SubscriberSet, Subscription, SvgRenderer, SystemPowerEvent, Task, TextSystem, ThermalState,
45 TrayAnchor, TrayIconClickEvent, TrayIconEvent, TrayIconRenderingMode, TrayMenuItem, Window,
46 WindowAppearance, WindowHandle, WindowId, WindowInvalidator, WindowPosition,
47 colors::{Colors, GlobalColors},
48 hash, init_app_menus, point, px, size,
49};
50
51mod async_context;
52mod context;
53mod entity_map;
54#[cfg(any(test, feature = "test-support"))]
55mod test_context;
56
57pub const SHUTDOWN_TIMEOUT: Duration = Duration::from_millis(100);
59
60#[doc(hidden)]
63pub struct AppCell {
64 app: RefCell<App>,
65}
66
67impl AppCell {
68 #[doc(hidden)]
69 #[track_caller]
70 pub fn borrow(&self) -> AppRef<'_> {
71 if option_env!("TRACK_THREAD_BORROWS").is_some() {
72 let thread_id = std::thread::current().id();
73 eprintln!("borrowed {thread_id:?}");
74 }
75 AppRef(self.app.borrow())
76 }
77
78 #[doc(hidden)]
79 #[track_caller]
80 pub fn borrow_mut(&self) -> AppRefMut<'_> {
81 if option_env!("TRACK_THREAD_BORROWS").is_some() {
82 let thread_id = std::thread::current().id();
83 eprintln!("borrowed {thread_id:?}");
84 }
85 AppRefMut(self.app.borrow_mut())
86 }
87
88 #[doc(hidden)]
89 #[track_caller]
90 pub fn try_borrow_mut(&self) -> Result<AppRefMut<'_>, BorrowMutError> {
91 if option_env!("TRACK_THREAD_BORROWS").is_some() {
92 let thread_id = std::thread::current().id();
93 eprintln!("borrowed {thread_id:?}");
94 }
95 Ok(AppRefMut(self.app.try_borrow_mut()?))
96 }
97}
98
99#[doc(hidden)]
100#[derive(Deref, DerefMut)]
101pub struct AppRef<'a>(Ref<'a, App>);
102
103impl Drop for AppRef<'_> {
104 fn drop(&mut self) {
105 if option_env!("TRACK_THREAD_BORROWS").is_some() {
106 let thread_id = std::thread::current().id();
107 eprintln!("dropped borrow from {thread_id:?}");
108 }
109 }
110}
111
112#[doc(hidden)]
113#[derive(Deref, DerefMut)]
114pub struct AppRefMut<'a>(RefMut<'a, App>);
115
116impl Drop for AppRefMut<'_> {
117 fn drop(&mut self) {
118 if option_env!("TRACK_THREAD_BORROWS").is_some() {
119 let thread_id = std::thread::current().id();
120 eprintln!("dropped {thread_id:?}");
121 }
122 }
123}
124
125pub struct Application(Rc<AppCell>);
128
129pub struct ApplicationHandle {
135 app: Rc<AppCell>,
136}
137
138impl ApplicationHandle {
139 pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> R {
143 let cx = &mut *self.app.borrow_mut();
144 f(cx)
145 }
146
147 pub fn to_async(&self) -> AsyncApp {
150 self.update(|cx| cx.to_async())
151 }
152}
153
154impl Application {
157 pub fn with_platform(platform: Rc<dyn Platform>) -> Self {
161 Self(App::new_app(
162 platform,
163 Arc::new(()),
164 Arc::new(NullHttpClient),
165 AppResourceProfile::default(),
166 ))
167 }
168
169 pub fn inaccessible(self) -> Self {
175 self.0.borrow_mut().accessibility_force_disabled = true;
176 self
177 }
178
179 pub fn with_assets(self, asset_source: impl AssetSource) -> Self {
181 let mut context_lock = self.0.borrow_mut();
182 let asset_source = Arc::new(asset_source);
183 context_lock.asset_source = asset_source.clone();
184 context_lock.svg_renderer = SvgRenderer::new(asset_source);
185 drop(context_lock);
186 self
187 }
188
189 pub fn with_restart_arguments(self, arguments: Vec<OsString>) -> Self {
191 self.0.borrow_mut().restart_arguments = arguments;
192 self
193 }
194
195 pub fn with_http_client(self, http_client: Arc<dyn HttpClient>) -> Self {
197 let mut context_lock = self.0.borrow_mut();
198 context_lock.http_client = http_client;
199 drop(context_lock);
200 self
201 }
202
203 pub fn with_quit_mode(self, mode: QuitMode) -> Self {
206 self.0.borrow_mut().set_quit_mode(mode);
207 self
208 }
209
210 pub fn with_resource_profile(self, profile: impl Into<AppResourceProfile>) -> Self {
229 let profile = profile.into();
230 crate::window::ELEMENT_ARENA_SIZE.store(
236 profile.element_arena_size.max(1),
237 std::sync::atomic::Ordering::Relaxed,
238 );
239 let mut context_lock = self.0.borrow_mut();
240 context_lock.platform.configure_gpu_resources(&profile.gpu);
241 context_lock.text_system = Arc::new(TextSystem::new(
242 context_lock.platform.text_system(),
243 &profile.text,
244 ));
245 context_lock.resource_profile = profile;
246 drop(context_lock);
247 self
248 }
249
250 pub fn run<F>(self, on_finish_launching: F)
253 where
254 F: 'static + FnOnce(&mut App),
255 {
256 let this = self.0.clone();
257 let platform = self.0.borrow().platform.clone();
258 platform.run(Box::new(move || {
259 let cx = &mut *this.borrow_mut();
260 on_finish_launching(cx);
261 }));
262 }
263
264 pub fn run_embedded<F>(self, on_finish_launching: F) -> ApplicationHandle
274 where
275 F: 'static + FnOnce(&mut App),
276 {
277 let this = self.0.clone();
278 let platform = self.0.borrow().platform.clone();
279 platform.run(Box::new(move || {
280 let cx = &mut *this.borrow_mut();
281 on_finish_launching(cx);
282 }));
283 ApplicationHandle { app: self.0 }
284 }
285
286 pub fn on_open_urls<F>(&self, mut callback: F) -> &Self
289 where
290 F: 'static + FnMut(Vec<String>),
291 {
292 self.0.borrow().platform.on_open_urls(Box::new(callback));
293 self
294 }
295
296 pub fn on_reopen<F>(&self, mut callback: F) -> &Self
299 where
300 F: 'static + FnMut(&mut App),
301 {
302 let this = Rc::downgrade(&self.0);
303 self.0.borrow_mut().platform.on_reopen(Box::new(move || {
304 if let Some(app) = this.upgrade() {
305 callback(&mut app.borrow_mut());
306 }
307 }));
308 self
309 }
310
311 pub fn background_executor(&self) -> BackgroundExecutor {
313 self.0.borrow().background_executor.clone()
314 }
315
316 pub fn foreground_executor(&self) -> ForegroundExecutor {
318 self.0.borrow().foreground_executor.clone()
319 }
320
321 pub fn text_system(&self) -> Arc<TextSystem> {
323 self.0.borrow().text_system.clone()
324 }
325
326 pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
328 self.0.borrow().path_for_auxiliary_executable(name)
329 }
330}
331
332type Handler = Box<dyn FnMut(&mut App) -> bool + 'static>;
333type Listener = Box<dyn FnMut(&dyn Any, &mut App) -> bool + 'static>;
334pub(crate) type KeystrokeObserver =
335 Box<dyn FnMut(&KeystrokeEvent, &mut Window, &mut App) -> bool + 'static>;
336type QuitHandler = Box<dyn FnOnce(&mut App) -> LocalBoxFuture<'static, ()> + 'static>;
337type WindowClosedHandler = Box<dyn FnMut(&mut App)>;
338type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut App) + 'static>;
339type NewEntityListener = Box<dyn FnMut(AnyEntity, &mut Option<&mut Window>, &mut App) + 'static>;
340
341#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
343pub enum QuitMode {
344 #[default]
346 Default,
347 LastWindowClosed,
349 Explicit,
351}
352
353#[doc(hidden)]
354#[derive(Clone, PartialEq, Eq)]
355pub struct SystemWindowTab {
356 pub id: WindowId,
357 pub title: SharedString,
358 pub handle: AnyWindowHandle,
359 pub last_active_at: Instant,
360}
361
362impl SystemWindowTab {
363 pub fn new(title: SharedString, handle: AnyWindowHandle) -> Self {
365 Self {
366 id: handle.id,
367 title,
368 handle,
369 last_active_at: Instant::now(),
370 }
371 }
372}
373
374#[derive(Default)]
376pub struct SystemWindowTabController {
377 visible: Option<bool>,
378 tab_groups: FxHashMap<usize, Vec<SystemWindowTab>>,
379}
380
381impl Global for SystemWindowTabController {}
382
383impl SystemWindowTabController {
384 pub fn new() -> Self {
386 Self {
387 visible: None,
388 tab_groups: FxHashMap::default(),
389 }
390 }
391
392 pub fn init(cx: &mut App) {
394 cx.set_global(SystemWindowTabController::new());
395 }
396
397 pub fn tab_groups(&self) -> &FxHashMap<usize, Vec<SystemWindowTab>> {
399 &self.tab_groups
400 }
401
402 pub fn get_next_tab_group_window(cx: &mut App, id: WindowId) -> Option<&AnyWindowHandle> {
404 let controller = cx.global::<SystemWindowTabController>();
405 let current_group = controller
406 .tab_groups
407 .iter()
408 .find_map(|(group, tabs)| tabs.iter().find(|tab| tab.id == id).map(|_| group));
409
410 let current_group = current_group?;
411 let mut group_ids: Vec<_> = controller.tab_groups.keys().collect();
412 let idx = group_ids.iter().position(|g| *g == current_group)?;
413 let next_idx = (idx + 1) % group_ids.len();
414
415 controller
416 .tab_groups
417 .get(group_ids[next_idx])
418 .and_then(|tabs| {
419 tabs.iter()
420 .max_by_key(|tab| tab.last_active_at)
421 .or_else(|| tabs.first())
422 .map(|tab| &tab.handle)
423 })
424 }
425
426 pub fn get_prev_tab_group_window(cx: &mut App, id: WindowId) -> Option<&AnyWindowHandle> {
428 let controller = cx.global::<SystemWindowTabController>();
429 let current_group = controller
430 .tab_groups
431 .iter()
432 .find_map(|(group, tabs)| tabs.iter().find(|tab| tab.id == id).map(|_| group));
433
434 let current_group = current_group?;
435 let mut group_ids: Vec<_> = controller.tab_groups.keys().collect();
436 let idx = group_ids.iter().position(|g| *g == current_group)?;
437 let prev_idx = if idx == 0 {
438 group_ids.len() - 1
439 } else {
440 idx - 1
441 };
442
443 controller
444 .tab_groups
445 .get(group_ids[prev_idx])
446 .and_then(|tabs| {
447 tabs.iter()
448 .max_by_key(|tab| tab.last_active_at)
449 .or_else(|| tabs.first())
450 .map(|tab| &tab.handle)
451 })
452 }
453
454 pub fn tabs(&self, id: WindowId) -> Option<&Vec<SystemWindowTab>> {
456 let tab_group = self
457 .tab_groups
458 .iter()
459 .find_map(|(group, tabs)| tabs.iter().find(|tab| tab.id == id).map(|_| *group));
460
461 if let Some(tab_group) = tab_group {
462 self.tab_groups.get(&tab_group)
463 } else {
464 None
465 }
466 }
467
468 pub fn init_visible(cx: &mut App, visible: bool) {
470 let mut controller = cx.global_mut::<SystemWindowTabController>();
471 if controller.visible.is_none() {
472 controller.visible = Some(visible);
473 }
474 }
475
476 pub fn is_visible(&self) -> bool {
478 self.visible.unwrap_or(false)
479 }
480
481 pub fn set_visible(cx: &mut App, visible: bool) {
483 let mut controller = cx.global_mut::<SystemWindowTabController>();
484 controller.visible = Some(visible);
485 }
486
487 pub fn update_last_active(cx: &mut App, id: WindowId) {
489 let mut controller = cx.global_mut::<SystemWindowTabController>();
490 for windows in controller.tab_groups.values_mut() {
491 for tab in windows.iter_mut() {
492 if tab.id == id {
493 tab.last_active_at = Instant::now();
494 }
495 }
496 }
497 }
498
499 pub fn update_tab_position(cx: &mut App, id: WindowId, ix: usize) {
501 let mut controller = cx.global_mut::<SystemWindowTabController>();
502 for (_, windows) in controller.tab_groups.iter_mut() {
503 if let Some(current_pos) = windows.iter().position(|tab| tab.id == id) {
504 if ix < windows.len() && current_pos != ix {
505 let window_tab = windows.remove(current_pos);
506 windows.insert(ix, window_tab);
507 }
508 break;
509 }
510 }
511 }
512
513 pub fn update_tab_title(cx: &mut App, id: WindowId, title: SharedString) {
515 let controller = cx.global::<SystemWindowTabController>();
516 let tab = controller
517 .tab_groups
518 .values()
519 .flat_map(|windows| windows.iter())
520 .find(|tab| tab.id == id);
521
522 if tab.map_or(true, |t| t.title == title) {
523 return;
524 }
525
526 let mut controller = cx.global_mut::<SystemWindowTabController>();
527 for windows in controller.tab_groups.values_mut() {
528 for tab in windows.iter_mut() {
529 if tab.id == id {
530 tab.title = title.clone();
531 }
532 }
533 }
534 }
535
536 pub fn add_tab(cx: &mut App, id: WindowId, tabs: Vec<SystemWindowTab>) {
538 let mut controller = cx.global_mut::<SystemWindowTabController>();
539 let Some(tab) = tabs.clone().into_iter().find(|tab| tab.id == id) else {
540 return;
541 };
542
543 let mut expected_tab_ids: Vec<_> = tabs
544 .iter()
545 .filter(|tab| tab.id != id)
546 .map(|tab| tab.id)
547 .sorted()
548 .collect();
549
550 let mut tab_group_id = None;
551 for (group_id, group_tabs) in &controller.tab_groups {
552 let tab_ids: Vec<_> = group_tabs.iter().map(|tab| tab.id).sorted().collect();
553 if tab_ids == expected_tab_ids {
554 tab_group_id = Some(*group_id);
555 break;
556 }
557 }
558
559 if let Some(tab_group_id) = tab_group_id {
560 if let Some(tabs) = controller.tab_groups.get_mut(&tab_group_id) {
561 tabs.push(tab);
562 }
563 } else {
564 let new_group_id = controller.tab_groups.len();
565 controller.tab_groups.insert(new_group_id, tabs);
566 }
567 }
568
569 pub fn remove_tab(cx: &mut App, id: WindowId) -> Option<SystemWindowTab> {
571 let mut controller = cx.global_mut::<SystemWindowTabController>();
572 let mut removed_tab = None;
573
574 controller.tab_groups.retain(|_, tabs| {
575 if let Some(pos) = tabs.iter().position(|tab| tab.id == id) {
576 removed_tab = Some(tabs.remove(pos));
577 }
578 !tabs.is_empty()
579 });
580
581 removed_tab
582 }
583
584 pub fn move_tab_to_new_window(cx: &mut App, id: WindowId) {
586 let mut removed_tab = Self::remove_tab(cx, id);
587 let mut controller = cx.global_mut::<SystemWindowTabController>();
588
589 if let Some(tab) = removed_tab {
590 let new_group_id = controller.tab_groups.keys().max().map_or(0, |k| k + 1);
591 controller.tab_groups.insert(new_group_id, vec![tab]);
592 }
593 }
594
595 pub fn merge_all_windows(cx: &mut App, id: WindowId) {
597 let mut controller = cx.global_mut::<SystemWindowTabController>();
598 let Some(initial_tabs) = controller.tabs(id) else {
599 return;
600 };
601
602 let mut all_tabs = initial_tabs.clone();
603 for tabs in controller.tab_groups.values() {
604 all_tabs.extend(
605 tabs.iter()
606 .filter(|tab| !initial_tabs.contains(tab))
607 .cloned(),
608 );
609 }
610
611 controller.tab_groups.clear();
612 controller.tab_groups.insert(0, all_tabs);
613 }
614
615 pub fn select_next_tab(cx: &mut App, id: WindowId) {
617 let mut controller = cx.global_mut::<SystemWindowTabController>();
618 let Some(tabs) = controller.tabs(id) else {
619 return;
620 };
621
622 let current_index = tabs.iter().position(|tab| tab.id == id).unwrap();
623 let next_index = (current_index + 1) % tabs.len();
624
625 let _ = &tabs[next_index].handle.update(cx, |_, window, _| {
626 window.activate_window();
627 });
628 }
629
630 pub fn select_previous_tab(cx: &mut App, id: WindowId) {
632 let mut controller = cx.global_mut::<SystemWindowTabController>();
633 let Some(tabs) = controller.tabs(id) else {
634 return;
635 };
636
637 let current_index = tabs.iter().position(|tab| tab.id == id).unwrap();
638 let previous_index = if current_index == 0 {
639 tabs.len() - 1
640 } else {
641 current_index - 1
642 };
643
644 let _ = &tabs[previous_index].handle.update(cx, |_, window, _| {
645 window.activate_window();
646 });
647 }
648}
649
650pub struct App {
654 pub(crate) this: Weak<AppCell>,
655 pub(crate) platform: Rc<dyn Platform>,
656 pub(crate) accessibility_force_disabled: bool,
657 pub(crate) resource_profile: AppResourceProfile,
658 text_system: Arc<TextSystem>,
659 flushing_effects: bool,
660 pending_updates: usize,
661 pub(crate) actions: Rc<ActionRegistry>,
662 pub(crate) active_drag: Option<AnyDrag>,
663 pub(crate) background_executor: BackgroundExecutor,
664 pub(crate) foreground_executor: ForegroundExecutor,
665 pub(crate) loading_assets: FxHashMap<(TypeId, u64), Box<dyn Any>>,
666 asset_source: Arc<dyn AssetSource>,
667 pub(crate) svg_renderer: SvgRenderer,
668 http_client: Arc<dyn HttpClient>,
669 pub(crate) globals_by_type: FxHashMap<TypeId, Box<dyn Any>>,
670 pub(crate) entities: EntityMap,
671 pub(crate) window_update_stack: Vec<WindowId>,
672 pub(crate) new_entity_observers: SubscriberSet<TypeId, NewEntityListener>,
673 pub(crate) windows: SlotMap<WindowId, Option<Window>>,
674 pub(crate) window_handles: FxHashMap<WindowId, AnyWindowHandle>,
675 pub(crate) focus_handles: Arc<FocusMap>,
676 pub(crate) keymap: Rc<RefCell<Keymap>>,
677 pub(crate) keyboard_layout: Box<dyn PlatformKeyboardLayout>,
678 pub(crate) keyboard_mapper: Rc<dyn PlatformKeyboardMapper>,
679 pub(crate) global_action_listeners:
680 FxHashMap<TypeId, Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
681 pending_effects: VecDeque<Effect>,
682 pub(crate) pending_notifications: FxHashSet<EntityId>,
683 pub(crate) pending_global_notifications: FxHashSet<TypeId>,
684 pub(crate) observers: SubscriberSet<EntityId, Handler>,
685 pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>,
687 pub(crate) keystroke_observers: SubscriberSet<(), KeystrokeObserver>,
688 pub(crate) keystroke_interceptors: SubscriberSet<(), KeystrokeObserver>,
689 pub(crate) keyboard_layout_observers: SubscriberSet<(), Handler>,
690 pub(crate) thermal_state_observers: SubscriberSet<(), Handler>,
691 pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>,
692 pub(crate) global_observers: SubscriberSet<TypeId, Handler>,
693 pub(crate) quit_observers: SubscriberSet<(), QuitHandler>,
694 pub(crate) restart_observers: SubscriberSet<(), Handler>,
695 pub(crate) restart_path: Option<PathBuf>,
696 pub(crate) restart_arguments: Vec<OsString>,
697 pub(crate) window_closed_observers: SubscriberSet<(), WindowClosedHandler>,
698 pub(crate) layout_id_buffer: Vec<LayoutId>, pub(crate) propagate_event: bool,
700 pub(crate) prompt_builder: Option<PromptBuilder>,
701 pub(crate) window_invalidators_by_entity:
702 FxHashMap<EntityId, FxHashMap<WindowId, WindowInvalidator>>,
703 pub(crate) tracked_entities: FxHashMap<WindowId, FxHashSet<EntityId>>,
704 #[cfg(any(feature = "inspector", debug_assertions))]
705 pub(crate) inspector_renderer: Option<crate::InspectorRenderer>,
706 #[cfg(any(feature = "inspector", debug_assertions))]
707 pub(crate) inspector_element_registry: InspectorElementRegistry,
708 #[cfg(any(test, feature = "test-support", debug_assertions))]
709 pub(crate) name: Option<&'static str>,
710 quitting: bool,
711 pub(crate) quit_mode: QuitMode,
712}
713
714impl App {
715 #[allow(clippy::new_ret_no_self)]
716 pub(crate) fn new_app(
717 platform: Rc<dyn Platform>,
718 asset_source: Arc<dyn AssetSource>,
719 http_client: Arc<dyn HttpClient>,
720 resource_profile: AppResourceProfile,
721 ) -> Rc<AppCell> {
722 let executor = platform.background_executor();
723 let foreground_executor = platform.foreground_executor();
724 assert!(
725 executor.is_main_thread(),
726 "must construct App on main thread"
727 );
728
729 let text_system = Arc::new(TextSystem::new(
730 platform.text_system(),
731 &resource_profile.text,
732 ));
733 platform.configure_gpu_resources(&resource_profile.gpu);
734 let entities = EntityMap::new();
735 let keyboard_layout = platform.keyboard_layout();
736 let keyboard_mapper = platform.keyboard_mapper();
737
738 let app = Rc::new_cyclic(|this| AppCell {
739 app: RefCell::new(App {
740 this: this.clone(),
741 platform: platform.clone(),
742 accessibility_force_disabled: false,
743 resource_profile,
744 text_system,
745 actions: Rc::new(ActionRegistry::default()),
746 flushing_effects: false,
747 pending_updates: 0,
748 active_drag: None,
749 background_executor: executor,
750 foreground_executor,
751 svg_renderer: SvgRenderer::new(asset_source.clone()),
752 loading_assets: Default::default(),
753 asset_source,
754 http_client,
755 globals_by_type: FxHashMap::default(),
756 entities,
757 new_entity_observers: SubscriberSet::new(),
758 windows: SlotMap::with_key(),
759 window_update_stack: Vec::new(),
760 window_handles: FxHashMap::default(),
761 focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
762 keymap: Rc::new(RefCell::new(Keymap::default())),
763 keyboard_layout,
764 keyboard_mapper,
765 global_action_listeners: FxHashMap::default(),
766 pending_effects: VecDeque::new(),
767 pending_notifications: FxHashSet::default(),
768 pending_global_notifications: FxHashSet::default(),
769 observers: SubscriberSet::new(),
770 tracked_entities: FxHashMap::default(),
771 window_invalidators_by_entity: FxHashMap::default(),
772 event_listeners: SubscriberSet::new(),
773 release_listeners: SubscriberSet::new(),
774 keystroke_observers: SubscriberSet::new(),
775 keystroke_interceptors: SubscriberSet::new(),
776 keyboard_layout_observers: SubscriberSet::new(),
777 thermal_state_observers: SubscriberSet::new(),
778 global_observers: SubscriberSet::new(),
779 quit_observers: SubscriberSet::new(),
780 restart_observers: SubscriberSet::new(),
781 restart_path: None,
782 restart_arguments: Vec::new(),
783 window_closed_observers: SubscriberSet::new(),
784 layout_id_buffer: Default::default(),
785 propagate_event: true,
786 prompt_builder: Some(PromptBuilder::Default),
787 #[cfg(any(feature = "inspector", debug_assertions))]
788 inspector_renderer: None,
789 #[cfg(any(feature = "inspector", debug_assertions))]
790 inspector_element_registry: InspectorElementRegistry::default(),
791 quitting: false,
792 quit_mode: QuitMode::default(),
793
794 #[cfg(any(test, feature = "test-support", debug_assertions))]
795 name: None,
796 }),
797 });
798
799 init_app_menus(platform.as_ref(), &app.borrow());
800 SystemWindowTabController::init(&mut app.borrow_mut());
801
802 platform.on_keyboard_layout_change(Box::new({
803 let app = Rc::downgrade(&app);
804 move || {
805 if let Some(app) = app.upgrade() {
806 let cx = &mut app.borrow_mut();
807 cx.keyboard_layout = cx.platform.keyboard_layout();
808 cx.keyboard_mapper = cx.platform.keyboard_mapper();
809 cx.keyboard_layout_observers
810 .clone()
811 .retain(&(), move |callback| (callback)(cx));
812 }
813 }
814 }));
815
816 platform.on_thermal_state_change(Box::new({
817 let app = Rc::downgrade(&app);
818 move || {
819 if let Some(app) = app.upgrade() {
820 let cx = &mut app.borrow_mut();
821 cx.thermal_state_observers
822 .clone()
823 .retain(&(), move |callback| (callback)(cx));
824 }
825 }
826 }));
827
828 platform.on_quit(Box::new({
829 let cx = app.clone();
830 move || match cx.try_borrow_mut() {
831 Ok(mut cx) => {
832 cx.shutdown();
833 true
834 }
835 Err(_) => {
836 false
840 }
841 }
842 }));
843
844 app
845 }
846
847 pub fn shutdown(&mut self) {
850 let mut futures = Vec::new();
851
852 for observer in self.quit_observers.remove(&()) {
853 futures.push(observer(self));
854 }
855
856 self.windows.clear();
857 self.window_handles.clear();
858 self.flush_effects();
859 self.quitting = true;
860
861 let futures = futures::future::join_all(futures);
862 if self
863 .background_executor
864 .block_with_timeout(SHUTDOWN_TIMEOUT, futures)
865 .is_err()
866 {
867 log::error!("timed out waiting on app_will_quit");
868 }
869
870 self.quitting = false;
871 }
872
873 pub fn keyboard_layout(&self) -> &dyn PlatformKeyboardLayout {
875 self.keyboard_layout.as_ref()
876 }
877
878 pub fn keyboard_mapper(&self) -> &Rc<dyn PlatformKeyboardMapper> {
880 &self.keyboard_mapper
881 }
882
883 pub fn on_keyboard_layout_change<F>(&self, mut callback: F) -> Subscription
885 where
886 F: 'static + FnMut(&mut App),
887 {
888 let (subscription, activate) = self.keyboard_layout_observers.insert(
889 (),
890 Box::new(move |cx| {
891 callback(cx);
892 true
893 }),
894 );
895 activate();
896 subscription
897 }
898
899 pub fn thermal_state(&self) -> ThermalState {
901 self.platform.thermal_state()
902 }
903
904 pub fn prevent_idle_sleep(&self, reason: &str) -> Task<Result<crate::ActivityGuard>> {
908 self.platform.prevent_idle_sleep(reason)
909 }
910
911 pub fn on_thermal_state_change<F>(&self, mut callback: F) -> Subscription
913 where
914 F: 'static + FnMut(&mut App),
915 {
916 let (subscription, activate) = self.thermal_state_observers.insert(
917 (),
918 Box::new(move |cx| {
919 callback(cx);
920 true
921 }),
922 );
923 activate();
924 subscription
925 }
926
927 pub fn quit(&self) {
929 self.platform.quit();
930 }
931
932 pub fn trim_gpu_caches(&self) {
945 for (_, window) in self.windows.iter() {
946 if let Some(window) = window {
947 window.platform_window.trim_renderer_caches();
948 }
949 }
950 self.platform.trim_renderer_caches();
951 }
952
953 pub fn renderer_cache_stats(&self) -> crate::RendererCacheStats {
960 self.platform.renderer_cache_stats()
961 }
962
963 pub fn refresh_windows(&mut self) {
966 self.pending_effects.push_back(Effect::RefreshWindows);
967 }
968
969 pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
970 self.start_update();
971 let result = update(self);
972 self.finish_update();
973 result
974 }
975
976 pub(crate) fn start_update(&mut self) {
977 self.pending_updates += 1;
978 }
979
980 pub(crate) fn finish_update(&mut self) {
981 if !self.flushing_effects && self.pending_updates == 1 {
982 self.flushing_effects = true;
983 self.flush_effects();
984 self.flushing_effects = false;
985 }
986 self.pending_updates -= 1;
987 }
988
989 pub fn observe<W>(
991 &mut self,
992 entity: &Entity<W>,
993 mut on_notify: impl FnMut(Entity<W>, &mut App) + 'static,
994 ) -> Subscription
995 where
996 W: 'static,
997 {
998 self.observe_internal(entity, move |e, cx| {
999 on_notify(e, cx);
1000 true
1001 })
1002 }
1003
1004 pub(crate) fn detect_accessed_entities<R>(
1005 &mut self,
1006 callback: impl FnOnce(&mut App) -> R,
1007 ) -> (R, FxHashSet<EntityId>) {
1008 let accessed_entities_start = self.entities.accessed_entities.borrow().clone();
1009 let result = callback(self);
1010 let accessed_entities_end = self.entities.accessed_entities.borrow().clone();
1011 let entities_accessed_in_callback = accessed_entities_end
1012 .difference(&accessed_entities_start)
1013 .copied()
1014 .collect::<FxHashSet<EntityId>>();
1015 (result, entities_accessed_in_callback)
1016 }
1017
1018 pub(crate) fn record_entities_accessed(
1019 &mut self,
1020 window_handle: AnyWindowHandle,
1021 invalidator: WindowInvalidator,
1022 entities: &FxHashSet<EntityId>,
1023 ) {
1024 let mut tracked_entities =
1025 std::mem::take(self.tracked_entities.entry(window_handle.id).or_default());
1026 for entity in tracked_entities.iter() {
1027 self.window_invalidators_by_entity
1028 .entry(*entity)
1029 .and_modify(|windows| {
1030 windows.remove(&window_handle.id);
1031 });
1032 }
1033 for entity in entities.iter() {
1034 self.window_invalidators_by_entity
1035 .entry(*entity)
1036 .or_default()
1037 .insert(window_handle.id, invalidator.clone());
1038 }
1039 tracked_entities.clear();
1040 tracked_entities.extend(entities.iter().copied());
1041 self.tracked_entities
1042 .insert(window_handle.id, tracked_entities);
1043 }
1044
1045 pub(crate) fn new_observer(&mut self, key: EntityId, value: Handler) -> Subscription {
1046 let (subscription, activate) = self.observers.insert(key, value);
1047 self.defer(move |_| activate());
1048 subscription
1049 }
1050
1051 pub(crate) fn observe_internal<W>(
1052 &mut self,
1053 entity: &Entity<W>,
1054 mut on_notify: impl FnMut(Entity<W>, &mut App) -> bool + 'static,
1055 ) -> Subscription
1056 where
1057 W: 'static,
1058 {
1059 let entity_id = entity.entity_id();
1060 let handle = entity.downgrade();
1061 self.new_observer(
1062 entity_id,
1063 Box::new(move |cx| {
1064 if let Some(entity) = handle.upgrade() {
1065 on_notify(entity, cx)
1066 } else {
1067 false
1068 }
1069 }),
1070 )
1071 }
1072
1073 pub fn subscribe<T, Event>(
1076 &mut self,
1077 entity: &Entity<T>,
1078 mut on_event: impl FnMut(Entity<T>, &Event, &mut App) + 'static,
1079 ) -> Subscription
1080 where
1081 T: 'static + EventEmitter<Event>,
1082 Event: 'static,
1083 {
1084 self.subscribe_internal(entity, move |entity, event, cx| {
1085 on_event(entity, event, cx);
1086 true
1087 })
1088 }
1089
1090 pub(crate) fn new_subscription(
1091 &mut self,
1092 key: EntityId,
1093 value: (TypeId, Listener),
1094 ) -> Subscription {
1095 let (subscription, activate) = self.event_listeners.insert(key, value);
1096 self.defer(move |_| activate());
1097 subscription
1098 }
1099 pub(crate) fn subscribe_internal<T, Evt>(
1100 &mut self,
1101 entity: &Entity<T>,
1102 mut on_event: impl FnMut(Entity<T>, &Evt, &mut App) -> bool + 'static,
1103 ) -> Subscription
1104 where
1105 T: 'static + EventEmitter<Evt>,
1106 Evt: 'static,
1107 {
1108 let entity_id = entity.entity_id();
1109 let handle = entity.downgrade();
1110 self.new_subscription(
1111 entity_id,
1112 (
1113 TypeId::of::<Evt>(),
1114 Box::new(move |event, cx| {
1115 let event: &Evt = event.downcast_ref().expect("invalid event type");
1116 if let Some(entity) = handle.upgrade() {
1117 on_event(entity, event, cx)
1118 } else {
1119 false
1120 }
1121 }),
1122 ),
1123 )
1124 }
1125
1126 pub fn windows(&self) -> Vec<AnyWindowHandle> {
1130 self.windows
1131 .keys()
1132 .flat_map(|window_id| self.window_handles.get(&window_id).copied())
1133 .collect()
1134 }
1135
1136 pub fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
1142 self.platform.window_stack()
1143 }
1144
1145 pub fn active_window(&self) -> Option<AnyWindowHandle> {
1147 self.platform.active_window()
1148 }
1149
1150 pub fn open_window<V: 'static + Render>(
1154 &mut self,
1155 options: crate::WindowOptions,
1156 build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity<V>,
1157 ) -> anyhow::Result<WindowHandle<V>> {
1158 self.update(|cx| {
1159 let id = cx.windows.insert(None);
1160 let handle = WindowHandle::new(id);
1161 match Window::new(handle.into(), options, cx) {
1162 Ok(mut window) => {
1163 cx.window_update_stack.push(id);
1164 let root_view = build_root_view(&mut window, cx);
1165 cx.window_update_stack.pop();
1166 window.root.replace(root_view.into());
1167 window.defer(cx, |window: &mut Window, cx| window.appearance_changed(cx));
1168
1169 let clear = window.draw(cx);
1174 clear.clear();
1175
1176 cx.window_handles.insert(id, window.handle);
1177 cx.windows.get_mut(id).unwrap().replace(window);
1178 Ok(handle)
1179 }
1180 Err(e) => {
1181 cx.windows.remove(id);
1182 Err(e)
1183 }
1184 }
1185 })
1186 }
1187
1188 pub fn activate(&self, ignoring_other_apps: bool) {
1190 self.platform.activate(ignoring_other_apps);
1191 }
1192
1193 pub fn hide(&self) {
1195 self.platform.hide();
1196 }
1197
1198 pub fn hide_other_apps(&self) {
1200 self.platform.hide_other_apps();
1201 }
1202
1203 pub fn unhide_other_apps(&self) {
1205 self.platform.unhide_other_apps();
1206 }
1207
1208 pub fn set_tray_icon(&self, icon: Option<&[u8]>) {
1210 self.platform.set_tray_icon(icon);
1211 }
1212
1213 pub fn set_tray_icon_rendering_mode(&self, rendering_mode: TrayIconRenderingMode) {
1215 self.platform.set_tray_icon_rendering_mode(rendering_mode);
1216 }
1217
1218 pub fn set_tray_menu(&self, menu: Vec<TrayMenuItem>) {
1220 self.platform.set_tray_menu(menu);
1221 }
1222
1223 pub fn set_tray_tooltip(&self, tooltip: &str) {
1225 self.platform.set_tray_tooltip(tooltip);
1226 }
1227
1228 pub fn set_tray_panel_mode(&self, enabled: bool) {
1234 self.platform.set_tray_panel_mode(enabled);
1235 }
1236
1237 #[allow(missing_docs)]
1238 pub fn tray_icon_anchor(&self) -> Option<TrayAnchor> {
1239 self.platform.get_tray_icon_anchor()
1240 }
1241
1242 pub fn tray_icon_bounds(&self) -> Option<Bounds<Pixels>> {
1244 self.platform.get_tray_icon_bounds()
1245 }
1246
1247 pub fn tray_anchor_for_position(&self, position: Point<Pixels>) -> Option<TrayAnchor> {
1255 const APPROXIMATE_TRAY_ICON_SIZE: Pixels = px(24.0);
1258
1259 let displays = self.displays();
1260 let display = displays
1261 .iter()
1262 .find(|display| display.bounds().contains(&position))
1263 .cloned()
1264 .or_else(|| self.primary_display())?;
1265 let display_bounds = display.bounds();
1266 let half_size = APPROXIMATE_TRAY_ICON_SIZE * 0.5;
1267 let local_position = position - display_bounds.origin;
1268
1269 Some(TrayAnchor {
1270 display_id: display.id(),
1271 bounds: Bounds::new(
1272 point(local_position.x - half_size, local_position.y - half_size),
1273 size(APPROXIMATE_TRAY_ICON_SIZE, APPROXIMATE_TRAY_ICON_SIZE),
1274 ),
1275 })
1276 }
1277
1278 pub fn on_tray_icon_event(&self, mut callback: impl FnMut(TrayIconEvent, &mut App) + 'static) {
1290 let this = self.this.clone();
1291 self.platform.on_tray_icon_event(Box::new(move |event| {
1292 if let Some(app) = this.upgrade() {
1293 callback(event, &mut app.borrow_mut());
1294 }
1295 }));
1296 }
1297
1298 pub fn on_tray_icon_click_event(
1306 &self,
1307 mut callback: impl FnMut(TrayIconClickEvent, &mut App) + 'static,
1308 ) {
1309 let this = self.this.clone();
1310 self.platform
1311 .on_tray_icon_click_event(Box::new(move |event| {
1312 if let Some(app) = this.upgrade() {
1313 callback(event, &mut app.borrow_mut());
1314 }
1315 }));
1316 }
1317
1318 pub fn on_tray_menu_action(&self, mut callback: impl FnMut(SharedString, &mut App) + 'static) {
1320 let this = self.this.clone();
1321 self.platform.on_tray_menu_action(Box::new(move |id| {
1322 if let Some(app) = this.upgrade() {
1323 callback(id, &mut app.borrow_mut());
1324 }
1325 }));
1326 }
1327
1328 pub fn register_global_hotkey(&self, id: u32, keystroke: &Keystroke) -> Result<()> {
1330 self.platform.register_global_hotkey(id, keystroke)
1331 }
1332
1333 pub fn unregister_global_hotkey(&self, id: u32) {
1335 self.platform.unregister_global_hotkey(id);
1336 }
1337
1338 pub fn on_global_hotkey(&self, callback: impl FnMut(u32) + 'static) {
1340 self.platform.on_global_hotkey(Box::new(callback));
1341 }
1342
1343 pub fn focused_window_info(&self) -> Option<FocusedWindowInfo> {
1345 self.platform.focused_window_info()
1346 }
1347
1348 pub fn accessibility_status(&self) -> PermissionStatus {
1350 self.platform.accessibility_status()
1351 }
1352
1353 pub fn request_accessibility_permission(&self) -> PermissionRequestStatus {
1355 self.platform.request_accessibility_permission()
1356 }
1357
1358 pub fn microphone_status(&self) -> PermissionStatus {
1360 self.platform.microphone_status()
1361 }
1362
1363 pub fn request_microphone_permission(
1365 &self,
1366 callback: impl FnOnce(bool) + 'static,
1367 ) -> PermissionRequestStatus {
1368 self.platform
1369 .request_microphone_permission(Box::new(callback))
1370 }
1371
1372 pub fn set_auto_launch(&self, app_id: &str, enabled: bool) -> Result<()> {
1374 self.platform.set_auto_launch(app_id, enabled)
1375 }
1376
1377 pub fn is_auto_launch_enabled(&self, app_id: &str) -> bool {
1379 self.platform.is_auto_launch_enabled(app_id)
1380 }
1381
1382 pub fn show_notification(&self, title: &str, body: &str) -> Result<()> {
1384 self.platform.show_notification(title, body)
1385 }
1386
1387 pub fn set_quit_mode(&mut self, mode: QuitMode) {
1390 self.quit_mode = mode;
1391 self.platform.set_quit_mode(mode);
1392 }
1393
1394 pub fn on_system_power_event(
1396 &self,
1397 mut callback: impl FnMut(SystemPowerEvent, &mut App) + 'static,
1398 ) {
1399 let this = self.this.clone();
1400 self.platform.on_system_power_event(Box::new(move |event| {
1401 if let Some(app) = this.upgrade() {
1402 callback(event, &mut app.borrow_mut());
1403 }
1404 }));
1405 }
1406
1407 pub fn start_power_save_blocker(&self, kind: PowerSaveBlockerKind) -> Option<u32> {
1409 self.platform.start_power_save_blocker(kind)
1410 }
1411
1412 pub fn stop_power_save_blocker(&self, id: u32) {
1414 self.platform.stop_power_save_blocker(id);
1415 }
1416
1417 pub fn system_idle_time(&self) -> Option<Duration> {
1419 self.platform.system_idle_time()
1420 }
1421
1422 pub fn network_status(&self) -> NetworkStatus {
1424 self.platform.network_status()
1425 }
1426
1427 pub fn on_network_status_change(
1429 &self,
1430 mut callback: impl FnMut(NetworkStatus, &mut App) + 'static,
1431 ) {
1432 let this = self.this.clone();
1433 self.platform
1434 .on_network_status_change(Box::new(move |status| {
1435 if let Some(app) = this.upgrade() {
1436 callback(status, &mut app.borrow_mut());
1437 }
1438 }));
1439 }
1440
1441 pub fn on_media_key_event(&self, mut callback: impl FnMut(MediaKeyEvent, &mut App) + 'static) {
1443 let this = self.this.clone();
1444 self.platform.on_media_key_event(Box::new(move |event| {
1445 if let Some(app) = this.upgrade() {
1446 callback(event, &mut app.borrow_mut());
1447 }
1448 }));
1449 }
1450
1451 pub fn request_user_attention(&self, attention_type: AttentionType) {
1453 self.platform.request_user_attention(attention_type);
1454 }
1455
1456 pub fn cancel_user_attention(&self) {
1458 self.platform.cancel_user_attention();
1459 }
1460
1461 pub fn set_dock_badge(&self, label: Option<&str>) {
1463 self.platform.set_dock_badge(label);
1464 }
1465
1466 pub fn show_context_menu(
1468 &self,
1469 position: Point<Pixels>,
1470 items: Vec<TrayMenuItem>,
1471 mut callback: impl FnMut(SharedString, &mut App) + 'static,
1472 ) {
1473 let this = self.this.clone();
1474 self.platform.show_context_menu(
1475 position,
1476 items,
1477 Box::new(move |id| {
1478 if let Some(app) = this.upgrade() {
1479 callback(id, &mut app.borrow_mut());
1480 }
1481 }),
1482 );
1483 }
1484
1485 pub fn show_dialog(&self, options: DialogOptions) -> oneshot::Receiver<usize> {
1487 self.platform.show_dialog(options)
1488 }
1489
1490 pub fn os_info(&self) -> OsInfo {
1492 self.platform.os_info()
1493 }
1494
1495 pub fn biometric_status(&self) -> BiometricStatus {
1497 self.platform.biometric_status()
1498 }
1499
1500 pub fn authenticate_biometric(
1502 &self,
1503 reason: &str,
1504 callback: impl FnOnce(bool) + Send + 'static,
1505 ) {
1506 self.platform
1507 .authenticate_biometric(reason, Box::new(callback));
1508 }
1509
1510 pub fn set_crash_handler(
1512 &self,
1513 app_version: Option<String>,
1514 handler: impl Fn(CrashReport) + Send + Sync + 'static,
1515 ) {
1516 let os_info = self.platform.os_info();
1517 let handler = std::sync::Arc::new(handler);
1518 std::panic::set_hook(Box::new(move |panic_info| {
1519 let message = if let Some(msg) = panic_info.payload().downcast_ref::<&str>() {
1520 msg.to_string()
1521 } else if let Some(msg) = panic_info.payload().downcast_ref::<String>() {
1522 msg.clone()
1523 } else {
1524 "Unknown panic".to_string()
1525 };
1526
1527 let location = panic_info
1528 .location()
1529 .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column()))
1530 .unwrap_or_default();
1531
1532 let full_message = if location.is_empty() {
1533 message
1534 } else {
1535 format!("{message} at {location}")
1536 };
1537
1538 let backtrace = std::backtrace::Backtrace::force_capture().to_string();
1539
1540 let report = CrashReport {
1541 message: full_message,
1542 backtrace,
1543 os_info: os_info.clone(),
1544 app_version: app_version.clone(),
1545 };
1546
1547 handler(report);
1548 }));
1549 }
1550
1551 pub fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
1553 self.platform.displays()
1554 }
1555
1556 pub fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
1558 self.platform.primary_display()
1559 }
1560
1561 pub fn compute_window_bounds(
1563 &self,
1564 size: Size<Pixels>,
1565 position: &WindowPosition,
1566 ) -> Bounds<Pixels> {
1567 let displays = self.platform.displays();
1568 let primary = self.platform.primary_display();
1569 crate::platform::window_positioner::compute_window_bounds(
1570 size,
1571 position,
1572 &displays,
1573 primary.as_ref(),
1574 )
1575 }
1576
1577 pub fn is_screen_capture_supported(&self) -> bool {
1579 self.platform.is_screen_capture_supported()
1580 }
1581
1582 pub fn screen_capture_sources(
1584 &self,
1585 ) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
1586 self.platform.screen_capture_sources()
1587 }
1588
1589 pub fn find_display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
1591 self.displays()
1592 .iter()
1593 .find(|display| display.id() == id)
1594 .cloned()
1595 }
1596
1597 pub fn window_appearance(&self) -> WindowAppearance {
1599 self.platform.window_appearance()
1600 }
1601
1602 pub fn set_window_appearance(&self, appearance: Option<WindowAppearance>) {
1614 self.platform.set_window_appearance(appearance);
1615 }
1616
1617 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1620 pub fn write_to_primary(&self, item: ClipboardItem) {
1621 self.platform.write_to_primary(item)
1622 }
1623
1624 pub fn write_to_clipboard(&self, item: ClipboardItem) {
1626 self.platform.write_to_clipboard(item)
1627 }
1628
1629 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1632 pub fn read_from_primary(&self) -> Option<ClipboardItem> {
1633 self.platform.read_from_primary()
1634 }
1635
1636 pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
1638 self.platform.read_from_clipboard()
1639 }
1640
1641 pub fn write_credentials(
1643 &self,
1644 url: &str,
1645 username: &str,
1646 password: &[u8],
1647 ) -> Task<Result<()>> {
1648 self.platform.write_credentials(url, username, password)
1649 }
1650
1651 pub fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
1653 self.platform.read_credentials(url)
1654 }
1655
1656 pub fn delete_credentials(&self, url: &str) -> Task<Result<()>> {
1658 self.platform.delete_credentials(url)
1659 }
1660
1661 pub fn open_url(&self, url: &str) {
1663 self.platform.open_url(url);
1664 }
1665
1666 pub fn register_url_scheme(&self, scheme: &str) -> Task<Result<()>> {
1673 self.platform.register_url_scheme(scheme)
1674 }
1675
1676 pub fn app_path(&self) -> Result<PathBuf> {
1680 self.platform.app_path()
1681 }
1682
1683 pub fn compositor_name(&self) -> &'static str {
1687 self.platform.compositor_name()
1688 }
1689
1690 pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
1692 self.platform.path_for_auxiliary_executable(name)
1693 }
1694
1695 pub fn prompt_for_paths(
1701 &self,
1702 options: PathPromptOptions,
1703 ) -> oneshot::Receiver<Result<Option<Vec<PathBuf>>>> {
1704 self.platform.prompt_for_paths(options)
1705 }
1706
1707 pub fn prompt_for_new_path(
1714 &self,
1715 directory: &Path,
1716 suggested_name: Option<&str>,
1717 ) -> oneshot::Receiver<Result<Option<PathBuf>>> {
1718 self.platform.prompt_for_new_path(directory, suggested_name)
1719 }
1720
1721 pub fn reveal_path(&self, path: &Path) {
1723 self.platform.reveal_path(path)
1724 }
1725
1726 pub fn open_with_system(&self, path: &Path) {
1728 self.platform.open_with_system(path)
1729 }
1730
1731 pub fn should_auto_hide_scrollbars(&self) -> bool {
1733 self.platform.should_auto_hide_scrollbars()
1734 }
1735
1736 pub fn restart(&mut self) {
1738 self.restart_observers
1739 .clone()
1740 .retain(&(), |observer| observer(self));
1741 self.platform.restart(
1742 self.restart_path.take(),
1743 mem::take(&mut self.restart_arguments),
1744 )
1745 }
1746
1747 pub fn set_restart_path(&mut self, path: PathBuf) {
1749 self.restart_path = Some(path);
1750 }
1751
1752 pub fn http_client(&self) -> Arc<dyn HttpClient> {
1754 self.http_client.clone()
1755 }
1756
1757 pub fn set_http_client(&mut self, new_client: Arc<dyn HttpClient>) {
1759 self.http_client = new_client;
1760 }
1761
1762 pub fn svg_renderer(&self) -> SvgRenderer {
1764 self.svg_renderer.clone()
1765 }
1766
1767 pub(crate) fn push_effect(&mut self, effect: Effect) {
1768 match &effect {
1769 Effect::Notify { emitter } => {
1770 if !self.pending_notifications.insert(*emitter) {
1771 return;
1772 }
1773 }
1774 Effect::NotifyGlobalObservers { global_type } => {
1775 if !self.pending_global_notifications.insert(*global_type) {
1776 return;
1777 }
1778 }
1779 _ => {}
1780 };
1781
1782 self.pending_effects.push_back(effect);
1783 }
1784
1785 fn flush_effects(&mut self) {
1789 loop {
1790 self.release_dropped_entities();
1791 self.release_dropped_focus_handles();
1792 if let Some(effect) = self.pending_effects.pop_front() {
1793 match effect {
1794 Effect::Notify { emitter } => {
1795 self.apply_notify_effect(emitter);
1796 }
1797
1798 Effect::Emit {
1799 emitter,
1800 event_type,
1801 event,
1802 } => self.apply_emit_effect(emitter, event_type, event),
1803
1804 Effect::RefreshWindows => {
1805 self.apply_refresh_effect();
1806 }
1807
1808 Effect::NotifyGlobalObservers { global_type } => {
1809 self.apply_notify_global_observers_effect(global_type);
1810 }
1811
1812 Effect::Defer { callback } => {
1813 self.apply_defer_effect(callback);
1814 }
1815 Effect::EntityCreated {
1816 entity,
1817 tid,
1818 window,
1819 } => {
1820 self.apply_entity_created_effect(entity, tid, window);
1821 }
1822 }
1823 } else {
1824 #[cfg(any(test, feature = "test-support"))]
1825 for window in self
1826 .windows
1827 .values()
1828 .filter_map(|window| {
1829 let window = window.as_ref()?;
1830 window.invalidator.is_dirty().then_some(window.handle)
1831 })
1832 .collect::<Vec<_>>()
1833 {
1834 self.update_window(window, |_, window, cx| window.draw(cx).clear())
1835 .unwrap();
1836 }
1837
1838 if self.pending_effects.is_empty() {
1839 for window in self.windows.values().filter_map(|window| window.as_ref()) {
1840 if window.invalidator.is_dirty()
1841 || window.needs_present.get()
1842 || !window.next_frame_callbacks.borrow().is_empty()
1843 {
1844 window.platform_window.schedule_frame();
1845 }
1846 }
1847
1848 break;
1849 }
1850 }
1851 }
1852 }
1853
1854 fn release_dropped_entities(&mut self) {
1858 loop {
1859 let dropped = self.entities.take_dropped();
1860 if dropped.is_empty() {
1861 break;
1862 }
1863
1864 for (entity_id, mut entity) in dropped {
1865 self.observers.remove(&entity_id);
1866 self.event_listeners.remove(&entity_id);
1867 for release_callback in self.release_listeners.remove(&entity_id) {
1868 release_callback(entity.as_mut(), self);
1869 }
1870 }
1871 }
1872 }
1873
1874 fn release_dropped_focus_handles(&mut self) {
1876 self.focus_handles
1877 .clone()
1878 .write()
1879 .retain(|handle_id, focus| {
1880 if focus.ref_count.load(SeqCst) == 0 {
1881 for window_handle in self.windows() {
1882 window_handle
1883 .update(self, |_, window, cx| {
1884 if window.focus == Some(handle_id) {
1885 window.blur(cx);
1886 }
1887 })
1888 .unwrap();
1889 }
1890 false
1891 } else {
1892 true
1893 }
1894 });
1895 }
1896
1897 fn apply_notify_effect(&mut self, emitter: EntityId) {
1898 self.pending_notifications.remove(&emitter);
1899
1900 self.observers
1901 .clone()
1902 .retain(&emitter, |handler| handler(self));
1903 }
1904
1905 fn apply_emit_effect(&mut self, emitter: EntityId, event_type: TypeId, event: Box<dyn Any>) {
1906 self.event_listeners
1907 .clone()
1908 .retain(&emitter, |(stored_type, handler)| {
1909 if *stored_type == event_type {
1910 handler(event.as_ref(), self)
1911 } else {
1912 true
1913 }
1914 });
1915 }
1916
1917 fn apply_refresh_effect(&mut self) {
1918 for window in self.windows.values_mut() {
1919 if let Some(window) = window.as_mut() {
1920 window.refreshing = true;
1921 window.invalidator.set_dirty(true);
1922 }
1923 }
1924 }
1925
1926 fn apply_notify_global_observers_effect(&mut self, type_id: TypeId) {
1927 self.pending_global_notifications.remove(&type_id);
1928 self.global_observers
1929 .clone()
1930 .retain(&type_id, |observer| observer(self));
1931 }
1932
1933 fn apply_defer_effect(&mut self, callback: Box<dyn FnOnce(&mut Self) + 'static>) {
1934 callback(self);
1935 }
1936
1937 fn apply_entity_created_effect(
1938 &mut self,
1939 entity: AnyEntity,
1940 tid: TypeId,
1941 window: Option<WindowId>,
1942 ) {
1943 self.new_entity_observers.clone().retain(&tid, |observer| {
1944 if let Some(id) = window {
1945 self.update_window_id(id, {
1946 let entity = entity.clone();
1947 |_, window, cx| (observer)(entity, &mut Some(window), cx)
1948 })
1949 .expect("All windows should be off the stack when flushing effects");
1950 } else {
1951 (observer)(entity.clone(), &mut None, self)
1952 }
1953 true
1954 });
1955 }
1956
1957 fn update_window_id<T, F>(&mut self, id: WindowId, update: F) -> Result<T>
1958 where
1959 F: FnOnce(AnyView, &mut Window, &mut App) -> T,
1960 {
1961 self.update(|cx| {
1962 let mut window = cx.windows.get_mut(id)?.take()?;
1963
1964 let root_view = window.root.clone().unwrap();
1965
1966 cx.window_update_stack.push(window.handle.id);
1967 let result = update(root_view, &mut window, cx);
1968 cx.window_update_stack.pop();
1969
1970 if window.removed {
1971 cx.window_handles.remove(&id);
1972 cx.windows.remove(id);
1973
1974 cx.window_closed_observers.clone().retain(&(), |callback| {
1975 callback(cx);
1976 true
1977 });
1978
1979 let quit_on_empty = match cx.quit_mode {
1980 QuitMode::Explicit => false,
1981 QuitMode::LastWindowClosed => true,
1982 QuitMode::Default => cfg!(not(target_os = "macos")),
1983 };
1984
1985 if quit_on_empty && cx.windows.is_empty() {
1986 cx.quit();
1987 }
1988 } else {
1989 cx.windows.get_mut(id)?.replace(window);
1990 }
1991
1992 Some(result)
1993 })
1994 .context("window not found")
1995 }
1996
1997 pub fn to_async(&self) -> AsyncApp {
2000 AsyncApp {
2001 app: self.this.clone(),
2002 background_executor: self.background_executor.clone(),
2003 foreground_executor: self.foreground_executor.clone(),
2004 }
2005 }
2006
2007 pub fn background_executor(&self) -> &BackgroundExecutor {
2009 &self.background_executor
2010 }
2011
2012 pub fn is_test(&self) -> bool {
2015 self.background_executor.is_test()
2016 }
2017
2018 pub fn foreground_executor(&self) -> &ForegroundExecutor {
2020 if self.quitting {
2021 panic!("Can't spawn on main thread after on_app_quit")
2022 };
2023 &self.foreground_executor
2024 }
2025
2026 #[track_caller]
2029 pub fn spawn<AsyncFn, R>(&self, f: AsyncFn) -> Task<R>
2030 where
2031 AsyncFn: AsyncFnOnce(&mut AsyncApp) -> R + 'static,
2032 R: 'static,
2033 {
2034 if self.quitting {
2035 debug_panic!("Can't spawn on main thread after on_app_quit")
2036 };
2037
2038 let mut cx = self.to_async();
2039
2040 self.foreground_executor
2041 .spawn(async move { f(&mut cx).await })
2042 }
2043
2044 pub fn defer(&mut self, f: impl FnOnce(&mut App) + 'static) {
2047 self.push_effect(Effect::Defer {
2048 callback: Box::new(f),
2049 });
2050 }
2051
2052 pub fn asset_source(&self) -> &Arc<dyn AssetSource> {
2054 &self.asset_source
2055 }
2056
2057 pub fn text_system(&self) -> &Arc<TextSystem> {
2059 &self.text_system
2060 }
2061
2062 pub fn has_global<G: Global>(&self) -> bool {
2064 self.globals_by_type.contains_key(&TypeId::of::<G>())
2065 }
2066
2067 #[track_caller]
2069 pub fn global<G: Global>(&self) -> &G {
2070 self.globals_by_type
2071 .get(&TypeId::of::<G>())
2072 .map(|any_state| any_state.downcast_ref::<G>().unwrap())
2073 .with_context(|| format!("no state of type {} exists", type_name::<G>()))
2074 .unwrap()
2075 }
2076
2077 pub fn try_global<G: Global>(&self) -> Option<&G> {
2079 self.globals_by_type
2080 .get(&TypeId::of::<G>())
2081 .map(|any_state| any_state.downcast_ref::<G>().unwrap())
2082 }
2083
2084 #[track_caller]
2086 pub fn global_mut<G: Global>(&mut self) -> &mut G {
2087 let global_type = TypeId::of::<G>();
2088 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2089 self.globals_by_type
2090 .get_mut(&global_type)
2091 .and_then(|any_state| any_state.downcast_mut::<G>())
2092 .with_context(|| format!("no state of type {} exists", type_name::<G>()))
2093 .unwrap()
2094 }
2095
2096 pub fn default_global<G: Global + Default>(&mut self) -> &mut G {
2099 let global_type = TypeId::of::<G>();
2100 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2101 self.globals_by_type
2102 .entry(global_type)
2103 .or_insert_with(|| Box::<G>::default())
2104 .downcast_mut::<G>()
2105 .unwrap()
2106 }
2107
2108 pub fn set_global<G: Global>(&mut self, global: G) {
2110 let global_type = TypeId::of::<G>();
2111 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2112 self.globals_by_type.insert(global_type, Box::new(global));
2113 }
2114
2115 #[cfg(any(test, feature = "test-support"))]
2117 pub fn clear_globals(&mut self) {
2118 self.globals_by_type.drain();
2119 }
2120
2121 pub fn remove_global<G: Global>(&mut self) -> G {
2123 let global_type = TypeId::of::<G>();
2124 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2125 *self
2126 .globals_by_type
2127 .remove(&global_type)
2128 .unwrap_or_else(|| panic!("no global added for {}", std::any::type_name::<G>()))
2129 .downcast()
2130 .unwrap()
2131 }
2132
2133 pub fn observe_global<G: Global>(
2135 &mut self,
2136 mut f: impl FnMut(&mut Self) + 'static,
2137 ) -> Subscription {
2138 let (subscription, activate) = self.global_observers.insert(
2139 TypeId::of::<G>(),
2140 Box::new(move |cx| {
2141 f(cx);
2142 true
2143 }),
2144 );
2145 self.defer(move |_| activate());
2146 subscription
2147 }
2148
2149 #[track_caller]
2151 pub(crate) fn lease_global<G: Global>(&mut self) -> GlobalLease<G> {
2152 GlobalLease::new(
2153 self.globals_by_type
2154 .remove(&TypeId::of::<G>())
2155 .with_context(|| format!("no global registered of type {}", type_name::<G>()))
2156 .unwrap(),
2157 )
2158 }
2159
2160 pub(crate) fn end_global_lease<G: Global>(&mut self, lease: GlobalLease<G>) {
2162 let global_type = TypeId::of::<G>();
2163
2164 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2165 self.globals_by_type.insert(global_type, lease.global);
2166 }
2167
2168 pub(crate) fn new_entity_observer(
2169 &self,
2170 key: TypeId,
2171 value: NewEntityListener,
2172 ) -> Subscription {
2173 let (subscription, activate) = self.new_entity_observers.insert(key, value);
2174 activate();
2175 subscription
2176 }
2177
2178 pub fn observe_new<T: 'static>(
2181 &self,
2182 on_new: impl 'static + Fn(&mut T, Option<&mut Window>, &mut Context<T>),
2183 ) -> Subscription {
2184 self.new_entity_observer(
2185 TypeId::of::<T>(),
2186 Box::new(
2187 move |any_entity: AnyEntity, window: &mut Option<&mut Window>, cx: &mut App| {
2188 any_entity
2189 .downcast::<T>()
2190 .unwrap()
2191 .update(cx, |entity_state, cx| {
2192 on_new(entity_state, window.as_deref_mut(), cx)
2193 })
2194 },
2195 ),
2196 )
2197 }
2198
2199 pub fn observe_release<T>(
2202 &self,
2203 handle: &Entity<T>,
2204 on_release: impl FnOnce(&mut T, &mut App) + 'static,
2205 ) -> Subscription
2206 where
2207 T: 'static,
2208 {
2209 let (subscription, activate) = self.release_listeners.insert(
2210 handle.entity_id(),
2211 Box::new(move |entity, cx| {
2212 let entity = entity.downcast_mut().expect("invalid entity type");
2213 on_release(entity, cx)
2214 }),
2215 );
2216 activate();
2217 subscription
2218 }
2219
2220 pub fn observe_release_in<T>(
2223 &self,
2224 handle: &Entity<T>,
2225 window: &Window,
2226 on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static,
2227 ) -> Subscription
2228 where
2229 T: 'static,
2230 {
2231 let window_handle = window.handle;
2232 self.observe_release(handle, move |entity, cx| {
2233 let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx));
2234 })
2235 }
2236
2237 pub fn observe_keystrokes(
2242 &mut self,
2243 mut f: impl FnMut(&KeystrokeEvent, &mut Window, &mut App) + 'static,
2244 ) -> Subscription {
2245 fn inner(
2246 keystroke_observers: &SubscriberSet<(), KeystrokeObserver>,
2247 handler: KeystrokeObserver,
2248 ) -> Subscription {
2249 let (subscription, activate) = keystroke_observers.insert((), handler);
2250 activate();
2251 subscription
2252 }
2253
2254 inner(
2255 &self.keystroke_observers,
2256 Box::new(move |event, window, cx| {
2257 f(event, window, cx);
2258 true
2259 }),
2260 )
2261 }
2262
2263 pub fn on_missing_glyphs(&self, mut callback: impl FnMut(&[MissingGlyph], &mut App) + 'static) {
2274 let Some(mut receiver) = self.text_system.take_missing_glyph_receiver() else {
2275 log::warn!("App::on_missing_glyphs called more than once; ignoring registration");
2276 return;
2277 };
2278 self.spawn(async move |cx| {
2279 while let Ok(missing_glyphs) = receiver.recv().await {
2280 if cx.update(|cx| callback(&missing_glyphs, cx)).is_err() {
2281 break;
2283 }
2284 }
2285 })
2286 .detach();
2287 }
2288
2289 pub fn intercept_keystrokes(
2294 &mut self,
2295 mut f: impl FnMut(&KeystrokeEvent, &mut Window, &mut App) + 'static,
2296 ) -> Subscription {
2297 fn inner(
2298 keystroke_interceptors: &SubscriberSet<(), KeystrokeObserver>,
2299 handler: KeystrokeObserver,
2300 ) -> Subscription {
2301 let (subscription, activate) = keystroke_interceptors.insert((), handler);
2302 activate();
2303 subscription
2304 }
2305
2306 inner(
2307 &self.keystroke_interceptors,
2308 Box::new(move |event, window, cx| {
2309 f(event, window, cx);
2310 true
2311 }),
2312 )
2313 }
2314
2315 pub fn bind_keys(&mut self, bindings: impl IntoIterator<Item = KeyBinding>) {
2317 self.keymap.borrow_mut().add_bindings(bindings);
2318 self.pending_effects.push_back(Effect::RefreshWindows);
2319 }
2320
2321 pub fn clear_key_bindings(&mut self) {
2323 self.keymap.borrow_mut().clear();
2324 self.pending_effects.push_back(Effect::RefreshWindows);
2325 }
2326
2327 pub fn key_bindings(&self) -> Rc<RefCell<Keymap>> {
2329 self.keymap.clone()
2330 }
2331
2332 pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Self) + 'static) {
2336 self.global_action_listeners
2337 .entry(TypeId::of::<A>())
2338 .or_default()
2339 .push(Rc::new(move |action, phase, cx| {
2340 if phase == DispatchPhase::Bubble {
2341 let action = action.downcast_ref().unwrap();
2342 listener(action, cx)
2343 }
2344 }));
2345 }
2346
2347 pub fn stop_propagation(&mut self) {
2352 self.propagate_event = false;
2353 }
2354
2355 pub fn propagate(&mut self) {
2360 self.propagate_event = true;
2361 }
2362
2363 pub fn build_action(
2365 &self,
2366 name: &str,
2367 data: Option<serde_json::Value>,
2368 ) -> std::result::Result<Box<dyn Action>, ActionBuildError> {
2369 self.actions.build_action(name, data)
2370 }
2371
2372 pub fn all_action_names(&self) -> &[&'static str] {
2375 self.actions.all_action_names()
2376 }
2377
2378 pub fn all_bindings_for_input(&self, input: &[Keystroke]) -> Vec<KeyBinding> {
2382 RefCell::borrow(&self.keymap).all_bindings_for_input(input)
2383 }
2384
2385 pub fn action_schemas(
2387 &self,
2388 generator: &mut schemars::SchemaGenerator,
2389 ) -> Vec<(&'static str, Option<schemars::Schema>)> {
2390 self.actions.action_schemas(generator)
2391 }
2392
2393 pub fn deprecated_actions_to_preferred_actions(&self) -> &HashMap<&'static str, &'static str> {
2395 self.actions.deprecated_aliases()
2396 }
2397
2398 pub fn action_deprecation_messages(&self) -> &HashMap<&'static str, &'static str> {
2400 self.actions.deprecation_messages()
2401 }
2402
2403 pub fn action_documentation(&self) -> &HashMap<&'static str, &'static str> {
2405 self.actions.documentation()
2406 }
2407
2408 pub fn on_app_quit<Fut>(
2411 &self,
2412 mut on_quit: impl FnMut(&mut App) -> Fut + 'static,
2413 ) -> Subscription
2414 where
2415 Fut: 'static + Future<Output = ()>,
2416 {
2417 let (subscription, activate) = self.quit_observers.insert(
2418 (),
2419 Box::new(move |cx| {
2420 let future = on_quit(cx);
2421 future.boxed_local()
2422 }),
2423 );
2424 activate();
2425 subscription
2426 }
2427
2428 pub fn on_app_restart(&self, mut on_restart: impl 'static + FnMut(&mut App)) -> Subscription {
2432 let (subscription, activate) = self.restart_observers.insert(
2433 (),
2434 Box::new(move |cx| {
2435 on_restart(cx);
2436 true
2437 }),
2438 );
2439 activate();
2440 subscription
2441 }
2442
2443 pub fn on_window_closed(&self, mut on_closed: impl FnMut(&mut App) + 'static) -> Subscription {
2446 let (subscription, activate) = self.window_closed_observers.insert((), Box::new(on_closed));
2447 activate();
2448 subscription
2449 }
2450
2451 pub(crate) fn clear_pending_keystrokes(&mut self) {
2452 for window in self.windows() {
2453 window
2454 .update(self, |_, window, cx| {
2455 window.clear_pending_keystrokes(cx);
2456 })
2457 .ok();
2458 }
2459 }
2460
2461 pub fn is_action_available(&mut self, action: &dyn Action) -> bool {
2464 let mut action_available = false;
2465 if let Some(window) = self.active_window()
2466 && let Ok(window_action_available) =
2467 window.update(self, |_, window, cx| window.is_action_available(action, cx))
2468 {
2469 action_available = window_action_available;
2470 }
2471
2472 action_available
2473 || self
2474 .global_action_listeners
2475 .contains_key(&action.as_any().type_id())
2476 }
2477
2478 pub fn set_menus(&self, menus: Vec<Menu>) {
2480 self.platform.set_menus(menus, &self.keymap.borrow());
2481 }
2482
2483 pub fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
2485 self.platform.get_menus()
2486 }
2487
2488 pub fn set_dock_menu(&self, menus: Vec<MenuItem>) {
2490 self.platform.set_dock_menu(menus, &self.keymap.borrow())
2491 }
2492
2493 pub fn perform_dock_menu_action(&self, action: usize) {
2495 self.platform.perform_dock_menu_action(action);
2496 }
2497
2498 pub fn add_recent_document(&self, path: &Path) {
2503 self.platform.add_recent_document(path);
2504 }
2505
2506 pub fn update_jump_list(
2509 &self,
2510 menus: Vec<MenuItem>,
2511 entries: Vec<SmallVec<[PathBuf; 2]>>,
2512 ) -> Vec<SmallVec<[PathBuf; 2]>> {
2513 self.platform.update_jump_list(menus, entries)
2514 }
2515
2516 pub fn dispatch_action(&mut self, action: &dyn Action) {
2519 if let Some(active_window) = self.active_window() {
2520 active_window
2521 .update(self, |_, window, cx| {
2522 window.dispatch_action(action.boxed_clone(), cx)
2523 })
2524 .log_err();
2525 } else {
2526 self.dispatch_global_action(action);
2527 }
2528 }
2529
2530 fn dispatch_global_action(&mut self, action: &dyn Action) {
2531 self.propagate_event = true;
2532
2533 if let Some(mut global_listeners) = self
2534 .global_action_listeners
2535 .remove(&action.as_any().type_id())
2536 {
2537 for listener in &global_listeners {
2538 listener(action.as_any(), DispatchPhase::Capture, self);
2539 if !self.propagate_event {
2540 break;
2541 }
2542 }
2543
2544 global_listeners.extend(
2545 self.global_action_listeners
2546 .remove(&action.as_any().type_id())
2547 .unwrap_or_default(),
2548 );
2549
2550 self.global_action_listeners
2551 .insert(action.as_any().type_id(), global_listeners);
2552 }
2553
2554 if self.propagate_event
2555 && let Some(mut global_listeners) = self
2556 .global_action_listeners
2557 .remove(&action.as_any().type_id())
2558 {
2559 for listener in global_listeners.iter().rev() {
2560 listener(action.as_any(), DispatchPhase::Bubble, self);
2561 if !self.propagate_event {
2562 break;
2563 }
2564 }
2565
2566 global_listeners.extend(
2567 self.global_action_listeners
2568 .remove(&action.as_any().type_id())
2569 .unwrap_or_default(),
2570 );
2571
2572 self.global_action_listeners
2573 .insert(action.as_any().type_id(), global_listeners);
2574 }
2575 }
2576
2577 pub fn has_active_drag(&self) -> bool {
2579 self.active_drag.is_some()
2580 }
2581
2582 pub fn active_drag_cursor_style(&self) -> Option<CursorStyle> {
2584 self.active_drag.as_ref().and_then(|drag| drag.cursor_style)
2585 }
2586
2587 pub fn stop_active_drag(&mut self, window: &mut Window) -> bool {
2589 if self.active_drag.is_some() {
2590 self.active_drag = None;
2591 window.refresh();
2592 true
2593 } else {
2594 false
2595 }
2596 }
2597
2598 pub fn set_active_drag_cursor_style(
2600 &mut self,
2601 cursor_style: CursorStyle,
2602 window: &mut Window,
2603 ) -> bool {
2604 if let Some(ref mut drag) = self.active_drag {
2605 drag.cursor_style = Some(cursor_style);
2606 window.refresh();
2607 true
2608 } else {
2609 false
2610 }
2611 }
2612
2613 pub fn set_prompt_builder(
2616 &mut self,
2617 renderer: impl Fn(
2618 PromptLevel,
2619 &str,
2620 Option<&str>,
2621 &[PromptButton],
2622 PromptHandle,
2623 &mut Window,
2624 &mut App,
2625 ) -> RenderablePromptHandle
2626 + 'static,
2627 ) {
2628 self.prompt_builder = Some(PromptBuilder::Custom(Box::new(renderer)));
2629 }
2630
2631 pub fn reset_prompt_builder(&mut self) {
2633 self.prompt_builder = Some(PromptBuilder::Default);
2634 }
2635
2636 pub fn remove_asset<A: Asset>(&mut self, source: &A::Source) {
2638 let asset_id = (TypeId::of::<A>(), hash(source));
2639 self.loading_assets.remove(&asset_id);
2640 }
2641
2642 #[cfg(any(test, feature = "test-support"))]
2645 pub fn has_asset<A: Asset>(&self, source: &A::Source) -> bool {
2646 let asset_id = (TypeId::of::<A>(), hash(source));
2647 self.loading_assets.contains_key(&asset_id)
2648 }
2649
2650 pub fn fetch_asset<A: Asset>(&mut self, source: &A::Source) -> Option<A::Output> {
2655 self.asset_entry::<A>(source).get()
2656 }
2657
2658 pub(crate) fn asset_entry<A: Asset>(&mut self, source: &A::Source) -> &CachedLoad<A::Output> {
2659 let asset_id = (TypeId::of::<A>(), hash(source));
2660 if !self.loading_assets.contains_key(&asset_id) {
2661 let future = A::load(source.clone(), self);
2662 let entry = CachedLoad::new(future, self);
2663 self.loading_assets.insert(asset_id, Box::new(entry));
2664 }
2665 self.loading_assets
2666 .get(&asset_id)
2667 .and_then(|entry| entry.downcast_ref())
2668 .expect("asset cache entries are keyed by their asset type")
2669 }
2670
2671 #[track_caller]
2674 pub fn focus_handle(&self) -> FocusHandle {
2675 FocusHandle::new(&self.focus_handles)
2676 }
2677
2678 pub fn notify(&mut self, entity_id: EntityId) {
2680 let window_invalidators = mem::take(
2681 self.window_invalidators_by_entity
2682 .entry(entity_id)
2683 .or_default(),
2684 );
2685
2686 if window_invalidators.is_empty() {
2687 if self.pending_notifications.insert(entity_id) {
2688 self.pending_effects
2689 .push_back(Effect::Notify { emitter: entity_id });
2690 }
2691 } else {
2692 for invalidator in window_invalidators.values() {
2693 invalidator.invalidate_view(entity_id, self);
2694 }
2695 }
2696
2697 self.window_invalidators_by_entity
2698 .insert(entity_id, window_invalidators);
2699 }
2700
2701 #[cfg(any(test, feature = "test-support", debug_assertions))]
2703 pub fn get_name(&self) -> Option<&'static str> {
2704 self.name
2705 }
2706
2707 pub fn can_select_mixed_files_and_dirs(&self) -> bool {
2709 self.platform.can_select_mixed_files_and_dirs()
2710 }
2711
2712 pub fn drop_image(&mut self, image: Arc<RenderImage>, current_window: Option<&mut Window>) {
2717 for window in self.windows.values_mut().flatten() {
2719 _ = window.drop_image(image.clone());
2720 }
2721
2722 if let Some(window) = current_window {
2724 _ = window.drop_image(image);
2725 }
2726 }
2727
2728 #[cfg(any(feature = "inspector", debug_assertions))]
2730 pub fn set_inspector_renderer(&mut self, f: crate::InspectorRenderer) {
2731 self.inspector_renderer = Some(f);
2732 }
2733
2734 #[cfg(any(feature = "inspector", debug_assertions))]
2736 pub fn register_inspector_element<T: 'static, R: crate::IntoElement>(
2737 &mut self,
2738 f: impl 'static + Fn(crate::InspectorElementId, &T, &mut Window, &mut App) -> R,
2739 ) {
2740 self.inspector_element_registry.register(f);
2741 }
2742
2743 pub fn init_colors(&mut self) {
2747 self.set_global(GlobalColors(Arc::new(Colors::default())));
2748 }
2749}
2750
2751impl AppContext for App {
2752 type Result<T> = T;
2753
2754 fn new<T: 'static>(&mut self, build_entity: impl FnOnce(&mut Context<T>) -> T) -> Entity<T> {
2759 self.update(|cx| {
2760 let slot = cx.entities.reserve();
2761 let handle = slot.clone();
2762 let entity = build_entity(&mut Context::new_context(cx, slot.downgrade()));
2763
2764 cx.push_effect(Effect::EntityCreated {
2765 entity: handle.clone().into_any(),
2766 tid: TypeId::of::<T>(),
2767 window: cx.window_update_stack.last().cloned(),
2768 });
2769
2770 cx.entities.insert(slot, entity);
2771 handle
2772 })
2773 }
2774
2775 fn reserve_entity<T: 'static>(&mut self) -> Self::Result<Reservation<T>> {
2776 Reservation(self.entities.reserve())
2777 }
2778
2779 fn insert_entity<T: 'static>(
2780 &mut self,
2781 reservation: Reservation<T>,
2782 build_entity: impl FnOnce(&mut Context<T>) -> T,
2783 ) -> Self::Result<Entity<T>> {
2784 self.update(|cx| {
2785 let slot = reservation.0;
2786 let entity = build_entity(&mut Context::new_context(cx, slot.downgrade()));
2787 cx.entities.insert(slot, entity)
2788 })
2789 }
2790
2791 fn update_entity<T: 'static, R>(
2794 &mut self,
2795 handle: &Entity<T>,
2796 update: impl FnOnce(&mut T, &mut Context<T>) -> R,
2797 ) -> R {
2798 self.update(|cx| {
2799 let mut entity = cx.entities.lease(handle);
2800 let result = update(
2801 &mut entity,
2802 &mut Context::new_context(cx, handle.downgrade()),
2803 );
2804 cx.entities.end_lease(entity);
2805 result
2806 })
2807 }
2808
2809 fn as_mut<'a, T>(&'a mut self, handle: &Entity<T>) -> GpuiBorrow<'a, T>
2810 where
2811 T: 'static,
2812 {
2813 GpuiBorrow::new(handle.clone(), self)
2814 }
2815
2816 fn read_entity<T, R>(
2817 &self,
2818 handle: &Entity<T>,
2819 read: impl FnOnce(&T, &App) -> R,
2820 ) -> Self::Result<R>
2821 where
2822 T: 'static,
2823 {
2824 let entity = self.entities.read(handle);
2825 read(entity, self)
2826 }
2827
2828 fn update_window<T, F>(&mut self, handle: AnyWindowHandle, update: F) -> Result<T>
2829 where
2830 F: FnOnce(AnyView, &mut Window, &mut App) -> T,
2831 {
2832 self.update_window_id(handle.id, update)
2833 }
2834
2835 fn read_window<T, R>(
2836 &self,
2837 window: &WindowHandle<T>,
2838 read: impl FnOnce(Entity<T>, &App) -> R,
2839 ) -> Result<R>
2840 where
2841 T: 'static,
2842 {
2843 let window = self
2844 .windows
2845 .get(window.id)
2846 .context("window not found")?
2847 .as_ref()
2848 .expect("attempted to read a window that is already on the stack");
2849
2850 let root_view = window.root.clone().unwrap();
2851 let view = root_view
2852 .downcast::<T>()
2853 .map_err(|_| anyhow!("root view's type has changed"))?;
2854
2855 Ok(read(view, self))
2856 }
2857
2858 fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
2859 where
2860 R: Send + 'static,
2861 {
2862 self.background_executor.spawn(future)
2863 }
2864
2865 fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
2866 where
2867 G: Global,
2868 {
2869 let mut g = self.global::<G>();
2870 callback(g, self)
2871 }
2872}
2873
2874pub(crate) enum Effect {
2876 Notify {
2877 emitter: EntityId,
2878 },
2879 Emit {
2880 emitter: EntityId,
2881 event_type: TypeId,
2882 event: Box<dyn Any>,
2883 },
2884 RefreshWindows,
2885 NotifyGlobalObservers {
2886 global_type: TypeId,
2887 },
2888 Defer {
2889 callback: Box<dyn FnOnce(&mut App) + 'static>,
2890 },
2891 EntityCreated {
2892 entity: AnyEntity,
2893 tid: TypeId,
2894 window: Option<WindowId>,
2895 },
2896}
2897
2898impl std::fmt::Debug for Effect {
2899 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2900 match self {
2901 Effect::Notify { emitter } => write!(f, "Notify({})", emitter),
2902 Effect::Emit { emitter, .. } => write!(f, "Emit({:?})", emitter),
2903 Effect::RefreshWindows => write!(f, "RefreshWindows"),
2904 Effect::NotifyGlobalObservers { global_type } => {
2905 write!(f, "NotifyGlobalObservers({:?})", global_type)
2906 }
2907 Effect::Defer { .. } => write!(f, "Defer(..)"),
2908 Effect::EntityCreated { entity, .. } => write!(f, "EntityCreated({:?})", entity),
2909 }
2910 }
2911}
2912
2913pub(crate) struct GlobalLease<G: Global> {
2915 global: Box<dyn Any>,
2916 global_type: PhantomData<G>,
2917}
2918
2919impl<G: Global> GlobalLease<G> {
2920 fn new(global: Box<dyn Any>) -> Self {
2921 GlobalLease {
2922 global,
2923 global_type: PhantomData,
2924 }
2925 }
2926}
2927
2928impl<G: Global> Deref for GlobalLease<G> {
2929 type Target = G;
2930
2931 fn deref(&self) -> &Self::Target {
2932 self.global.downcast_ref().unwrap()
2933 }
2934}
2935
2936impl<G: Global> DerefMut for GlobalLease<G> {
2937 fn deref_mut(&mut self) -> &mut Self::Target {
2938 self.global.downcast_mut().unwrap()
2939 }
2940}
2941
2942pub struct AnyDrag {
2945 pub view: AnyView,
2947
2948 pub value: Arc<dyn Any>,
2950
2951 pub cursor_offset: Point<Pixels>,
2954
2955 pub cursor_style: Option<CursorStyle>,
2957}
2958
2959#[derive(Clone)]
2962pub struct AnyTooltip {
2963 pub view: AnyView,
2965
2966 pub mouse_position: Point<Pixels>,
2968
2969 pub check_visible_and_update: Rc<dyn Fn(Bounds<Pixels>, &mut Window, &mut App) -> bool>,
2973}
2974
2975#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2977pub enum InputPreference {
2978 CharacterInput,
2980 KeyBindings,
2982}
2983
2984#[derive(Debug)]
2986pub struct KeystrokeEvent {
2987 pub keystroke: Keystroke,
2989
2990 pub input_preference: InputPreference,
2998
2999 pub action: Option<Box<dyn Action>>,
3001
3002 pub context_stack: Vec<KeyContext>,
3004}
3005
3006struct NullHttpClient;
3007
3008impl HttpClient for NullHttpClient {
3009 fn send(
3010 &self,
3011 _req: http_client::Request<http_client::AsyncBody>,
3012 ) -> futures::future::BoxFuture<
3013 'static,
3014 anyhow::Result<http_client::Response<http_client::AsyncBody>>,
3015 > {
3016 async move {
3017 anyhow::bail!("No HttpClient available");
3018 }
3019 .boxed()
3020 }
3021
3022 fn user_agent(&self) -> Option<&http_client::http::HeaderValue> {
3023 None
3024 }
3025
3026 fn proxy(&self) -> Option<&Url> {
3027 None
3028 }
3029
3030 fn type_name(&self) -> &'static str {
3031 type_name::<Self>()
3032 }
3033}
3034
3035pub struct GpuiBorrow<'a, T> {
3037 inner: Option<Lease<T>>,
3038 app: &'a mut App,
3039}
3040
3041impl<'a, T: 'static> GpuiBorrow<'a, T> {
3042 fn new(inner: Entity<T>, app: &'a mut App) -> Self {
3043 app.start_update();
3044 let lease = app.entities.lease(&inner);
3045 Self {
3046 inner: Some(lease),
3047 app,
3048 }
3049 }
3050}
3051
3052impl<'a, T: 'static> std::borrow::Borrow<T> for GpuiBorrow<'a, T> {
3053 fn borrow(&self) -> &T {
3054 self.inner.as_ref().unwrap().borrow()
3055 }
3056}
3057
3058impl<'a, T: 'static> std::borrow::BorrowMut<T> for GpuiBorrow<'a, T> {
3059 fn borrow_mut(&mut self) -> &mut T {
3060 self.inner.as_mut().unwrap().borrow_mut()
3061 }
3062}
3063
3064impl<'a, T: 'static> std::ops::Deref for GpuiBorrow<'a, T> {
3065 type Target = T;
3066
3067 fn deref(&self) -> &Self::Target {
3068 self.inner.as_ref().unwrap()
3069 }
3070}
3071
3072impl<'a, T: 'static> std::ops::DerefMut for GpuiBorrow<'a, T> {
3073 fn deref_mut(&mut self) -> &mut T {
3074 self.inner.as_mut().unwrap()
3075 }
3076}
3077
3078impl<'a, T> Drop for GpuiBorrow<'a, T> {
3079 fn drop(&mut self) {
3080 let lease = self.inner.take().unwrap();
3081 self.app.notify(lease.id);
3082 self.app.entities.end_lease(lease);
3083 self.app.finish_update();
3084 }
3085}
3086
3087#[cfg(test)]
3088mod test {
3089 use std::{
3090 cell::{Cell, RefCell},
3091 ffi::OsString,
3092 path::PathBuf,
3093 rc::Rc,
3094 sync::Arc,
3095 };
3096
3097 #[cfg(unix)]
3098 use std::os::unix::ffi::OsStringExt;
3099
3100 use rand::{SeedableRng, rngs::StdRng};
3101
3102 use super::{Application, ApplicationHandle, NullHttpClient};
3103 use crate::{
3104 AppContext, AppResourceProfile, BackgroundExecutor, Context, Empty, ForegroundExecutor,
3105 IntoElement, Platform, QuitMode, Render, TestAppContext, TestDispatcher, TestPlatform,
3106 TrayIconClickEvent, TrayIconEvent, TrayIconRenderingMode, Window, WindowAppearance, point,
3107 px,
3108 };
3109
3110 struct RenderCounter(Rc<Cell<usize>>);
3111
3112 impl Render for RenderCounter {
3113 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
3114 self.0.set(self.0.get() + 1);
3115 Empty
3116 }
3117 }
3118
3119 #[crate::test]
3120 fn async_app_refresh_flushes_refresh_effect(cx: &mut TestAppContext) {
3121 let render_count = Rc::new(Cell::new(0));
3122
3123 let _window = cx.add_window({
3124 let render_count = render_count.clone();
3125 move |_, _| RenderCounter(render_count)
3126 });
3127
3128 cx.run_until_parked();
3129 let render_count_before_refresh = render_count.get();
3130
3131 cx.to_async().refresh().unwrap();
3132
3133 assert_eq!(render_count.get(), render_count_before_refresh + 1);
3134 }
3135
3136 #[test]
3137 fn test_with_platform_uses_injected_platform() {
3138 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3139 let platform: Rc<dyn Platform> = TestPlatform::new(
3140 BackgroundExecutor::new(dispatcher.clone()),
3141 ForegroundExecutor::new(dispatcher),
3142 );
3143
3144 let application = Application::with_platform(platform.clone());
3145
3146 assert!(Rc::ptr_eq(&application.0.borrow().platform, &platform));
3147 }
3148
3149 #[test]
3150 fn test_with_quit_mode_updates_core_and_platform_state() {
3151 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3152 let platform = TestPlatform::new(
3153 BackgroundExecutor::new(dispatcher.clone()),
3154 ForegroundExecutor::new(dispatcher),
3155 );
3156
3157 let application =
3158 Application::with_platform(platform.clone()).with_quit_mode(QuitMode::Explicit);
3159
3160 assert_eq!(application.0.borrow().quit_mode, QuitMode::Explicit);
3161 assert_eq!(*platform.last_quit_mode.lock(), Some(QuitMode::Explicit));
3162 }
3163
3164 #[test]
3165 fn test_inaccessible_sets_force_disabled() {
3166 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3167 let platform = TestPlatform::new(
3168 BackgroundExecutor::new(dispatcher.clone()),
3169 ForegroundExecutor::new(dispatcher),
3170 );
3171 let application = Application(super::App::new_app(
3172 platform,
3173 Arc::new(()),
3174 Arc::new(NullHttpClient),
3175 AppResourceProfile::default(),
3176 ))
3177 .inaccessible();
3178
3179 assert!(application.0.borrow().accessibility_force_disabled);
3180 }
3181
3182 #[test]
3183 fn test_with_resource_profile_rebuilds_text_system_budget() {
3184 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3185 let platform = TestPlatform::new(
3186 BackgroundExecutor::new(dispatcher.clone()),
3187 ForegroundExecutor::new(dispatcher),
3188 );
3189 let mut profile = AppResourceProfile::default();
3190 profile.text.line_layout_cache_max_entries = 7;
3191 profile.text.line_layout_cache_low_watermark = 3;
3192 profile.text.raster_bounds_cache_max_entries = Some(11);
3193 profile.element_arena_size =
3194 crate::window::ELEMENT_ARENA_SIZE.load(std::sync::atomic::Ordering::Relaxed);
3195
3196 let application = Application(super::App::new_app(
3197 platform,
3198 Arc::new(()),
3199 Arc::new(NullHttpClient),
3200 AppResourceProfile::default(),
3201 ))
3202 .with_resource_profile(profile);
3203
3204 let app = application.0.borrow();
3205 assert_eq!(app.text_system.resource_budget_for_test(), (7, 3, Some(11)));
3206 assert_eq!(app.resource_profile.text.line_layout_cache_max_entries, 7);
3207 assert_eq!(
3208 app.resource_profile.gpu.instance_buffer_initial_size,
3209 2 * 1024 * 1024
3210 );
3211 }
3212
3213 #[test]
3214 fn test_with_resource_profile_updates_gpu_budget() {
3215 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3216 let platform = TestPlatform::new(
3217 BackgroundExecutor::new(dispatcher.clone()),
3218 ForegroundExecutor::new(dispatcher),
3219 );
3220 let platform_handle = platform.clone();
3221 let mut profile = AppResourceProfile::default();
3222 profile.gpu.instance_buffer_initial_size = 768 * 1024;
3223 profile.element_arena_size =
3224 crate::window::ELEMENT_ARENA_SIZE.load(std::sync::atomic::Ordering::Relaxed);
3225
3226 let application = Application(super::App::new_app(
3227 platform,
3228 Arc::new(()),
3229 Arc::new(NullHttpClient),
3230 AppResourceProfile::default(),
3231 ))
3232 .with_resource_profile(profile);
3233
3234 let app = application.0.borrow();
3235 assert_eq!(
3236 app.resource_profile.gpu.instance_buffer_initial_size,
3237 768 * 1024
3238 );
3239 assert_eq!(
3240 platform_handle
3241 .gpu_resource_budget
3242 .lock()
3243 .instance_buffer_initial_size,
3244 768 * 1024
3245 );
3246 }
3247
3248 #[test]
3249 fn test_application_handle_keeps_app_alive_for_embedders() {
3250 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3251 let platform = TestPlatform::new(
3252 BackgroundExecutor::new(dispatcher.clone()),
3253 ForegroundExecutor::new(dispatcher),
3254 );
3255 let application = Application(super::App::new_app(
3256 platform,
3257 Arc::new(()),
3258 Arc::new(NullHttpClient),
3259 AppResourceProfile::default(),
3260 ));
3261 let weak_app = Rc::downgrade(&application.0);
3262 let handle = ApplicationHandle {
3263 app: application.0.clone(),
3264 };
3265 drop(application);
3266
3267 assert!(weak_app.upgrade().is_some());
3268
3269 let did_update = Rc::new(RefCell::new(false));
3270 let did_update_clone = did_update.clone();
3271 handle.update(move |_| {
3272 *did_update_clone.borrow_mut() = true;
3273 });
3274
3275 assert!(*did_update.borrow());
3276
3277 drop(handle);
3278 assert!(weak_app.upgrade().is_none());
3279 }
3280
3281 #[test]
3282 fn test_with_resource_profile_clamps_line_layout_cache_to_one_entry() {
3283 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3284 let platform = TestPlatform::new(
3285 BackgroundExecutor::new(dispatcher.clone()),
3286 ForegroundExecutor::new(dispatcher),
3287 );
3288 let mut profile = AppResourceProfile::default();
3289 profile.text.line_layout_cache_max_entries = 0;
3290 profile.text.line_layout_cache_low_watermark = 5;
3291 profile.element_arena_size =
3292 crate::window::ELEMENT_ARENA_SIZE.load(std::sync::atomic::Ordering::Relaxed);
3293
3294 let application = Application(super::App::new_app(
3295 platform,
3296 Arc::new(()),
3297 Arc::new(NullHttpClient),
3298 AppResourceProfile::default(),
3299 ))
3300 .with_resource_profile(profile);
3301
3302 let app = application.0.borrow();
3303 assert_eq!(
3304 app.text_system.resource_budget_for_test(),
3305 (1, 0, None),
3306 "line layout caches should retain a valid minimum capacity"
3307 );
3308 }
3309
3310 #[test]
3311 fn test_gpui_borrow() {
3312 let cx = TestAppContext::single();
3313 let observation_count = Rc::new(RefCell::new(0));
3314
3315 let state = cx.update(|cx| {
3316 let state = cx.new(|_| false);
3317 cx.observe(&state, {
3318 let observation_count = observation_count.clone();
3319 move |_, _| {
3320 let mut count = observation_count.borrow_mut();
3321 *count += 1;
3322 }
3323 })
3324 .detach();
3325
3326 state
3327 });
3328
3329 cx.update(|cx| {
3330 *std::borrow::BorrowMut::borrow_mut(&mut state.as_mut(cx)) = true;
3332 });
3333
3334 cx.update(|cx| {
3335 state.write(cx, false);
3336 });
3337
3338 assert_eq!(*observation_count.borrow(), 2);
3339 }
3340
3341 #[test]
3342 fn test_tray_icon_rendering_mode_default() {
3343 assert_eq!(
3344 TrayIconRenderingMode::default(),
3345 TrayIconRenderingMode::Adaptive
3346 );
3347 }
3348
3349 #[test]
3350 fn test_set_tray_icon_forwards_to_platform() {
3351 let cx = TestAppContext::single();
3352 let icon = [1_u8, 2, 3, 4];
3353
3354 cx.update(|cx| {
3355 cx.set_tray_icon(Some(&icon));
3356 });
3357
3358 assert_eq!(cx.tray_icon(), Some(icon.to_vec()));
3359 }
3360
3361 #[test]
3362 fn test_set_tray_icon_rendering_mode_forwards_to_platform() {
3363 let cx = TestAppContext::single();
3364
3365 cx.update(|cx| {
3366 cx.set_tray_icon_rendering_mode(TrayIconRenderingMode::Original);
3367 });
3368
3369 assert_eq!(
3370 cx.tray_icon_rendering_mode(),
3371 TrayIconRenderingMode::Original
3372 );
3373 }
3374
3375 #[test]
3376 fn test_tray_icon_rendering_mode_and_icon_updates_are_order_independent() {
3377 let first = TestAppContext::single();
3378 let second = TestAppContext::single();
3379 let icon = [9_u8, 8, 7, 6];
3380
3381 first.update(|cx| {
3382 cx.set_tray_icon_rendering_mode(TrayIconRenderingMode::Original);
3383 cx.set_tray_icon(Some(&icon));
3384 });
3385
3386 second.update(|cx| {
3387 cx.set_tray_icon(Some(&icon));
3388 cx.set_tray_icon_rendering_mode(TrayIconRenderingMode::Original);
3389 });
3390
3391 assert_eq!(first.tray_icon(), Some(icon.to_vec()));
3392 assert_eq!(second.tray_icon(), Some(icon.to_vec()));
3393 assert_eq!(
3394 first.tray_icon_rendering_mode(),
3395 TrayIconRenderingMode::Original
3396 );
3397 assert_eq!(
3398 second.tray_icon_rendering_mode(),
3399 TrayIconRenderingMode::Original
3400 );
3401 }
3402
3403 #[test]
3404 fn test_tray_icon_click_event_keeps_legacy_callback_compatible() {
3405 let cx = TestAppContext::single();
3406 let legacy_event = Rc::new(RefCell::new(None));
3407 let click_event = Rc::new(RefCell::new(None));
3408
3409 cx.update({
3410 let legacy_event = legacy_event.clone();
3411 let click_event = click_event.clone();
3412 |cx| {
3413 cx.on_tray_icon_event(move |event, _| {
3414 *legacy_event.borrow_mut() = Some(event);
3415 });
3416 cx.on_tray_icon_click_event(move |event, _| {
3417 *click_event.borrow_mut() = Some(event);
3418 });
3419 }
3420 });
3421
3422 let event =
3423 TrayIconClickEvent::with_position(TrayIconEvent::LeftClick, point(px(120.0), px(24.0)));
3424 cx.simulate_tray_icon_click_event(event.clone());
3425
3426 assert_eq!(*legacy_event.borrow(), Some(TrayIconEvent::LeftClick));
3427 assert_eq!(*click_event.borrow(), Some(event));
3428 }
3429
3430 #[test]
3431 fn test_tray_icon_callbacks_keep_re_registration_during_dispatch() {
3432 let cx = TestAppContext::single();
3433 let legacy_events = Rc::new(RefCell::new(Vec::new()));
3434 let click_events = Rc::new(RefCell::new(Vec::new()));
3435
3436 cx.update({
3437 let legacy_events = legacy_events.clone();
3438 let click_events = click_events.clone();
3439 move |cx| {
3440 cx.on_tray_icon_event({
3441 let legacy_events = legacy_events.clone();
3442 move |event, cx| {
3443 legacy_events.borrow_mut().push(("old", event));
3444 cx.on_tray_icon_event({
3445 let legacy_events = legacy_events.clone();
3446 move |event, _| {
3447 legacy_events.borrow_mut().push(("new", event));
3448 }
3449 });
3450 }
3451 });
3452
3453 cx.on_tray_icon_click_event({
3454 let click_events = click_events.clone();
3455 move |event, cx| {
3456 click_events.borrow_mut().push(("old", event.kind));
3457 cx.on_tray_icon_click_event({
3458 let click_events = click_events.clone();
3459 move |event, _| {
3460 click_events.borrow_mut().push(("new", event.kind));
3461 }
3462 });
3463 }
3464 });
3465 }
3466 });
3467
3468 cx.simulate_tray_icon_click_event(TrayIconClickEvent::new(TrayIconEvent::LeftClick));
3469 cx.simulate_tray_icon_click_event(TrayIconClickEvent::new(TrayIconEvent::RightClick));
3470
3471 assert_eq!(
3472 *legacy_events.borrow(),
3473 vec![
3474 ("old", TrayIconEvent::LeftClick),
3475 ("new", TrayIconEvent::RightClick)
3476 ]
3477 );
3478 assert_eq!(
3479 *click_events.borrow(),
3480 vec![
3481 ("old", TrayIconEvent::LeftClick),
3482 ("new", TrayIconEvent::RightClick)
3483 ]
3484 );
3485 }
3486
3487 #[test]
3488 fn test_set_window_appearance_override_is_reported_by_getter() {
3489 let cx = TestAppContext::single();
3490 assert_eq!(
3491 cx.read(|cx| cx.window_appearance()),
3492 WindowAppearance::Light
3493 );
3494
3495 cx.update(|cx| cx.set_window_appearance(Some(WindowAppearance::Dark)));
3496 assert_eq!(cx.read(|cx| cx.window_appearance()), WindowAppearance::Dark);
3497
3498 cx.update(|cx| cx.set_window_appearance(Some(WindowAppearance::VibrantLight)));
3499 assert_eq!(
3500 cx.read(|cx| cx.window_appearance()),
3501 WindowAppearance::VibrantLight
3502 );
3503
3504 cx.update(|cx| cx.set_window_appearance(None));
3505 assert_eq!(
3506 cx.read(|cx| cx.window_appearance()),
3507 WindowAppearance::Light
3508 );
3509 }
3510
3511 #[test]
3512 fn test_tray_anchor_for_position_uses_display_local_bounds() {
3513 let cx = TestAppContext::single();
3514 let anchor = cx.update(|cx| cx.tray_anchor_for_position(point(px(120.0), px(24.0))));
3515 let anchor = anchor.expect("expected tray anchor");
3516
3517 assert_eq!(anchor.display_id.0, 1);
3518 assert_eq!(anchor.bounds.origin, point(px(108.0), px(12.0)));
3519 assert_eq!(anchor.bounds.size.width, px(24.0));
3520 assert_eq!(anchor.bounds.size.height, px(24.0));
3521 }
3522
3523 #[test]
3524 fn test_tray_anchor_for_position_keeps_out_of_display_hint() {
3525 let cx = TestAppContext::single();
3526 let anchor = cx.update(|cx| cx.tray_anchor_for_position(point(px(-20.0), px(10.0))));
3527 let anchor = anchor.expect("expected tray anchor");
3528
3529 assert_eq!(anchor.display_id.0, 1);
3530 assert_eq!(anchor.bounds.origin, point(px(-32.0), px(-2.0)));
3531 }
3532
3533 #[crate::test]
3534 async fn test_on_missing_glyphs_delivers_reports(cx: &mut TestAppContext) {
3535 use crate::{FallbackFontClass, MissingGlyph};
3536
3537 let reported = Rc::new(RefCell::new(Vec::new()));
3538 cx.update(|cx| {
3539 cx.on_missing_glyphs({
3540 let reported = reported.clone();
3541 move |glyphs, _| reported.borrow_mut().extend(glyphs.iter().cloned())
3542 });
3543 cx.text_system().report_missing_glyphs_in_test(vec![
3544 MissingGlyph::new("\u{1F980}".into(), FallbackFontClass::Monospace),
3545 MissingGlyph::new("\u{1F980}".into(), FallbackFontClass::Monospace),
3546 ]);
3547 });
3548
3549 cx.run_until_parked();
3550
3551 assert_eq!(
3553 reported.borrow().as_slice(),
3554 &[MissingGlyph::new(
3555 "\u{1F980}".into(),
3556 FallbackFontClass::Monospace
3557 )]
3558 );
3559 }
3560
3561 #[crate::test]
3562 async fn test_restart_preserves_path_and_arguments(cx: &mut TestAppContext) {
3563 #[cfg(unix)]
3564 let user_data_dir = OsString::from_vec(b"/tmp/zed data/\xff".to_vec());
3565 #[cfg(not(unix))]
3566 let user_data_dir = OsString::from("C:\\zed data");
3567 let arguments = vec![OsString::from("--user-data-dir"), user_data_dir];
3568 let restart_path = PathBuf::from("updated-zed");
3569 let _application =
3570 super::Application(cx.app.clone()).with_restart_arguments(arguments.clone());
3571 let restart = cx.expect_restart();
3572
3573 cx.update(|cx| {
3574 cx.set_restart_path(restart_path.clone());
3575 cx.restart();
3576 });
3577
3578 let (path, restart_arguments) = restart.await.expect("restart was not requested");
3579 assert_eq!(path, Some(restart_path));
3580 assert_eq!(restart_arguments, arguments);
3581 }
3582}