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) {
944 self.platform.trim_renderer_caches();
945 }
946
947 pub fn renderer_cache_stats(&self) -> crate::RendererCacheStats {
954 self.platform.renderer_cache_stats()
955 }
956
957 pub fn refresh_windows(&mut self) {
960 self.pending_effects.push_back(Effect::RefreshWindows);
961 }
962
963 pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
964 self.start_update();
965 let result = update(self);
966 self.finish_update();
967 result
968 }
969
970 pub(crate) fn start_update(&mut self) {
971 self.pending_updates += 1;
972 }
973
974 pub(crate) fn finish_update(&mut self) {
975 if !self.flushing_effects && self.pending_updates == 1 {
976 self.flushing_effects = true;
977 self.flush_effects();
978 self.flushing_effects = false;
979 }
980 self.pending_updates -= 1;
981 }
982
983 pub fn observe<W>(
985 &mut self,
986 entity: &Entity<W>,
987 mut on_notify: impl FnMut(Entity<W>, &mut App) + 'static,
988 ) -> Subscription
989 where
990 W: 'static,
991 {
992 self.observe_internal(entity, move |e, cx| {
993 on_notify(e, cx);
994 true
995 })
996 }
997
998 pub(crate) fn detect_accessed_entities<R>(
999 &mut self,
1000 callback: impl FnOnce(&mut App) -> R,
1001 ) -> (R, FxHashSet<EntityId>) {
1002 let accessed_entities_start = self.entities.accessed_entities.borrow().clone();
1003 let result = callback(self);
1004 let accessed_entities_end = self.entities.accessed_entities.borrow().clone();
1005 let entities_accessed_in_callback = accessed_entities_end
1006 .difference(&accessed_entities_start)
1007 .copied()
1008 .collect::<FxHashSet<EntityId>>();
1009 (result, entities_accessed_in_callback)
1010 }
1011
1012 pub(crate) fn record_entities_accessed(
1013 &mut self,
1014 window_handle: AnyWindowHandle,
1015 invalidator: WindowInvalidator,
1016 entities: &FxHashSet<EntityId>,
1017 ) {
1018 let mut tracked_entities =
1019 std::mem::take(self.tracked_entities.entry(window_handle.id).or_default());
1020 for entity in tracked_entities.iter() {
1021 self.window_invalidators_by_entity
1022 .entry(*entity)
1023 .and_modify(|windows| {
1024 windows.remove(&window_handle.id);
1025 });
1026 }
1027 for entity in entities.iter() {
1028 self.window_invalidators_by_entity
1029 .entry(*entity)
1030 .or_default()
1031 .insert(window_handle.id, invalidator.clone());
1032 }
1033 tracked_entities.clear();
1034 tracked_entities.extend(entities.iter().copied());
1035 self.tracked_entities
1036 .insert(window_handle.id, tracked_entities);
1037 }
1038
1039 pub(crate) fn new_observer(&mut self, key: EntityId, value: Handler) -> Subscription {
1040 let (subscription, activate) = self.observers.insert(key, value);
1041 self.defer(move |_| activate());
1042 subscription
1043 }
1044
1045 pub(crate) fn observe_internal<W>(
1046 &mut self,
1047 entity: &Entity<W>,
1048 mut on_notify: impl FnMut(Entity<W>, &mut App) -> bool + 'static,
1049 ) -> Subscription
1050 where
1051 W: 'static,
1052 {
1053 let entity_id = entity.entity_id();
1054 let handle = entity.downgrade();
1055 self.new_observer(
1056 entity_id,
1057 Box::new(move |cx| {
1058 if let Some(entity) = handle.upgrade() {
1059 on_notify(entity, cx)
1060 } else {
1061 false
1062 }
1063 }),
1064 )
1065 }
1066
1067 pub fn subscribe<T, Event>(
1070 &mut self,
1071 entity: &Entity<T>,
1072 mut on_event: impl FnMut(Entity<T>, &Event, &mut App) + 'static,
1073 ) -> Subscription
1074 where
1075 T: 'static + EventEmitter<Event>,
1076 Event: 'static,
1077 {
1078 self.subscribe_internal(entity, move |entity, event, cx| {
1079 on_event(entity, event, cx);
1080 true
1081 })
1082 }
1083
1084 pub(crate) fn new_subscription(
1085 &mut self,
1086 key: EntityId,
1087 value: (TypeId, Listener),
1088 ) -> Subscription {
1089 let (subscription, activate) = self.event_listeners.insert(key, value);
1090 self.defer(move |_| activate());
1091 subscription
1092 }
1093 pub(crate) fn subscribe_internal<T, Evt>(
1094 &mut self,
1095 entity: &Entity<T>,
1096 mut on_event: impl FnMut(Entity<T>, &Evt, &mut App) -> bool + 'static,
1097 ) -> Subscription
1098 where
1099 T: 'static + EventEmitter<Evt>,
1100 Evt: 'static,
1101 {
1102 let entity_id = entity.entity_id();
1103 let handle = entity.downgrade();
1104 self.new_subscription(
1105 entity_id,
1106 (
1107 TypeId::of::<Evt>(),
1108 Box::new(move |event, cx| {
1109 let event: &Evt = event.downcast_ref().expect("invalid event type");
1110 if let Some(entity) = handle.upgrade() {
1111 on_event(entity, event, cx)
1112 } else {
1113 false
1114 }
1115 }),
1116 ),
1117 )
1118 }
1119
1120 pub fn windows(&self) -> Vec<AnyWindowHandle> {
1124 self.windows
1125 .keys()
1126 .flat_map(|window_id| self.window_handles.get(&window_id).copied())
1127 .collect()
1128 }
1129
1130 pub fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
1136 self.platform.window_stack()
1137 }
1138
1139 pub fn active_window(&self) -> Option<AnyWindowHandle> {
1141 self.platform.active_window()
1142 }
1143
1144 pub fn open_window<V: 'static + Render>(
1148 &mut self,
1149 options: crate::WindowOptions,
1150 build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity<V>,
1151 ) -> anyhow::Result<WindowHandle<V>> {
1152 self.update(|cx| {
1153 let id = cx.windows.insert(None);
1154 let handle = WindowHandle::new(id);
1155 match Window::new(handle.into(), options, cx) {
1156 Ok(mut window) => {
1157 cx.window_update_stack.push(id);
1158 let root_view = build_root_view(&mut window, cx);
1159 cx.window_update_stack.pop();
1160 window.root.replace(root_view.into());
1161 window.defer(cx, |window: &mut Window, cx| window.appearance_changed(cx));
1162
1163 let clear = window.draw(cx);
1168 clear.clear();
1169
1170 cx.window_handles.insert(id, window.handle);
1171 cx.windows.get_mut(id).unwrap().replace(window);
1172 Ok(handle)
1173 }
1174 Err(e) => {
1175 cx.windows.remove(id);
1176 Err(e)
1177 }
1178 }
1179 })
1180 }
1181
1182 pub fn activate(&self, ignoring_other_apps: bool) {
1184 self.platform.activate(ignoring_other_apps);
1185 }
1186
1187 pub fn hide(&self) {
1189 self.platform.hide();
1190 }
1191
1192 pub fn hide_other_apps(&self) {
1194 self.platform.hide_other_apps();
1195 }
1196
1197 pub fn unhide_other_apps(&self) {
1199 self.platform.unhide_other_apps();
1200 }
1201
1202 pub fn set_tray_icon(&self, icon: Option<&[u8]>) {
1204 self.platform.set_tray_icon(icon);
1205 }
1206
1207 pub fn set_tray_icon_rendering_mode(&self, rendering_mode: TrayIconRenderingMode) {
1209 self.platform.set_tray_icon_rendering_mode(rendering_mode);
1210 }
1211
1212 pub fn set_tray_menu(&self, menu: Vec<TrayMenuItem>) {
1214 self.platform.set_tray_menu(menu);
1215 }
1216
1217 pub fn set_tray_tooltip(&self, tooltip: &str) {
1219 self.platform.set_tray_tooltip(tooltip);
1220 }
1221
1222 pub fn set_tray_panel_mode(&self, enabled: bool) {
1225 self.platform.set_tray_panel_mode(enabled);
1226 }
1227
1228 #[allow(missing_docs)]
1229 pub fn tray_icon_anchor(&self) -> Option<TrayAnchor> {
1230 self.platform.get_tray_icon_anchor()
1231 }
1232
1233 pub fn tray_icon_bounds(&self) -> Option<Bounds<Pixels>> {
1235 self.platform.get_tray_icon_bounds()
1236 }
1237
1238 pub fn tray_anchor_for_position(&self, position: Point<Pixels>) -> Option<TrayAnchor> {
1246 const APPROXIMATE_TRAY_ICON_SIZE: Pixels = px(24.0);
1249
1250 let displays = self.displays();
1251 let display = displays
1252 .iter()
1253 .find(|display| display.bounds().contains(&position))
1254 .cloned()
1255 .or_else(|| self.primary_display())?;
1256 let display_bounds = display.bounds();
1257 let half_size = APPROXIMATE_TRAY_ICON_SIZE * 0.5;
1258 let local_position = position - display_bounds.origin;
1259
1260 Some(TrayAnchor {
1261 display_id: display.id(),
1262 bounds: Bounds::new(
1263 point(local_position.x - half_size, local_position.y - half_size),
1264 size(APPROXIMATE_TRAY_ICON_SIZE, APPROXIMATE_TRAY_ICON_SIZE),
1265 ),
1266 })
1267 }
1268
1269 pub fn on_tray_icon_event(&self, mut callback: impl FnMut(TrayIconEvent, &mut App) + 'static) {
1271 let this = self.this.clone();
1272 self.platform.on_tray_icon_event(Box::new(move |event| {
1273 if let Some(app) = this.upgrade() {
1274 callback(event, &mut app.borrow_mut());
1275 }
1276 }));
1277 }
1278
1279 pub fn on_tray_icon_click_event(
1281 &self,
1282 mut callback: impl FnMut(TrayIconClickEvent, &mut App) + 'static,
1283 ) {
1284 let this = self.this.clone();
1285 self.platform
1286 .on_tray_icon_click_event(Box::new(move |event| {
1287 if let Some(app) = this.upgrade() {
1288 callback(event, &mut app.borrow_mut());
1289 }
1290 }));
1291 }
1292
1293 pub fn on_tray_menu_action(&self, mut callback: impl FnMut(SharedString, &mut App) + 'static) {
1295 let this = self.this.clone();
1296 self.platform.on_tray_menu_action(Box::new(move |id| {
1297 if let Some(app) = this.upgrade() {
1298 callback(id, &mut app.borrow_mut());
1299 }
1300 }));
1301 }
1302
1303 pub fn register_global_hotkey(&self, id: u32, keystroke: &Keystroke) -> Result<()> {
1305 self.platform.register_global_hotkey(id, keystroke)
1306 }
1307
1308 pub fn unregister_global_hotkey(&self, id: u32) {
1310 self.platform.unregister_global_hotkey(id);
1311 }
1312
1313 pub fn on_global_hotkey(&self, callback: impl FnMut(u32) + 'static) {
1315 self.platform.on_global_hotkey(Box::new(callback));
1316 }
1317
1318 pub fn focused_window_info(&self) -> Option<FocusedWindowInfo> {
1320 self.platform.focused_window_info()
1321 }
1322
1323 pub fn accessibility_status(&self) -> PermissionStatus {
1325 self.platform.accessibility_status()
1326 }
1327
1328 pub fn request_accessibility_permission(&self) -> PermissionRequestStatus {
1330 self.platform.request_accessibility_permission()
1331 }
1332
1333 pub fn microphone_status(&self) -> PermissionStatus {
1335 self.platform.microphone_status()
1336 }
1337
1338 pub fn request_microphone_permission(
1340 &self,
1341 callback: impl FnOnce(bool) + 'static,
1342 ) -> PermissionRequestStatus {
1343 self.platform
1344 .request_microphone_permission(Box::new(callback))
1345 }
1346
1347 pub fn set_auto_launch(&self, app_id: &str, enabled: bool) -> Result<()> {
1349 self.platform.set_auto_launch(app_id, enabled)
1350 }
1351
1352 pub fn is_auto_launch_enabled(&self, app_id: &str) -> bool {
1354 self.platform.is_auto_launch_enabled(app_id)
1355 }
1356
1357 pub fn show_notification(&self, title: &str, body: &str) -> Result<()> {
1359 self.platform.show_notification(title, body)
1360 }
1361
1362 pub fn set_quit_mode(&mut self, mode: QuitMode) {
1365 self.quit_mode = mode;
1366 self.platform.set_quit_mode(mode);
1367 }
1368
1369 pub fn on_system_power_event(
1371 &self,
1372 mut callback: impl FnMut(SystemPowerEvent, &mut App) + 'static,
1373 ) {
1374 let this = self.this.clone();
1375 self.platform.on_system_power_event(Box::new(move |event| {
1376 if let Some(app) = this.upgrade() {
1377 callback(event, &mut app.borrow_mut());
1378 }
1379 }));
1380 }
1381
1382 pub fn start_power_save_blocker(&self, kind: PowerSaveBlockerKind) -> Option<u32> {
1384 self.platform.start_power_save_blocker(kind)
1385 }
1386
1387 pub fn stop_power_save_blocker(&self, id: u32) {
1389 self.platform.stop_power_save_blocker(id);
1390 }
1391
1392 pub fn system_idle_time(&self) -> Option<Duration> {
1394 self.platform.system_idle_time()
1395 }
1396
1397 pub fn network_status(&self) -> NetworkStatus {
1399 self.platform.network_status()
1400 }
1401
1402 pub fn on_network_status_change(
1404 &self,
1405 mut callback: impl FnMut(NetworkStatus, &mut App) + 'static,
1406 ) {
1407 let this = self.this.clone();
1408 self.platform
1409 .on_network_status_change(Box::new(move |status| {
1410 if let Some(app) = this.upgrade() {
1411 callback(status, &mut app.borrow_mut());
1412 }
1413 }));
1414 }
1415
1416 pub fn on_media_key_event(&self, mut callback: impl FnMut(MediaKeyEvent, &mut App) + 'static) {
1418 let this = self.this.clone();
1419 self.platform.on_media_key_event(Box::new(move |event| {
1420 if let Some(app) = this.upgrade() {
1421 callback(event, &mut app.borrow_mut());
1422 }
1423 }));
1424 }
1425
1426 pub fn request_user_attention(&self, attention_type: AttentionType) {
1428 self.platform.request_user_attention(attention_type);
1429 }
1430
1431 pub fn cancel_user_attention(&self) {
1433 self.platform.cancel_user_attention();
1434 }
1435
1436 pub fn set_dock_badge(&self, label: Option<&str>) {
1438 self.platform.set_dock_badge(label);
1439 }
1440
1441 pub fn show_context_menu(
1443 &self,
1444 position: Point<Pixels>,
1445 items: Vec<TrayMenuItem>,
1446 mut callback: impl FnMut(SharedString, &mut App) + 'static,
1447 ) {
1448 let this = self.this.clone();
1449 self.platform.show_context_menu(
1450 position,
1451 items,
1452 Box::new(move |id| {
1453 if let Some(app) = this.upgrade() {
1454 callback(id, &mut app.borrow_mut());
1455 }
1456 }),
1457 );
1458 }
1459
1460 pub fn show_dialog(&self, options: DialogOptions) -> oneshot::Receiver<usize> {
1462 self.platform.show_dialog(options)
1463 }
1464
1465 pub fn os_info(&self) -> OsInfo {
1467 self.platform.os_info()
1468 }
1469
1470 pub fn biometric_status(&self) -> BiometricStatus {
1472 self.platform.biometric_status()
1473 }
1474
1475 pub fn authenticate_biometric(
1477 &self,
1478 reason: &str,
1479 callback: impl FnOnce(bool) + Send + 'static,
1480 ) {
1481 self.platform
1482 .authenticate_biometric(reason, Box::new(callback));
1483 }
1484
1485 pub fn set_crash_handler(
1487 &self,
1488 app_version: Option<String>,
1489 handler: impl Fn(CrashReport) + Send + Sync + 'static,
1490 ) {
1491 let os_info = self.platform.os_info();
1492 let handler = std::sync::Arc::new(handler);
1493 std::panic::set_hook(Box::new(move |panic_info| {
1494 let message = if let Some(msg) = panic_info.payload().downcast_ref::<&str>() {
1495 msg.to_string()
1496 } else if let Some(msg) = panic_info.payload().downcast_ref::<String>() {
1497 msg.clone()
1498 } else {
1499 "Unknown panic".to_string()
1500 };
1501
1502 let location = panic_info
1503 .location()
1504 .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column()))
1505 .unwrap_or_default();
1506
1507 let full_message = if location.is_empty() {
1508 message
1509 } else {
1510 format!("{message} at {location}")
1511 };
1512
1513 let backtrace = std::backtrace::Backtrace::force_capture().to_string();
1514
1515 let report = CrashReport {
1516 message: full_message,
1517 backtrace,
1518 os_info: os_info.clone(),
1519 app_version: app_version.clone(),
1520 };
1521
1522 handler(report);
1523 }));
1524 }
1525
1526 pub fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
1528 self.platform.displays()
1529 }
1530
1531 pub fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
1533 self.platform.primary_display()
1534 }
1535
1536 pub fn compute_window_bounds(
1538 &self,
1539 size: Size<Pixels>,
1540 position: &WindowPosition,
1541 ) -> Bounds<Pixels> {
1542 let displays = self.platform.displays();
1543 let primary = self.platform.primary_display();
1544 crate::platform::window_positioner::compute_window_bounds(
1545 size,
1546 position,
1547 &displays,
1548 primary.as_ref(),
1549 )
1550 }
1551
1552 pub fn is_screen_capture_supported(&self) -> bool {
1554 self.platform.is_screen_capture_supported()
1555 }
1556
1557 pub fn screen_capture_sources(
1559 &self,
1560 ) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
1561 self.platform.screen_capture_sources()
1562 }
1563
1564 pub fn find_display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
1566 self.displays()
1567 .iter()
1568 .find(|display| display.id() == id)
1569 .cloned()
1570 }
1571
1572 pub fn window_appearance(&self) -> WindowAppearance {
1574 self.platform.window_appearance()
1575 }
1576
1577 pub fn set_window_appearance(&self, appearance: Option<WindowAppearance>) {
1589 self.platform.set_window_appearance(appearance);
1590 }
1591
1592 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1595 pub fn write_to_primary(&self, item: ClipboardItem) {
1596 self.platform.write_to_primary(item)
1597 }
1598
1599 pub fn write_to_clipboard(&self, item: ClipboardItem) {
1601 self.platform.write_to_clipboard(item)
1602 }
1603
1604 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1607 pub fn read_from_primary(&self) -> Option<ClipboardItem> {
1608 self.platform.read_from_primary()
1609 }
1610
1611 pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
1613 self.platform.read_from_clipboard()
1614 }
1615
1616 pub fn write_credentials(
1618 &self,
1619 url: &str,
1620 username: &str,
1621 password: &[u8],
1622 ) -> Task<Result<()>> {
1623 self.platform.write_credentials(url, username, password)
1624 }
1625
1626 pub fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
1628 self.platform.read_credentials(url)
1629 }
1630
1631 pub fn delete_credentials(&self, url: &str) -> Task<Result<()>> {
1633 self.platform.delete_credentials(url)
1634 }
1635
1636 pub fn open_url(&self, url: &str) {
1638 self.platform.open_url(url);
1639 }
1640
1641 pub fn register_url_scheme(&self, scheme: &str) -> Task<Result<()>> {
1648 self.platform.register_url_scheme(scheme)
1649 }
1650
1651 pub fn app_path(&self) -> Result<PathBuf> {
1655 self.platform.app_path()
1656 }
1657
1658 pub fn compositor_name(&self) -> &'static str {
1662 self.platform.compositor_name()
1663 }
1664
1665 pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
1667 self.platform.path_for_auxiliary_executable(name)
1668 }
1669
1670 pub fn prompt_for_paths(
1676 &self,
1677 options: PathPromptOptions,
1678 ) -> oneshot::Receiver<Result<Option<Vec<PathBuf>>>> {
1679 self.platform.prompt_for_paths(options)
1680 }
1681
1682 pub fn prompt_for_new_path(
1689 &self,
1690 directory: &Path,
1691 suggested_name: Option<&str>,
1692 ) -> oneshot::Receiver<Result<Option<PathBuf>>> {
1693 self.platform.prompt_for_new_path(directory, suggested_name)
1694 }
1695
1696 pub fn reveal_path(&self, path: &Path) {
1698 self.platform.reveal_path(path)
1699 }
1700
1701 pub fn open_with_system(&self, path: &Path) {
1703 self.platform.open_with_system(path)
1704 }
1705
1706 pub fn should_auto_hide_scrollbars(&self) -> bool {
1708 self.platform.should_auto_hide_scrollbars()
1709 }
1710
1711 pub fn restart(&mut self) {
1713 self.restart_observers
1714 .clone()
1715 .retain(&(), |observer| observer(self));
1716 self.platform.restart(
1717 self.restart_path.take(),
1718 mem::take(&mut self.restart_arguments),
1719 )
1720 }
1721
1722 pub fn set_restart_path(&mut self, path: PathBuf) {
1724 self.restart_path = Some(path);
1725 }
1726
1727 pub fn http_client(&self) -> Arc<dyn HttpClient> {
1729 self.http_client.clone()
1730 }
1731
1732 pub fn set_http_client(&mut self, new_client: Arc<dyn HttpClient>) {
1734 self.http_client = new_client;
1735 }
1736
1737 pub fn svg_renderer(&self) -> SvgRenderer {
1739 self.svg_renderer.clone()
1740 }
1741
1742 pub(crate) fn push_effect(&mut self, effect: Effect) {
1743 match &effect {
1744 Effect::Notify { emitter } => {
1745 if !self.pending_notifications.insert(*emitter) {
1746 return;
1747 }
1748 }
1749 Effect::NotifyGlobalObservers { global_type } => {
1750 if !self.pending_global_notifications.insert(*global_type) {
1751 return;
1752 }
1753 }
1754 _ => {}
1755 };
1756
1757 self.pending_effects.push_back(effect);
1758 }
1759
1760 fn flush_effects(&mut self) {
1764 loop {
1765 self.release_dropped_entities();
1766 self.release_dropped_focus_handles();
1767 if let Some(effect) = self.pending_effects.pop_front() {
1768 match effect {
1769 Effect::Notify { emitter } => {
1770 self.apply_notify_effect(emitter);
1771 }
1772
1773 Effect::Emit {
1774 emitter,
1775 event_type,
1776 event,
1777 } => self.apply_emit_effect(emitter, event_type, event),
1778
1779 Effect::RefreshWindows => {
1780 self.apply_refresh_effect();
1781 }
1782
1783 Effect::NotifyGlobalObservers { global_type } => {
1784 self.apply_notify_global_observers_effect(global_type);
1785 }
1786
1787 Effect::Defer { callback } => {
1788 self.apply_defer_effect(callback);
1789 }
1790 Effect::EntityCreated {
1791 entity,
1792 tid,
1793 window,
1794 } => {
1795 self.apply_entity_created_effect(entity, tid, window);
1796 }
1797 }
1798 } else {
1799 #[cfg(any(test, feature = "test-support"))]
1800 for window in self
1801 .windows
1802 .values()
1803 .filter_map(|window| {
1804 let window = window.as_ref()?;
1805 window.invalidator.is_dirty().then_some(window.handle)
1806 })
1807 .collect::<Vec<_>>()
1808 {
1809 self.update_window(window, |_, window, cx| window.draw(cx).clear())
1810 .unwrap();
1811 }
1812
1813 if self.pending_effects.is_empty() {
1814 for window in self.windows.values().filter_map(|window| window.as_ref()) {
1815 if window.invalidator.is_dirty()
1816 || window.needs_present.get()
1817 || !window.next_frame_callbacks.borrow().is_empty()
1818 {
1819 window.platform_window.schedule_frame();
1820 }
1821 }
1822
1823 break;
1824 }
1825 }
1826 }
1827 }
1828
1829 fn release_dropped_entities(&mut self) {
1833 loop {
1834 let dropped = self.entities.take_dropped();
1835 if dropped.is_empty() {
1836 break;
1837 }
1838
1839 for (entity_id, mut entity) in dropped {
1840 self.observers.remove(&entity_id);
1841 self.event_listeners.remove(&entity_id);
1842 for release_callback in self.release_listeners.remove(&entity_id) {
1843 release_callback(entity.as_mut(), self);
1844 }
1845 }
1846 }
1847 }
1848
1849 fn release_dropped_focus_handles(&mut self) {
1851 self.focus_handles
1852 .clone()
1853 .write()
1854 .retain(|handle_id, focus| {
1855 if focus.ref_count.load(SeqCst) == 0 {
1856 for window_handle in self.windows() {
1857 window_handle
1858 .update(self, |_, window, cx| {
1859 if window.focus == Some(handle_id) {
1860 window.blur(cx);
1861 }
1862 })
1863 .unwrap();
1864 }
1865 false
1866 } else {
1867 true
1868 }
1869 });
1870 }
1871
1872 fn apply_notify_effect(&mut self, emitter: EntityId) {
1873 self.pending_notifications.remove(&emitter);
1874
1875 self.observers
1876 .clone()
1877 .retain(&emitter, |handler| handler(self));
1878 }
1879
1880 fn apply_emit_effect(&mut self, emitter: EntityId, event_type: TypeId, event: Box<dyn Any>) {
1881 self.event_listeners
1882 .clone()
1883 .retain(&emitter, |(stored_type, handler)| {
1884 if *stored_type == event_type {
1885 handler(event.as_ref(), self)
1886 } else {
1887 true
1888 }
1889 });
1890 }
1891
1892 fn apply_refresh_effect(&mut self) {
1893 for window in self.windows.values_mut() {
1894 if let Some(window) = window.as_mut() {
1895 window.refreshing = true;
1896 window.invalidator.set_dirty(true);
1897 }
1898 }
1899 }
1900
1901 fn apply_notify_global_observers_effect(&mut self, type_id: TypeId) {
1902 self.pending_global_notifications.remove(&type_id);
1903 self.global_observers
1904 .clone()
1905 .retain(&type_id, |observer| observer(self));
1906 }
1907
1908 fn apply_defer_effect(&mut self, callback: Box<dyn FnOnce(&mut Self) + 'static>) {
1909 callback(self);
1910 }
1911
1912 fn apply_entity_created_effect(
1913 &mut self,
1914 entity: AnyEntity,
1915 tid: TypeId,
1916 window: Option<WindowId>,
1917 ) {
1918 self.new_entity_observers.clone().retain(&tid, |observer| {
1919 if let Some(id) = window {
1920 self.update_window_id(id, {
1921 let entity = entity.clone();
1922 |_, window, cx| (observer)(entity, &mut Some(window), cx)
1923 })
1924 .expect("All windows should be off the stack when flushing effects");
1925 } else {
1926 (observer)(entity.clone(), &mut None, self)
1927 }
1928 true
1929 });
1930 }
1931
1932 fn update_window_id<T, F>(&mut self, id: WindowId, update: F) -> Result<T>
1933 where
1934 F: FnOnce(AnyView, &mut Window, &mut App) -> T,
1935 {
1936 self.update(|cx| {
1937 let mut window = cx.windows.get_mut(id)?.take()?;
1938
1939 let root_view = window.root.clone().unwrap();
1940
1941 cx.window_update_stack.push(window.handle.id);
1942 let result = update(root_view, &mut window, cx);
1943 cx.window_update_stack.pop();
1944
1945 if window.removed {
1946 cx.window_handles.remove(&id);
1947 cx.windows.remove(id);
1948
1949 cx.window_closed_observers.clone().retain(&(), |callback| {
1950 callback(cx);
1951 true
1952 });
1953
1954 let quit_on_empty = match cx.quit_mode {
1955 QuitMode::Explicit => false,
1956 QuitMode::LastWindowClosed => true,
1957 QuitMode::Default => cfg!(not(target_os = "macos")),
1958 };
1959
1960 if quit_on_empty && cx.windows.is_empty() {
1961 cx.quit();
1962 }
1963 } else {
1964 cx.windows.get_mut(id)?.replace(window);
1965 }
1966
1967 Some(result)
1968 })
1969 .context("window not found")
1970 }
1971
1972 pub fn to_async(&self) -> AsyncApp {
1975 AsyncApp {
1976 app: self.this.clone(),
1977 background_executor: self.background_executor.clone(),
1978 foreground_executor: self.foreground_executor.clone(),
1979 }
1980 }
1981
1982 pub fn background_executor(&self) -> &BackgroundExecutor {
1984 &self.background_executor
1985 }
1986
1987 pub fn foreground_executor(&self) -> &ForegroundExecutor {
1989 if self.quitting {
1990 panic!("Can't spawn on main thread after on_app_quit")
1991 };
1992 &self.foreground_executor
1993 }
1994
1995 #[track_caller]
1998 pub fn spawn<AsyncFn, R>(&self, f: AsyncFn) -> Task<R>
1999 where
2000 AsyncFn: AsyncFnOnce(&mut AsyncApp) -> R + 'static,
2001 R: 'static,
2002 {
2003 if self.quitting {
2004 debug_panic!("Can't spawn on main thread after on_app_quit")
2005 };
2006
2007 let mut cx = self.to_async();
2008
2009 self.foreground_executor
2010 .spawn(async move { f(&mut cx).await })
2011 }
2012
2013 pub fn defer(&mut self, f: impl FnOnce(&mut App) + 'static) {
2016 self.push_effect(Effect::Defer {
2017 callback: Box::new(f),
2018 });
2019 }
2020
2021 pub fn asset_source(&self) -> &Arc<dyn AssetSource> {
2023 &self.asset_source
2024 }
2025
2026 pub fn text_system(&self) -> &Arc<TextSystem> {
2028 &self.text_system
2029 }
2030
2031 pub fn has_global<G: Global>(&self) -> bool {
2033 self.globals_by_type.contains_key(&TypeId::of::<G>())
2034 }
2035
2036 #[track_caller]
2038 pub fn global<G: Global>(&self) -> &G {
2039 self.globals_by_type
2040 .get(&TypeId::of::<G>())
2041 .map(|any_state| any_state.downcast_ref::<G>().unwrap())
2042 .with_context(|| format!("no state of type {} exists", type_name::<G>()))
2043 .unwrap()
2044 }
2045
2046 pub fn try_global<G: Global>(&self) -> Option<&G> {
2048 self.globals_by_type
2049 .get(&TypeId::of::<G>())
2050 .map(|any_state| any_state.downcast_ref::<G>().unwrap())
2051 }
2052
2053 #[track_caller]
2055 pub fn global_mut<G: Global>(&mut self) -> &mut G {
2056 let global_type = TypeId::of::<G>();
2057 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2058 self.globals_by_type
2059 .get_mut(&global_type)
2060 .and_then(|any_state| any_state.downcast_mut::<G>())
2061 .with_context(|| format!("no state of type {} exists", type_name::<G>()))
2062 .unwrap()
2063 }
2064
2065 pub fn default_global<G: Global + Default>(&mut self) -> &mut G {
2068 let global_type = TypeId::of::<G>();
2069 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2070 self.globals_by_type
2071 .entry(global_type)
2072 .or_insert_with(|| Box::<G>::default())
2073 .downcast_mut::<G>()
2074 .unwrap()
2075 }
2076
2077 pub fn set_global<G: Global>(&mut self, global: G) {
2079 let global_type = TypeId::of::<G>();
2080 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2081 self.globals_by_type.insert(global_type, Box::new(global));
2082 }
2083
2084 #[cfg(any(test, feature = "test-support"))]
2086 pub fn clear_globals(&mut self) {
2087 self.globals_by_type.drain();
2088 }
2089
2090 pub fn remove_global<G: Global>(&mut self) -> G {
2092 let global_type = TypeId::of::<G>();
2093 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2094 *self
2095 .globals_by_type
2096 .remove(&global_type)
2097 .unwrap_or_else(|| panic!("no global added for {}", std::any::type_name::<G>()))
2098 .downcast()
2099 .unwrap()
2100 }
2101
2102 pub fn observe_global<G: Global>(
2104 &mut self,
2105 mut f: impl FnMut(&mut Self) + 'static,
2106 ) -> Subscription {
2107 let (subscription, activate) = self.global_observers.insert(
2108 TypeId::of::<G>(),
2109 Box::new(move |cx| {
2110 f(cx);
2111 true
2112 }),
2113 );
2114 self.defer(move |_| activate());
2115 subscription
2116 }
2117
2118 #[track_caller]
2120 pub(crate) fn lease_global<G: Global>(&mut self) -> GlobalLease<G> {
2121 GlobalLease::new(
2122 self.globals_by_type
2123 .remove(&TypeId::of::<G>())
2124 .with_context(|| format!("no global registered of type {}", type_name::<G>()))
2125 .unwrap(),
2126 )
2127 }
2128
2129 pub(crate) fn end_global_lease<G: Global>(&mut self, lease: GlobalLease<G>) {
2131 let global_type = TypeId::of::<G>();
2132
2133 self.push_effect(Effect::NotifyGlobalObservers { global_type });
2134 self.globals_by_type.insert(global_type, lease.global);
2135 }
2136
2137 pub(crate) fn new_entity_observer(
2138 &self,
2139 key: TypeId,
2140 value: NewEntityListener,
2141 ) -> Subscription {
2142 let (subscription, activate) = self.new_entity_observers.insert(key, value);
2143 activate();
2144 subscription
2145 }
2146
2147 pub fn observe_new<T: 'static>(
2150 &self,
2151 on_new: impl 'static + Fn(&mut T, Option<&mut Window>, &mut Context<T>),
2152 ) -> Subscription {
2153 self.new_entity_observer(
2154 TypeId::of::<T>(),
2155 Box::new(
2156 move |any_entity: AnyEntity, window: &mut Option<&mut Window>, cx: &mut App| {
2157 any_entity
2158 .downcast::<T>()
2159 .unwrap()
2160 .update(cx, |entity_state, cx| {
2161 on_new(entity_state, window.as_deref_mut(), cx)
2162 })
2163 },
2164 ),
2165 )
2166 }
2167
2168 pub fn observe_release<T>(
2171 &self,
2172 handle: &Entity<T>,
2173 on_release: impl FnOnce(&mut T, &mut App) + 'static,
2174 ) -> Subscription
2175 where
2176 T: 'static,
2177 {
2178 let (subscription, activate) = self.release_listeners.insert(
2179 handle.entity_id(),
2180 Box::new(move |entity, cx| {
2181 let entity = entity.downcast_mut().expect("invalid entity type");
2182 on_release(entity, cx)
2183 }),
2184 );
2185 activate();
2186 subscription
2187 }
2188
2189 pub fn observe_release_in<T>(
2192 &self,
2193 handle: &Entity<T>,
2194 window: &Window,
2195 on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static,
2196 ) -> Subscription
2197 where
2198 T: 'static,
2199 {
2200 let window_handle = window.handle;
2201 self.observe_release(handle, move |entity, cx| {
2202 let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx));
2203 })
2204 }
2205
2206 pub fn observe_keystrokes(
2211 &mut self,
2212 mut f: impl FnMut(&KeystrokeEvent, &mut Window, &mut App) + 'static,
2213 ) -> Subscription {
2214 fn inner(
2215 keystroke_observers: &SubscriberSet<(), KeystrokeObserver>,
2216 handler: KeystrokeObserver,
2217 ) -> Subscription {
2218 let (subscription, activate) = keystroke_observers.insert((), handler);
2219 activate();
2220 subscription
2221 }
2222
2223 inner(
2224 &self.keystroke_observers,
2225 Box::new(move |event, window, cx| {
2226 f(event, window, cx);
2227 true
2228 }),
2229 )
2230 }
2231
2232 pub fn on_missing_glyphs(&self, mut callback: impl FnMut(&[MissingGlyph], &mut App) + 'static) {
2243 let Some(mut receiver) = self.text_system.take_missing_glyph_receiver() else {
2244 log::warn!("App::on_missing_glyphs called more than once; ignoring registration");
2245 return;
2246 };
2247 self.spawn(async move |cx| {
2248 while let Ok(missing_glyphs) = receiver.recv().await {
2249 if cx.update(|cx| callback(&missing_glyphs, cx)).is_err() {
2250 break;
2252 }
2253 }
2254 })
2255 .detach();
2256 }
2257
2258 pub fn intercept_keystrokes(
2263 &mut self,
2264 mut f: impl FnMut(&KeystrokeEvent, &mut Window, &mut App) + 'static,
2265 ) -> Subscription {
2266 fn inner(
2267 keystroke_interceptors: &SubscriberSet<(), KeystrokeObserver>,
2268 handler: KeystrokeObserver,
2269 ) -> Subscription {
2270 let (subscription, activate) = keystroke_interceptors.insert((), handler);
2271 activate();
2272 subscription
2273 }
2274
2275 inner(
2276 &self.keystroke_interceptors,
2277 Box::new(move |event, window, cx| {
2278 f(event, window, cx);
2279 true
2280 }),
2281 )
2282 }
2283
2284 pub fn bind_keys(&mut self, bindings: impl IntoIterator<Item = KeyBinding>) {
2286 self.keymap.borrow_mut().add_bindings(bindings);
2287 self.pending_effects.push_back(Effect::RefreshWindows);
2288 }
2289
2290 pub fn clear_key_bindings(&mut self) {
2292 self.keymap.borrow_mut().clear();
2293 self.pending_effects.push_back(Effect::RefreshWindows);
2294 }
2295
2296 pub fn key_bindings(&self) -> Rc<RefCell<Keymap>> {
2298 self.keymap.clone()
2299 }
2300
2301 pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Self) + 'static) {
2305 self.global_action_listeners
2306 .entry(TypeId::of::<A>())
2307 .or_default()
2308 .push(Rc::new(move |action, phase, cx| {
2309 if phase == DispatchPhase::Bubble {
2310 let action = action.downcast_ref().unwrap();
2311 listener(action, cx)
2312 }
2313 }));
2314 }
2315
2316 pub fn stop_propagation(&mut self) {
2321 self.propagate_event = false;
2322 }
2323
2324 pub fn propagate(&mut self) {
2329 self.propagate_event = true;
2330 }
2331
2332 pub fn build_action(
2334 &self,
2335 name: &str,
2336 data: Option<serde_json::Value>,
2337 ) -> std::result::Result<Box<dyn Action>, ActionBuildError> {
2338 self.actions.build_action(name, data)
2339 }
2340
2341 pub fn all_action_names(&self) -> &[&'static str] {
2344 self.actions.all_action_names()
2345 }
2346
2347 pub fn all_bindings_for_input(&self, input: &[Keystroke]) -> Vec<KeyBinding> {
2351 RefCell::borrow(&self.keymap).all_bindings_for_input(input)
2352 }
2353
2354 pub fn action_schemas(
2356 &self,
2357 generator: &mut schemars::SchemaGenerator,
2358 ) -> Vec<(&'static str, Option<schemars::Schema>)> {
2359 self.actions.action_schemas(generator)
2360 }
2361
2362 pub fn deprecated_actions_to_preferred_actions(&self) -> &HashMap<&'static str, &'static str> {
2364 self.actions.deprecated_aliases()
2365 }
2366
2367 pub fn action_deprecation_messages(&self) -> &HashMap<&'static str, &'static str> {
2369 self.actions.deprecation_messages()
2370 }
2371
2372 pub fn action_documentation(&self) -> &HashMap<&'static str, &'static str> {
2374 self.actions.documentation()
2375 }
2376
2377 pub fn on_app_quit<Fut>(
2380 &self,
2381 mut on_quit: impl FnMut(&mut App) -> Fut + 'static,
2382 ) -> Subscription
2383 where
2384 Fut: 'static + Future<Output = ()>,
2385 {
2386 let (subscription, activate) = self.quit_observers.insert(
2387 (),
2388 Box::new(move |cx| {
2389 let future = on_quit(cx);
2390 future.boxed_local()
2391 }),
2392 );
2393 activate();
2394 subscription
2395 }
2396
2397 pub fn on_app_restart(&self, mut on_restart: impl 'static + FnMut(&mut App)) -> Subscription {
2401 let (subscription, activate) = self.restart_observers.insert(
2402 (),
2403 Box::new(move |cx| {
2404 on_restart(cx);
2405 true
2406 }),
2407 );
2408 activate();
2409 subscription
2410 }
2411
2412 pub fn on_window_closed(&self, mut on_closed: impl FnMut(&mut App) + 'static) -> Subscription {
2415 let (subscription, activate) = self.window_closed_observers.insert((), Box::new(on_closed));
2416 activate();
2417 subscription
2418 }
2419
2420 pub(crate) fn clear_pending_keystrokes(&mut self) {
2421 for window in self.windows() {
2422 window
2423 .update(self, |_, window, cx| {
2424 window.clear_pending_keystrokes(cx);
2425 })
2426 .ok();
2427 }
2428 }
2429
2430 pub fn is_action_available(&mut self, action: &dyn Action) -> bool {
2433 let mut action_available = false;
2434 if let Some(window) = self.active_window()
2435 && let Ok(window_action_available) =
2436 window.update(self, |_, window, cx| window.is_action_available(action, cx))
2437 {
2438 action_available = window_action_available;
2439 }
2440
2441 action_available
2442 || self
2443 .global_action_listeners
2444 .contains_key(&action.as_any().type_id())
2445 }
2446
2447 pub fn set_menus(&self, menus: Vec<Menu>) {
2449 self.platform.set_menus(menus, &self.keymap.borrow());
2450 }
2451
2452 pub fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
2454 self.platform.get_menus()
2455 }
2456
2457 pub fn set_dock_menu(&self, menus: Vec<MenuItem>) {
2459 self.platform.set_dock_menu(menus, &self.keymap.borrow())
2460 }
2461
2462 pub fn perform_dock_menu_action(&self, action: usize) {
2464 self.platform.perform_dock_menu_action(action);
2465 }
2466
2467 pub fn add_recent_document(&self, path: &Path) {
2472 self.platform.add_recent_document(path);
2473 }
2474
2475 pub fn update_jump_list(
2478 &self,
2479 menus: Vec<MenuItem>,
2480 entries: Vec<SmallVec<[PathBuf; 2]>>,
2481 ) -> Vec<SmallVec<[PathBuf; 2]>> {
2482 self.platform.update_jump_list(menus, entries)
2483 }
2484
2485 pub fn dispatch_action(&mut self, action: &dyn Action) {
2488 if let Some(active_window) = self.active_window() {
2489 active_window
2490 .update(self, |_, window, cx| {
2491 window.dispatch_action(action.boxed_clone(), cx)
2492 })
2493 .log_err();
2494 } else {
2495 self.dispatch_global_action(action);
2496 }
2497 }
2498
2499 fn dispatch_global_action(&mut self, action: &dyn Action) {
2500 self.propagate_event = true;
2501
2502 if let Some(mut global_listeners) = self
2503 .global_action_listeners
2504 .remove(&action.as_any().type_id())
2505 {
2506 for listener in &global_listeners {
2507 listener(action.as_any(), DispatchPhase::Capture, self);
2508 if !self.propagate_event {
2509 break;
2510 }
2511 }
2512
2513 global_listeners.extend(
2514 self.global_action_listeners
2515 .remove(&action.as_any().type_id())
2516 .unwrap_or_default(),
2517 );
2518
2519 self.global_action_listeners
2520 .insert(action.as_any().type_id(), global_listeners);
2521 }
2522
2523 if self.propagate_event
2524 && let Some(mut global_listeners) = self
2525 .global_action_listeners
2526 .remove(&action.as_any().type_id())
2527 {
2528 for listener in global_listeners.iter().rev() {
2529 listener(action.as_any(), DispatchPhase::Bubble, self);
2530 if !self.propagate_event {
2531 break;
2532 }
2533 }
2534
2535 global_listeners.extend(
2536 self.global_action_listeners
2537 .remove(&action.as_any().type_id())
2538 .unwrap_or_default(),
2539 );
2540
2541 self.global_action_listeners
2542 .insert(action.as_any().type_id(), global_listeners);
2543 }
2544 }
2545
2546 pub fn has_active_drag(&self) -> bool {
2548 self.active_drag.is_some()
2549 }
2550
2551 pub fn active_drag_cursor_style(&self) -> Option<CursorStyle> {
2553 self.active_drag.as_ref().and_then(|drag| drag.cursor_style)
2554 }
2555
2556 pub fn stop_active_drag(&mut self, window: &mut Window) -> bool {
2558 if self.active_drag.is_some() {
2559 self.active_drag = None;
2560 window.refresh();
2561 true
2562 } else {
2563 false
2564 }
2565 }
2566
2567 pub fn set_active_drag_cursor_style(
2569 &mut self,
2570 cursor_style: CursorStyle,
2571 window: &mut Window,
2572 ) -> bool {
2573 if let Some(ref mut drag) = self.active_drag {
2574 drag.cursor_style = Some(cursor_style);
2575 window.refresh();
2576 true
2577 } else {
2578 false
2579 }
2580 }
2581
2582 pub fn set_prompt_builder(
2585 &mut self,
2586 renderer: impl Fn(
2587 PromptLevel,
2588 &str,
2589 Option<&str>,
2590 &[PromptButton],
2591 PromptHandle,
2592 &mut Window,
2593 &mut App,
2594 ) -> RenderablePromptHandle
2595 + 'static,
2596 ) {
2597 self.prompt_builder = Some(PromptBuilder::Custom(Box::new(renderer)));
2598 }
2599
2600 pub fn reset_prompt_builder(&mut self) {
2602 self.prompt_builder = Some(PromptBuilder::Default);
2603 }
2604
2605 pub fn remove_asset<A: Asset>(&mut self, source: &A::Source) {
2607 let asset_id = (TypeId::of::<A>(), hash(source));
2608 self.loading_assets.remove(&asset_id);
2609 }
2610
2611 #[cfg(any(test, feature = "test-support"))]
2614 pub fn has_asset<A: Asset>(&self, source: &A::Source) -> bool {
2615 let asset_id = (TypeId::of::<A>(), hash(source));
2616 self.loading_assets.contains_key(&asset_id)
2617 }
2618
2619 pub fn fetch_asset<A: Asset>(&mut self, source: &A::Source) -> Option<A::Output> {
2624 self.asset_entry::<A>(source).get()
2625 }
2626
2627 pub(crate) fn asset_entry<A: Asset>(&mut self, source: &A::Source) -> &CachedLoad<A::Output> {
2628 let asset_id = (TypeId::of::<A>(), hash(source));
2629 if !self.loading_assets.contains_key(&asset_id) {
2630 let future = A::load(source.clone(), self);
2631 let entry = CachedLoad::new(future, self);
2632 self.loading_assets.insert(asset_id, Box::new(entry));
2633 }
2634 self.loading_assets
2635 .get(&asset_id)
2636 .and_then(|entry| entry.downcast_ref())
2637 .expect("asset cache entries are keyed by their asset type")
2638 }
2639
2640 #[track_caller]
2643 pub fn focus_handle(&self) -> FocusHandle {
2644 FocusHandle::new(&self.focus_handles)
2645 }
2646
2647 pub fn notify(&mut self, entity_id: EntityId) {
2649 let window_invalidators = mem::take(
2650 self.window_invalidators_by_entity
2651 .entry(entity_id)
2652 .or_default(),
2653 );
2654
2655 if window_invalidators.is_empty() {
2656 if self.pending_notifications.insert(entity_id) {
2657 self.pending_effects
2658 .push_back(Effect::Notify { emitter: entity_id });
2659 }
2660 } else {
2661 for invalidator in window_invalidators.values() {
2662 invalidator.invalidate_view(entity_id, self);
2663 }
2664 }
2665
2666 self.window_invalidators_by_entity
2667 .insert(entity_id, window_invalidators);
2668 }
2669
2670 #[cfg(any(test, feature = "test-support", debug_assertions))]
2672 pub fn get_name(&self) -> Option<&'static str> {
2673 self.name
2674 }
2675
2676 pub fn can_select_mixed_files_and_dirs(&self) -> bool {
2678 self.platform.can_select_mixed_files_and_dirs()
2679 }
2680
2681 pub fn drop_image(&mut self, image: Arc<RenderImage>, current_window: Option<&mut Window>) {
2686 for window in self.windows.values_mut().flatten() {
2688 _ = window.drop_image(image.clone());
2689 }
2690
2691 if let Some(window) = current_window {
2693 _ = window.drop_image(image);
2694 }
2695 }
2696
2697 #[cfg(any(feature = "inspector", debug_assertions))]
2699 pub fn set_inspector_renderer(&mut self, f: crate::InspectorRenderer) {
2700 self.inspector_renderer = Some(f);
2701 }
2702
2703 #[cfg(any(feature = "inspector", debug_assertions))]
2705 pub fn register_inspector_element<T: 'static, R: crate::IntoElement>(
2706 &mut self,
2707 f: impl 'static + Fn(crate::InspectorElementId, &T, &mut Window, &mut App) -> R,
2708 ) {
2709 self.inspector_element_registry.register(f);
2710 }
2711
2712 pub fn init_colors(&mut self) {
2716 self.set_global(GlobalColors(Arc::new(Colors::default())));
2717 }
2718}
2719
2720impl AppContext for App {
2721 type Result<T> = T;
2722
2723 fn new<T: 'static>(&mut self, build_entity: impl FnOnce(&mut Context<T>) -> T) -> Entity<T> {
2728 self.update(|cx| {
2729 let slot = cx.entities.reserve();
2730 let handle = slot.clone();
2731 let entity = build_entity(&mut Context::new_context(cx, slot.downgrade()));
2732
2733 cx.push_effect(Effect::EntityCreated {
2734 entity: handle.clone().into_any(),
2735 tid: TypeId::of::<T>(),
2736 window: cx.window_update_stack.last().cloned(),
2737 });
2738
2739 cx.entities.insert(slot, entity);
2740 handle
2741 })
2742 }
2743
2744 fn reserve_entity<T: 'static>(&mut self) -> Self::Result<Reservation<T>> {
2745 Reservation(self.entities.reserve())
2746 }
2747
2748 fn insert_entity<T: 'static>(
2749 &mut self,
2750 reservation: Reservation<T>,
2751 build_entity: impl FnOnce(&mut Context<T>) -> T,
2752 ) -> Self::Result<Entity<T>> {
2753 self.update(|cx| {
2754 let slot = reservation.0;
2755 let entity = build_entity(&mut Context::new_context(cx, slot.downgrade()));
2756 cx.entities.insert(slot, entity)
2757 })
2758 }
2759
2760 fn update_entity<T: 'static, R>(
2763 &mut self,
2764 handle: &Entity<T>,
2765 update: impl FnOnce(&mut T, &mut Context<T>) -> R,
2766 ) -> R {
2767 self.update(|cx| {
2768 let mut entity = cx.entities.lease(handle);
2769 let result = update(
2770 &mut entity,
2771 &mut Context::new_context(cx, handle.downgrade()),
2772 );
2773 cx.entities.end_lease(entity);
2774 result
2775 })
2776 }
2777
2778 fn as_mut<'a, T>(&'a mut self, handle: &Entity<T>) -> GpuiBorrow<'a, T>
2779 where
2780 T: 'static,
2781 {
2782 GpuiBorrow::new(handle.clone(), self)
2783 }
2784
2785 fn read_entity<T, R>(
2786 &self,
2787 handle: &Entity<T>,
2788 read: impl FnOnce(&T, &App) -> R,
2789 ) -> Self::Result<R>
2790 where
2791 T: 'static,
2792 {
2793 let entity = self.entities.read(handle);
2794 read(entity, self)
2795 }
2796
2797 fn update_window<T, F>(&mut self, handle: AnyWindowHandle, update: F) -> Result<T>
2798 where
2799 F: FnOnce(AnyView, &mut Window, &mut App) -> T,
2800 {
2801 self.update_window_id(handle.id, update)
2802 }
2803
2804 fn read_window<T, R>(
2805 &self,
2806 window: &WindowHandle<T>,
2807 read: impl FnOnce(Entity<T>, &App) -> R,
2808 ) -> Result<R>
2809 where
2810 T: 'static,
2811 {
2812 let window = self
2813 .windows
2814 .get(window.id)
2815 .context("window not found")?
2816 .as_ref()
2817 .expect("attempted to read a window that is already on the stack");
2818
2819 let root_view = window.root.clone().unwrap();
2820 let view = root_view
2821 .downcast::<T>()
2822 .map_err(|_| anyhow!("root view's type has changed"))?;
2823
2824 Ok(read(view, self))
2825 }
2826
2827 fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
2828 where
2829 R: Send + 'static,
2830 {
2831 self.background_executor.spawn(future)
2832 }
2833
2834 fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
2835 where
2836 G: Global,
2837 {
2838 let mut g = self.global::<G>();
2839 callback(g, self)
2840 }
2841}
2842
2843pub(crate) enum Effect {
2845 Notify {
2846 emitter: EntityId,
2847 },
2848 Emit {
2849 emitter: EntityId,
2850 event_type: TypeId,
2851 event: Box<dyn Any>,
2852 },
2853 RefreshWindows,
2854 NotifyGlobalObservers {
2855 global_type: TypeId,
2856 },
2857 Defer {
2858 callback: Box<dyn FnOnce(&mut App) + 'static>,
2859 },
2860 EntityCreated {
2861 entity: AnyEntity,
2862 tid: TypeId,
2863 window: Option<WindowId>,
2864 },
2865}
2866
2867impl std::fmt::Debug for Effect {
2868 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2869 match self {
2870 Effect::Notify { emitter } => write!(f, "Notify({})", emitter),
2871 Effect::Emit { emitter, .. } => write!(f, "Emit({:?})", emitter),
2872 Effect::RefreshWindows => write!(f, "RefreshWindows"),
2873 Effect::NotifyGlobalObservers { global_type } => {
2874 write!(f, "NotifyGlobalObservers({:?})", global_type)
2875 }
2876 Effect::Defer { .. } => write!(f, "Defer(..)"),
2877 Effect::EntityCreated { entity, .. } => write!(f, "EntityCreated({:?})", entity),
2878 }
2879 }
2880}
2881
2882pub(crate) struct GlobalLease<G: Global> {
2884 global: Box<dyn Any>,
2885 global_type: PhantomData<G>,
2886}
2887
2888impl<G: Global> GlobalLease<G> {
2889 fn new(global: Box<dyn Any>) -> Self {
2890 GlobalLease {
2891 global,
2892 global_type: PhantomData,
2893 }
2894 }
2895}
2896
2897impl<G: Global> Deref for GlobalLease<G> {
2898 type Target = G;
2899
2900 fn deref(&self) -> &Self::Target {
2901 self.global.downcast_ref().unwrap()
2902 }
2903}
2904
2905impl<G: Global> DerefMut for GlobalLease<G> {
2906 fn deref_mut(&mut self) -> &mut Self::Target {
2907 self.global.downcast_mut().unwrap()
2908 }
2909}
2910
2911pub struct AnyDrag {
2914 pub view: AnyView,
2916
2917 pub value: Arc<dyn Any>,
2919
2920 pub cursor_offset: Point<Pixels>,
2923
2924 pub cursor_style: Option<CursorStyle>,
2926}
2927
2928#[derive(Clone)]
2931pub struct AnyTooltip {
2932 pub view: AnyView,
2934
2935 pub mouse_position: Point<Pixels>,
2937
2938 pub check_visible_and_update: Rc<dyn Fn(Bounds<Pixels>, &mut Window, &mut App) -> bool>,
2942}
2943
2944#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2946pub enum InputPreference {
2947 CharacterInput,
2949 KeyBindings,
2951}
2952
2953#[derive(Debug)]
2955pub struct KeystrokeEvent {
2956 pub keystroke: Keystroke,
2958
2959 pub input_preference: InputPreference,
2967
2968 pub action: Option<Box<dyn Action>>,
2970
2971 pub context_stack: Vec<KeyContext>,
2973}
2974
2975struct NullHttpClient;
2976
2977impl HttpClient for NullHttpClient {
2978 fn send(
2979 &self,
2980 _req: http_client::Request<http_client::AsyncBody>,
2981 ) -> futures::future::BoxFuture<
2982 'static,
2983 anyhow::Result<http_client::Response<http_client::AsyncBody>>,
2984 > {
2985 async move {
2986 anyhow::bail!("No HttpClient available");
2987 }
2988 .boxed()
2989 }
2990
2991 fn user_agent(&self) -> Option<&http_client::http::HeaderValue> {
2992 None
2993 }
2994
2995 fn proxy(&self) -> Option<&Url> {
2996 None
2997 }
2998
2999 fn type_name(&self) -> &'static str {
3000 type_name::<Self>()
3001 }
3002}
3003
3004pub struct GpuiBorrow<'a, T> {
3006 inner: Option<Lease<T>>,
3007 app: &'a mut App,
3008}
3009
3010impl<'a, T: 'static> GpuiBorrow<'a, T> {
3011 fn new(inner: Entity<T>, app: &'a mut App) -> Self {
3012 app.start_update();
3013 let lease = app.entities.lease(&inner);
3014 Self {
3015 inner: Some(lease),
3016 app,
3017 }
3018 }
3019}
3020
3021impl<'a, T: 'static> std::borrow::Borrow<T> for GpuiBorrow<'a, T> {
3022 fn borrow(&self) -> &T {
3023 self.inner.as_ref().unwrap().borrow()
3024 }
3025}
3026
3027impl<'a, T: 'static> std::borrow::BorrowMut<T> for GpuiBorrow<'a, T> {
3028 fn borrow_mut(&mut self) -> &mut T {
3029 self.inner.as_mut().unwrap().borrow_mut()
3030 }
3031}
3032
3033impl<'a, T: 'static> std::ops::Deref for GpuiBorrow<'a, T> {
3034 type Target = T;
3035
3036 fn deref(&self) -> &Self::Target {
3037 self.inner.as_ref().unwrap()
3038 }
3039}
3040
3041impl<'a, T: 'static> std::ops::DerefMut for GpuiBorrow<'a, T> {
3042 fn deref_mut(&mut self) -> &mut T {
3043 self.inner.as_mut().unwrap()
3044 }
3045}
3046
3047impl<'a, T> Drop for GpuiBorrow<'a, T> {
3048 fn drop(&mut self) {
3049 let lease = self.inner.take().unwrap();
3050 self.app.notify(lease.id);
3051 self.app.entities.end_lease(lease);
3052 self.app.finish_update();
3053 }
3054}
3055
3056#[cfg(test)]
3057mod test {
3058 use std::{
3059 cell::{Cell, RefCell},
3060 ffi::OsString,
3061 path::PathBuf,
3062 rc::Rc,
3063 sync::Arc,
3064 };
3065
3066 #[cfg(unix)]
3067 use std::os::unix::ffi::OsStringExt;
3068
3069 use rand::{SeedableRng, rngs::StdRng};
3070
3071 use super::{Application, ApplicationHandle, NullHttpClient};
3072 use crate::{
3073 AppContext, AppResourceProfile, BackgroundExecutor, Context, Empty, ForegroundExecutor,
3074 IntoElement, Platform, QuitMode, Render, TestAppContext, TestDispatcher, TestPlatform,
3075 TrayIconClickEvent, TrayIconEvent, TrayIconRenderingMode, Window, WindowAppearance, point,
3076 px,
3077 };
3078
3079 struct RenderCounter(Rc<Cell<usize>>);
3080
3081 impl Render for RenderCounter {
3082 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
3083 self.0.set(self.0.get() + 1);
3084 Empty
3085 }
3086 }
3087
3088 #[crate::test]
3089 fn async_app_refresh_flushes_refresh_effect(cx: &mut TestAppContext) {
3090 let render_count = Rc::new(Cell::new(0));
3091
3092 let _window = cx.add_window({
3093 let render_count = render_count.clone();
3094 move |_, _| RenderCounter(render_count)
3095 });
3096
3097 cx.run_until_parked();
3098 let render_count_before_refresh = render_count.get();
3099
3100 cx.to_async().refresh().unwrap();
3101
3102 assert_eq!(render_count.get(), render_count_before_refresh + 1);
3103 }
3104
3105 #[test]
3106 fn test_with_platform_uses_injected_platform() {
3107 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3108 let platform: Rc<dyn Platform> = TestPlatform::new(
3109 BackgroundExecutor::new(dispatcher.clone()),
3110 ForegroundExecutor::new(dispatcher),
3111 );
3112
3113 let application = Application::with_platform(platform.clone());
3114
3115 assert!(Rc::ptr_eq(&application.0.borrow().platform, &platform));
3116 }
3117
3118 #[test]
3119 fn test_with_quit_mode_updates_core_and_platform_state() {
3120 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3121 let platform = TestPlatform::new(
3122 BackgroundExecutor::new(dispatcher.clone()),
3123 ForegroundExecutor::new(dispatcher),
3124 );
3125
3126 let application =
3127 Application::with_platform(platform.clone()).with_quit_mode(QuitMode::Explicit);
3128
3129 assert_eq!(application.0.borrow().quit_mode, QuitMode::Explicit);
3130 assert_eq!(*platform.last_quit_mode.lock(), Some(QuitMode::Explicit));
3131 }
3132
3133 #[test]
3134 fn test_inaccessible_sets_force_disabled() {
3135 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3136 let platform = TestPlatform::new(
3137 BackgroundExecutor::new(dispatcher.clone()),
3138 ForegroundExecutor::new(dispatcher),
3139 );
3140 let application = Application(super::App::new_app(
3141 platform,
3142 Arc::new(()),
3143 Arc::new(NullHttpClient),
3144 AppResourceProfile::default(),
3145 ))
3146 .inaccessible();
3147
3148 assert!(application.0.borrow().accessibility_force_disabled);
3149 }
3150
3151 #[test]
3152 fn test_with_resource_profile_rebuilds_text_system_budget() {
3153 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3154 let platform = TestPlatform::new(
3155 BackgroundExecutor::new(dispatcher.clone()),
3156 ForegroundExecutor::new(dispatcher),
3157 );
3158 let mut profile = AppResourceProfile::default();
3159 profile.text.line_layout_cache_max_entries = 7;
3160 profile.text.line_layout_cache_low_watermark = 3;
3161 profile.text.raster_bounds_cache_max_entries = Some(11);
3162 profile.element_arena_size =
3163 crate::window::ELEMENT_ARENA_SIZE.load(std::sync::atomic::Ordering::Relaxed);
3164
3165 let application = Application(super::App::new_app(
3166 platform,
3167 Arc::new(()),
3168 Arc::new(NullHttpClient),
3169 AppResourceProfile::default(),
3170 ))
3171 .with_resource_profile(profile);
3172
3173 let app = application.0.borrow();
3174 assert_eq!(app.text_system.resource_budget_for_test(), (7, 3, Some(11)));
3175 assert_eq!(app.resource_profile.text.line_layout_cache_max_entries, 7);
3176 assert_eq!(
3177 app.resource_profile.gpu.instance_buffer_initial_size,
3178 2 * 1024 * 1024
3179 );
3180 }
3181
3182 #[test]
3183 fn test_with_resource_profile_updates_gpu_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 platform_handle = platform.clone();
3190 let mut profile = AppResourceProfile::default();
3191 profile.gpu.instance_buffer_initial_size = 768 * 1024;
3192 profile.element_arena_size =
3193 crate::window::ELEMENT_ARENA_SIZE.load(std::sync::atomic::Ordering::Relaxed);
3194
3195 let application = Application(super::App::new_app(
3196 platform,
3197 Arc::new(()),
3198 Arc::new(NullHttpClient),
3199 AppResourceProfile::default(),
3200 ))
3201 .with_resource_profile(profile);
3202
3203 let app = application.0.borrow();
3204 assert_eq!(
3205 app.resource_profile.gpu.instance_buffer_initial_size,
3206 768 * 1024
3207 );
3208 assert_eq!(
3209 platform_handle
3210 .gpu_resource_budget
3211 .lock()
3212 .instance_buffer_initial_size,
3213 768 * 1024
3214 );
3215 }
3216
3217 #[test]
3218 fn test_application_handle_keeps_app_alive_for_embedders() {
3219 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3220 let platform = TestPlatform::new(
3221 BackgroundExecutor::new(dispatcher.clone()),
3222 ForegroundExecutor::new(dispatcher),
3223 );
3224 let application = Application(super::App::new_app(
3225 platform,
3226 Arc::new(()),
3227 Arc::new(NullHttpClient),
3228 AppResourceProfile::default(),
3229 ));
3230 let weak_app = Rc::downgrade(&application.0);
3231 let handle = ApplicationHandle {
3232 app: application.0.clone(),
3233 };
3234 drop(application);
3235
3236 assert!(weak_app.upgrade().is_some());
3237
3238 let did_update = Rc::new(RefCell::new(false));
3239 let did_update_clone = did_update.clone();
3240 handle.update(move |_| {
3241 *did_update_clone.borrow_mut() = true;
3242 });
3243
3244 assert!(*did_update.borrow());
3245
3246 drop(handle);
3247 assert!(weak_app.upgrade().is_none());
3248 }
3249
3250 #[test]
3251 fn test_with_resource_profile_clamps_line_layout_cache_to_one_entry() {
3252 let dispatcher = Arc::new(TestDispatcher::new(StdRng::seed_from_u64(0)));
3253 let platform = TestPlatform::new(
3254 BackgroundExecutor::new(dispatcher.clone()),
3255 ForegroundExecutor::new(dispatcher),
3256 );
3257 let mut profile = AppResourceProfile::default();
3258 profile.text.line_layout_cache_max_entries = 0;
3259 profile.text.line_layout_cache_low_watermark = 5;
3260 profile.element_arena_size =
3261 crate::window::ELEMENT_ARENA_SIZE.load(std::sync::atomic::Ordering::Relaxed);
3262
3263 let application = Application(super::App::new_app(
3264 platform,
3265 Arc::new(()),
3266 Arc::new(NullHttpClient),
3267 AppResourceProfile::default(),
3268 ))
3269 .with_resource_profile(profile);
3270
3271 let app = application.0.borrow();
3272 assert_eq!(
3273 app.text_system.resource_budget_for_test(),
3274 (1, 0, None),
3275 "line layout caches should retain a valid minimum capacity"
3276 );
3277 }
3278
3279 #[test]
3280 fn test_gpui_borrow() {
3281 let cx = TestAppContext::single();
3282 let observation_count = Rc::new(RefCell::new(0));
3283
3284 let state = cx.update(|cx| {
3285 let state = cx.new(|_| false);
3286 cx.observe(&state, {
3287 let observation_count = observation_count.clone();
3288 move |_, _| {
3289 let mut count = observation_count.borrow_mut();
3290 *count += 1;
3291 }
3292 })
3293 .detach();
3294
3295 state
3296 });
3297
3298 cx.update(|cx| {
3299 *std::borrow::BorrowMut::borrow_mut(&mut state.as_mut(cx)) = true;
3301 });
3302
3303 cx.update(|cx| {
3304 state.write(cx, false);
3305 });
3306
3307 assert_eq!(*observation_count.borrow(), 2);
3308 }
3309
3310 #[test]
3311 fn test_tray_icon_rendering_mode_default() {
3312 assert_eq!(
3313 TrayIconRenderingMode::default(),
3314 TrayIconRenderingMode::Adaptive
3315 );
3316 }
3317
3318 #[test]
3319 fn test_set_tray_icon_forwards_to_platform() {
3320 let cx = TestAppContext::single();
3321 let icon = [1_u8, 2, 3, 4];
3322
3323 cx.update(|cx| {
3324 cx.set_tray_icon(Some(&icon));
3325 });
3326
3327 assert_eq!(cx.tray_icon(), Some(icon.to_vec()));
3328 }
3329
3330 #[test]
3331 fn test_set_tray_icon_rendering_mode_forwards_to_platform() {
3332 let cx = TestAppContext::single();
3333
3334 cx.update(|cx| {
3335 cx.set_tray_icon_rendering_mode(TrayIconRenderingMode::Original);
3336 });
3337
3338 assert_eq!(
3339 cx.tray_icon_rendering_mode(),
3340 TrayIconRenderingMode::Original
3341 );
3342 }
3343
3344 #[test]
3345 fn test_tray_icon_rendering_mode_and_icon_updates_are_order_independent() {
3346 let first = TestAppContext::single();
3347 let second = TestAppContext::single();
3348 let icon = [9_u8, 8, 7, 6];
3349
3350 first.update(|cx| {
3351 cx.set_tray_icon_rendering_mode(TrayIconRenderingMode::Original);
3352 cx.set_tray_icon(Some(&icon));
3353 });
3354
3355 second.update(|cx| {
3356 cx.set_tray_icon(Some(&icon));
3357 cx.set_tray_icon_rendering_mode(TrayIconRenderingMode::Original);
3358 });
3359
3360 assert_eq!(first.tray_icon(), Some(icon.to_vec()));
3361 assert_eq!(second.tray_icon(), Some(icon.to_vec()));
3362 assert_eq!(
3363 first.tray_icon_rendering_mode(),
3364 TrayIconRenderingMode::Original
3365 );
3366 assert_eq!(
3367 second.tray_icon_rendering_mode(),
3368 TrayIconRenderingMode::Original
3369 );
3370 }
3371
3372 #[test]
3373 fn test_tray_icon_click_event_keeps_legacy_callback_compatible() {
3374 let cx = TestAppContext::single();
3375 let legacy_event = Rc::new(RefCell::new(None));
3376 let click_event = Rc::new(RefCell::new(None));
3377
3378 cx.update({
3379 let legacy_event = legacy_event.clone();
3380 let click_event = click_event.clone();
3381 |cx| {
3382 cx.on_tray_icon_event(move |event, _| {
3383 *legacy_event.borrow_mut() = Some(event);
3384 });
3385 cx.on_tray_icon_click_event(move |event, _| {
3386 *click_event.borrow_mut() = Some(event);
3387 });
3388 }
3389 });
3390
3391 let event =
3392 TrayIconClickEvent::with_position(TrayIconEvent::LeftClick, point(px(120.0), px(24.0)));
3393 cx.simulate_tray_icon_click_event(event.clone());
3394
3395 assert_eq!(*legacy_event.borrow(), Some(TrayIconEvent::LeftClick));
3396 assert_eq!(*click_event.borrow(), Some(event));
3397 }
3398
3399 #[test]
3400 fn test_tray_icon_callbacks_keep_re_registration_during_dispatch() {
3401 let cx = TestAppContext::single();
3402 let legacy_events = Rc::new(RefCell::new(Vec::new()));
3403 let click_events = Rc::new(RefCell::new(Vec::new()));
3404
3405 cx.update({
3406 let legacy_events = legacy_events.clone();
3407 let click_events = click_events.clone();
3408 move |cx| {
3409 cx.on_tray_icon_event({
3410 let legacy_events = legacy_events.clone();
3411 move |event, cx| {
3412 legacy_events.borrow_mut().push(("old", event));
3413 cx.on_tray_icon_event({
3414 let legacy_events = legacy_events.clone();
3415 move |event, _| {
3416 legacy_events.borrow_mut().push(("new", event));
3417 }
3418 });
3419 }
3420 });
3421
3422 cx.on_tray_icon_click_event({
3423 let click_events = click_events.clone();
3424 move |event, cx| {
3425 click_events.borrow_mut().push(("old", event.kind));
3426 cx.on_tray_icon_click_event({
3427 let click_events = click_events.clone();
3428 move |event, _| {
3429 click_events.borrow_mut().push(("new", event.kind));
3430 }
3431 });
3432 }
3433 });
3434 }
3435 });
3436
3437 cx.simulate_tray_icon_click_event(TrayIconClickEvent::new(TrayIconEvent::LeftClick));
3438 cx.simulate_tray_icon_click_event(TrayIconClickEvent::new(TrayIconEvent::RightClick));
3439
3440 assert_eq!(
3441 *legacy_events.borrow(),
3442 vec![
3443 ("old", TrayIconEvent::LeftClick),
3444 ("new", TrayIconEvent::RightClick)
3445 ]
3446 );
3447 assert_eq!(
3448 *click_events.borrow(),
3449 vec![
3450 ("old", TrayIconEvent::LeftClick),
3451 ("new", TrayIconEvent::RightClick)
3452 ]
3453 );
3454 }
3455
3456 #[test]
3457 fn test_set_window_appearance_override_is_reported_by_getter() {
3458 let cx = TestAppContext::single();
3459 assert_eq!(
3460 cx.read(|cx| cx.window_appearance()),
3461 WindowAppearance::Light
3462 );
3463
3464 cx.update(|cx| cx.set_window_appearance(Some(WindowAppearance::Dark)));
3465 assert_eq!(cx.read(|cx| cx.window_appearance()), WindowAppearance::Dark);
3466
3467 cx.update(|cx| cx.set_window_appearance(Some(WindowAppearance::VibrantLight)));
3468 assert_eq!(
3469 cx.read(|cx| cx.window_appearance()),
3470 WindowAppearance::VibrantLight
3471 );
3472
3473 cx.update(|cx| cx.set_window_appearance(None));
3474 assert_eq!(
3475 cx.read(|cx| cx.window_appearance()),
3476 WindowAppearance::Light
3477 );
3478 }
3479
3480 #[test]
3481 fn test_tray_anchor_for_position_uses_display_local_bounds() {
3482 let cx = TestAppContext::single();
3483 let anchor = cx.update(|cx| cx.tray_anchor_for_position(point(px(120.0), px(24.0))));
3484 let anchor = anchor.expect("expected tray anchor");
3485
3486 assert_eq!(anchor.display_id.0, 1);
3487 assert_eq!(anchor.bounds.origin, point(px(108.0), px(12.0)));
3488 assert_eq!(anchor.bounds.size.width, px(24.0));
3489 assert_eq!(anchor.bounds.size.height, px(24.0));
3490 }
3491
3492 #[test]
3493 fn test_tray_anchor_for_position_keeps_out_of_display_hint() {
3494 let cx = TestAppContext::single();
3495 let anchor = cx.update(|cx| cx.tray_anchor_for_position(point(px(-20.0), px(10.0))));
3496 let anchor = anchor.expect("expected tray anchor");
3497
3498 assert_eq!(anchor.display_id.0, 1);
3499 assert_eq!(anchor.bounds.origin, point(px(-32.0), px(-2.0)));
3500 }
3501
3502 #[crate::test]
3503 async fn test_on_missing_glyphs_delivers_reports(cx: &mut TestAppContext) {
3504 use crate::{FallbackFontClass, MissingGlyph};
3505
3506 let reported = Rc::new(RefCell::new(Vec::new()));
3507 cx.update(|cx| {
3508 cx.on_missing_glyphs({
3509 let reported = reported.clone();
3510 move |glyphs, _| reported.borrow_mut().extend(glyphs.iter().cloned())
3511 });
3512 cx.text_system().report_missing_glyphs_in_test(vec![
3513 MissingGlyph::new("\u{1F980}".into(), FallbackFontClass::Monospace),
3514 MissingGlyph::new("\u{1F980}".into(), FallbackFontClass::Monospace),
3515 ]);
3516 });
3517
3518 cx.run_until_parked();
3519
3520 assert_eq!(
3522 reported.borrow().as_slice(),
3523 &[MissingGlyph::new(
3524 "\u{1F980}".into(),
3525 FallbackFontClass::Monospace
3526 )]
3527 );
3528 }
3529
3530 #[crate::test]
3531 async fn test_restart_preserves_path_and_arguments(cx: &mut TestAppContext) {
3532 #[cfg(unix)]
3533 let user_data_dir = OsString::from_vec(b"/tmp/zed data/\xff".to_vec());
3534 #[cfg(not(unix))]
3535 let user_data_dir = OsString::from("C:\\zed data");
3536 let arguments = vec![OsString::from("--user-data-dir"), user_data_dir];
3537 let restart_path = PathBuf::from("updated-zed");
3538 let _application =
3539 super::Application(cx.app.clone()).with_restart_arguments(arguments.clone());
3540 let restart = cx.expect_restart();
3541
3542 cx.update(|cx| {
3543 cx.set_restart_path(restart_path.clone());
3544 cx.restart();
3545 });
3546
3547 let (path, restart_arguments) = restart.await.expect("restart was not requested");
3548 assert_eq!(path, Some(restart_path));
3549 assert_eq!(restart_arguments, arguments);
3550 }
3551}