use std::{ any::{type_name, TypeId}, cell::{Ref, RefCell, RefMut}, marker::PhantomData, mem, ops::{Deref, DerefMut}, path::{Path, PathBuf}, rc::{Rc, Weak}, sync::{atomic::Ordering::SeqCst, Arc}, time::Duration, }; use anyhow::{anyhow, Result}; use derive_more::{Deref, DerefMut}; use futures::{ channel::oneshot, future::{LocalBoxFuture, Shared}, Future, FutureExt, }; use parking_lot::RwLock; use slotmap::SlotMap; pub use async_context::*; use collections::{FxHashMap, FxHashSet, HashMap, VecDeque}; pub use context::*; pub use entity_map::*; use http_client::HttpClient; #[cfg(any(test, feature = "test-support"))] pub use test_context::*; use util::ResultExt; use crate::{ current_platform, hash, init_app_menus, Action, ActionBuildError, ActionRegistry, Any, AnyView, AnyWindowHandle, AppContext, Asset, AssetSource, BackgroundExecutor, Bounds, ClipboardItem, DispatchPhase, DisplayId, EventEmitter, FocusHandle, FocusMap, ForegroundExecutor, Global, KeyBinding, Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, Window, WindowAppearance, WindowHandle, WindowId, WindowInvalidator, }; mod async_context; mod context; mod entity_map; #[cfg(any(test, feature = "test-support"))] mod test_context; /// The duration for which futures returned from [Context::on_app_quit] can run before the application fully quits. pub const SHUTDOWN_TIMEOUT: Duration = Duration::from_millis(100); /// Temporary(?) wrapper around [`RefCell`] to help us debug any double borrows. /// Strongly consider removing after stabilization. #[doc(hidden)] pub struct AppCell { app: RefCell, } impl AppCell { #[doc(hidden)] #[track_caller] pub fn borrow(&self) -> AppRef { if option_env!("TRACK_THREAD_BORROWS").is_some() { let thread_id = std::thread::current().id(); eprintln!("borrowed {thread_id:?}"); } AppRef(self.app.borrow()) } #[doc(hidden)] #[track_caller] pub fn borrow_mut(&self) -> AppRefMut { if option_env!("TRACK_THREAD_BORROWS").is_some() { let thread_id = std::thread::current().id(); eprintln!("borrowed {thread_id:?}"); } AppRefMut(self.app.borrow_mut()) } } #[doc(hidden)] #[derive(Deref, DerefMut)] pub struct AppRef<'a>(Ref<'a, App>); impl<'a> Drop for AppRef<'a> { fn drop(&mut self) { if option_env!("TRACK_THREAD_BORROWS").is_some() { let thread_id = std::thread::current().id(); eprintln!("dropped borrow from {thread_id:?}"); } } } #[doc(hidden)] #[derive(Deref, DerefMut)] pub struct AppRefMut<'a>(RefMut<'a, App>); impl<'a> Drop for AppRefMut<'a> { fn drop(&mut self) { if option_env!("TRACK_THREAD_BORROWS").is_some() { let thread_id = std::thread::current().id(); eprintln!("dropped {thread_id:?}"); } } } /// A reference to a GPUI application, typically constructed in the `main` function of your app. /// You won't interact with this type much outside of initial configuration and startup. pub struct Application(Rc); /// Represents an application before it is fully launched. Once your app is /// configured, you'll start the app with `App::run`. impl Application { /// Builds an app with the given asset source. #[allow(clippy::new_without_default)] pub fn new() -> Self { #[cfg(any(test, feature = "test-support"))] log::info!("GPUI was compiled in test mode"); Self(App::new_app( current_platform(false), Arc::new(()), Arc::new(NullHttpClient), )) } /// Build an app in headless mode. This prevents opening windows, /// but makes it possible to run an application in an context like /// SSH, where GUI applications are not allowed. pub fn headless() -> Self { Self(App::new_app( current_platform(true), Arc::new(()), Arc::new(NullHttpClient), )) } /// Assign pub fn with_assets(self, asset_source: impl AssetSource) -> Self { let mut context_lock = self.0.borrow_mut(); let asset_source = Arc::new(asset_source); context_lock.asset_source = asset_source.clone(); context_lock.svg_renderer = SvgRenderer::new(asset_source); drop(context_lock); self } /// Sets the HTTP client for the application. pub fn with_http_client(self, http_client: Arc) -> Self { let mut context_lock = self.0.borrow_mut(); context_lock.http_client = http_client; drop(context_lock); self } /// Start the application. The provided callback will be called once the /// app is fully launched. pub fn run(self, on_finish_launching: F) where F: 'static + FnOnce(&mut App), { let this = self.0.clone(); let platform = self.0.borrow().platform.clone(); platform.run(Box::new(move || { let cx = &mut *this.borrow_mut(); on_finish_launching(cx); })); } /// Register a handler to be invoked when the platform instructs the application /// to open one or more URLs. pub fn on_open_urls(&self, mut callback: F) -> &Self where F: 'static + FnMut(Vec), { self.0.borrow().platform.on_open_urls(Box::new(callback)); self } /// Invokes a handler when an already-running application is launched. /// On macOS, this can occur when the application icon is double-clicked or the app is launched via the dock. pub fn on_reopen(&self, mut callback: F) -> &Self where F: 'static + FnMut(&mut App), { let this = Rc::downgrade(&self.0); self.0.borrow_mut().platform.on_reopen(Box::new(move || { if let Some(app) = this.upgrade() { callback(&mut app.borrow_mut()); } })); self } /// Returns a handle to the [`BackgroundExecutor`] associated with this app, which can be used to spawn futures in the background. pub fn background_executor(&self) -> BackgroundExecutor { self.0.borrow().background_executor.clone() } /// Returns a handle to the [`ForegroundExecutor`] associated with this app, which can be used to spawn futures in the foreground. pub fn foreground_executor(&self) -> ForegroundExecutor { self.0.borrow().foreground_executor.clone() } /// Returns a reference to the [`TextSystem`] associated with this app. pub fn text_system(&self) -> Arc { self.0.borrow().text_system.clone() } /// Returns the file URL of the executable with the specified name in the application bundle pub fn path_for_auxiliary_executable(&self, name: &str) -> Result { self.0.borrow().path_for_auxiliary_executable(name) } } type Handler = Box bool + 'static>; type Listener = Box bool + 'static>; pub(crate) type KeystrokeObserver = Box bool + 'static>; type QuitHandler = Box LocalBoxFuture<'static, ()> + 'static>; type ReleaseListener = Box; type NewEntityListener = Box, &mut App) + 'static>; /// Contains the state of the full application, and passed as a reference to a variety of callbacks. /// Other [Context] derefs to this type. /// You need a reference to an `App` to access the state of a [Entity]. pub struct App { pub(crate) this: Weak, pub(crate) platform: Rc, text_system: Arc, flushing_effects: bool, pending_updates: usize, pub(crate) actions: Rc, pub(crate) active_drag: Option, pub(crate) background_executor: BackgroundExecutor, pub(crate) foreground_executor: ForegroundExecutor, pub(crate) loading_assets: FxHashMap<(TypeId, u64), Box>, asset_source: Arc, pub(crate) svg_renderer: SvgRenderer, http_client: Arc, pub(crate) globals_by_type: FxHashMap>, pub(crate) entities: EntityMap, pub(crate) window_update_stack: Vec, pub(crate) new_entity_observers: SubscriberSet, pub(crate) windows: SlotMap>, pub(crate) window_handles: FxHashMap, pub(crate) focus_handles: Arc, pub(crate) keymap: Rc>, pub(crate) keyboard_layout: SharedString, pub(crate) global_action_listeners: FxHashMap>>, pending_effects: VecDeque, pub(crate) pending_notifications: FxHashSet, pub(crate) pending_global_notifications: FxHashSet, pub(crate) observers: SubscriberSet, // TypeId is the type of the event that the listener callback expects pub(crate) event_listeners: SubscriberSet, pub(crate) keystroke_observers: SubscriberSet<(), KeystrokeObserver>, pub(crate) keyboard_layout_observers: SubscriberSet<(), Handler>, pub(crate) release_listeners: SubscriberSet, pub(crate) global_observers: SubscriberSet, pub(crate) quit_observers: SubscriberSet<(), QuitHandler>, pub(crate) layout_id_buffer: Vec, // We recycle this memory across layout requests. pub(crate) propagate_event: bool, pub(crate) prompt_builder: Option, pub(crate) window_invalidators_by_entity: FxHashMap>, pub(crate) tracked_entities: FxHashMap>, #[cfg(any(test, feature = "test-support", debug_assertions))] pub(crate) name: Option<&'static str>, } impl App { #[allow(clippy::new_ret_no_self)] pub(crate) fn new_app( platform: Rc, asset_source: Arc, http_client: Arc, ) -> Rc { let executor = platform.background_executor(); let foreground_executor = platform.foreground_executor(); assert!( executor.is_main_thread(), "must construct App on main thread" ); let text_system = Arc::new(TextSystem::new(platform.text_system())); let entities = EntityMap::new(); let keyboard_layout = SharedString::from(platform.keyboard_layout()); let app = Rc::new_cyclic(|this| AppCell { app: RefCell::new(App { this: this.clone(), platform: platform.clone(), text_system, actions: Rc::new(ActionRegistry::default()), flushing_effects: false, pending_updates: 0, active_drag: None, background_executor: executor, foreground_executor, svg_renderer: SvgRenderer::new(asset_source.clone()), loading_assets: Default::default(), asset_source, http_client, globals_by_type: FxHashMap::default(), entities, new_entity_observers: SubscriberSet::new(), windows: SlotMap::with_key(), window_update_stack: Vec::new(), window_handles: FxHashMap::default(), focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), keymap: Rc::new(RefCell::new(Keymap::default())), keyboard_layout, global_action_listeners: FxHashMap::default(), pending_effects: VecDeque::new(), pending_notifications: FxHashSet::default(), pending_global_notifications: FxHashSet::default(), observers: SubscriberSet::new(), tracked_entities: FxHashMap::default(), window_invalidators_by_entity: FxHashMap::default(), event_listeners: SubscriberSet::new(), release_listeners: SubscriberSet::new(), keystroke_observers: SubscriberSet::new(), keyboard_layout_observers: SubscriberSet::new(), global_observers: SubscriberSet::new(), quit_observers: SubscriberSet::new(), layout_id_buffer: Default::default(), propagate_event: true, prompt_builder: Some(PromptBuilder::Default), #[cfg(any(test, feature = "test-support", debug_assertions))] name: None, }), }); init_app_menus(platform.as_ref(), &mut app.borrow_mut()); platform.on_keyboard_layout_change(Box::new({ let app = Rc::downgrade(&app); move || { if let Some(app) = app.upgrade() { let cx = &mut app.borrow_mut(); cx.keyboard_layout = SharedString::from(cx.platform.keyboard_layout()); cx.keyboard_layout_observers .clone() .retain(&(), move |callback| (callback)(cx)); } } })); platform.on_quit(Box::new({ let cx = app.clone(); move || { cx.borrow_mut().shutdown(); } })); app } /// Quit the application gracefully. Handlers registered with [`Context::on_app_quit`] /// will be given 100ms to complete before exiting. pub fn shutdown(&mut self) { let mut futures = Vec::new(); for observer in self.quit_observers.remove(&()) { futures.push(observer(self)); } self.windows.clear(); self.window_handles.clear(); self.flush_effects(); let futures = futures::future::join_all(futures); if self .background_executor .block_with_timeout(SHUTDOWN_TIMEOUT, futures) .is_err() { log::error!("timed out waiting on app_will_quit"); } } /// Get the id of the current keyboard layout pub fn keyboard_layout(&self) -> &SharedString { &self.keyboard_layout } /// Invokes a handler when the current keyboard layout changes pub fn on_keyboard_layout_change(&self, mut callback: F) -> Subscription where F: 'static + FnMut(&mut App), { let (subscription, activate) = self.keyboard_layout_observers.insert( (), Box::new(move |cx| { callback(cx); true }), ); activate(); subscription } /// Gracefully quit the application via the platform's standard routine. pub fn quit(&self) { self.platform.quit(); } /// Schedules all windows in the application to be redrawn. This can be called /// multiple times in an update cycle and still result in a single redraw. pub fn refresh_windows(&mut self) { self.pending_effects.push_back(Effect::RefreshWindows); } pub(crate) fn update(&mut self, update: impl FnOnce(&mut Self) -> R) -> R { self.pending_updates += 1; let result = update(self); if !self.flushing_effects && self.pending_updates == 1 { self.flushing_effects = true; self.flush_effects(); self.flushing_effects = false; } self.pending_updates -= 1; result } /// Arrange a callback to be invoked when the given entity calls `notify` on its respective context. pub fn observe( &mut self, entity: &Entity, mut on_notify: impl FnMut(Entity, &mut App) + 'static, ) -> Subscription where W: 'static, { self.observe_internal(entity, move |e, cx| { on_notify(e, cx); true }) } pub(crate) fn detect_accessed_entities( &mut self, callback: impl FnOnce(&mut App) -> R, ) -> (R, FxHashSet) { let accessed_entities_start = self.entities.accessed_entities.borrow().clone(); let result = callback(self); let accessed_entities_end = self.entities.accessed_entities.borrow().clone(); let entities_accessed_in_callback = accessed_entities_end .difference(&accessed_entities_start) .copied() .collect::>(); (result, entities_accessed_in_callback) } pub(crate) fn record_entities_accessed( &mut self, window_handle: AnyWindowHandle, invalidator: WindowInvalidator, entities: &FxHashSet, ) { let mut tracked_entities = std::mem::take(self.tracked_entities.entry(window_handle.id).or_default()); for entity in tracked_entities.iter() { self.window_invalidators_by_entity .entry(*entity) .and_modify(|windows| { windows.remove(&window_handle.id); }); } for entity in entities.iter() { self.window_invalidators_by_entity .entry(*entity) .or_default() .insert(window_handle.id, invalidator.clone()); } tracked_entities.clear(); tracked_entities.extend(entities.iter().copied()); self.tracked_entities .insert(window_handle.id, tracked_entities); } pub(crate) fn new_observer(&mut self, key: EntityId, value: Handler) -> Subscription { let (subscription, activate) = self.observers.insert(key, value); self.defer(move |_| activate()); subscription } pub(crate) fn observe_internal( &mut self, entity: &Entity, mut on_notify: impl FnMut(Entity, &mut App) -> bool + 'static, ) -> Subscription where W: 'static, { let entity_id = entity.entity_id(); let handle = entity.downgrade(); self.new_observer( entity_id, Box::new(move |cx| { if let Some(handle) = Entity::::upgrade_from(&handle) { on_notify(handle, cx) } else { false } }), ) } /// Arrange for the given callback to be invoked whenever the given entity emits an event of a given type. /// The callback is provided a handle to the emitting entity and a reference to the emitted event. pub fn subscribe( &mut self, entity: &Entity, mut on_event: impl FnMut(Entity, &Event, &mut App) + 'static, ) -> Subscription where T: 'static + EventEmitter, Event: 'static, { self.subscribe_internal(entity, move |entity, event, cx| { on_event(entity, event, cx); true }) } pub(crate) fn new_subscription( &mut self, key: EntityId, value: (TypeId, Listener), ) -> Subscription { let (subscription, activate) = self.event_listeners.insert(key, value); self.defer(move |_| activate()); subscription } pub(crate) fn subscribe_internal( &mut self, entity: &Entity, mut on_event: impl FnMut(Entity, &Evt, &mut App) -> bool + 'static, ) -> Subscription where T: 'static + EventEmitter, Evt: 'static, { let entity_id = entity.entity_id(); let entity = entity.downgrade(); self.new_subscription( entity_id, ( TypeId::of::(), Box::new(move |event, cx| { let event: &Evt = event.downcast_ref().expect("invalid event type"); if let Some(handle) = Entity::::upgrade_from(&entity) { on_event(handle, event, cx) } else { false } }), ), ) } /// Returns handles to all open windows in the application. /// Each handle could be downcast to a handle typed for the root view of that window. /// To find all windows of a given type, you could filter on pub fn windows(&self) -> Vec { self.windows .keys() .flat_map(|window_id| self.window_handles.get(&window_id).copied()) .collect() } /// Returns the window handles ordered by their appearance on screen, front to back. /// /// The first window in the returned list is the active/topmost window of the application. /// /// This method returns None if the platform doesn't implement the method yet. pub fn window_stack(&self) -> Option> { self.platform.window_stack() } /// Returns a handle to the window that is currently focused at the platform level, if one exists. pub fn active_window(&self) -> Option { self.platform.active_window() } /// Opens a new window with the given option and the root view returned by the given function. /// The function is invoked with a `Window`, which can be used to interact with window-specific /// functionality. pub fn open_window( &mut self, options: crate::WindowOptions, build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity, ) -> anyhow::Result> { self.update(|cx| { let id = cx.windows.insert(None); let handle = WindowHandle::new(id); match Window::new(handle.into(), options, cx) { Ok(mut window) => { cx.window_update_stack.push(id); let root_view = build_root_view(&mut window, cx); cx.window_update_stack.pop(); window.root.replace(root_view.into()); window.defer(cx, |window: &mut Window, cx| window.appearance_changed(cx)); cx.window_handles.insert(id, window.handle); cx.windows.get_mut(id).unwrap().replace(window); Ok(handle) } Err(e) => { cx.windows.remove(id); Err(e) } } }) } /// Instructs the platform to activate the application by bringing it to the foreground. pub fn activate(&self, ignoring_other_apps: bool) { self.platform.activate(ignoring_other_apps); } /// Hide the application at the platform level. pub fn hide(&self) { self.platform.hide(); } /// Hide other applications at the platform level. pub fn hide_other_apps(&self) { self.platform.hide_other_apps(); } /// Unhide other applications at the platform level. pub fn unhide_other_apps(&self) { self.platform.unhide_other_apps(); } /// Returns the list of currently active displays. pub fn displays(&self) -> Vec> { self.platform.displays() } /// Returns the primary display that will be used for new windows. pub fn primary_display(&self) -> Option> { self.platform.primary_display() } /// Returns a list of available screen capture sources. pub fn screen_capture_sources( &self, ) -> oneshot::Receiver>>> { self.platform.screen_capture_sources() } /// Returns the display with the given ID, if one exists. pub fn find_display(&self, id: DisplayId) -> Option> { self.displays() .iter() .find(|display| display.id() == id) .cloned() } /// Returns the appearance of the application's windows. pub fn window_appearance(&self) -> WindowAppearance { self.platform.window_appearance() } /// Writes data to the primary selection buffer. /// Only available on Linux. #[cfg(any(target_os = "linux", target_os = "freebsd"))] pub fn write_to_primary(&self, item: ClipboardItem) { self.platform.write_to_primary(item) } /// Writes data to the platform clipboard. pub fn write_to_clipboard(&self, item: ClipboardItem) { self.platform.write_to_clipboard(item) } /// Reads data from the primary selection buffer. /// Only available on Linux. #[cfg(any(target_os = "linux", target_os = "freebsd"))] pub fn read_from_primary(&self) -> Option { self.platform.read_from_primary() } /// Reads data from the platform clipboard. pub fn read_from_clipboard(&self) -> Option { self.platform.read_from_clipboard() } /// Writes credentials to the platform keychain. pub fn write_credentials( &self, url: &str, username: &str, password: &[u8], ) -> Task> { self.platform.write_credentials(url, username, password) } /// Reads credentials from the platform keychain. pub fn read_credentials(&self, url: &str) -> Task)>>> { self.platform.read_credentials(url) } /// Deletes credentials from the platform keychain. pub fn delete_credentials(&self, url: &str) -> Task> { self.platform.delete_credentials(url) } /// Directs the platform's default browser to open the given URL. pub fn open_url(&self, url: &str) { self.platform.open_url(url); } /// Registers the given URL scheme (e.g. `zed` for `zed://` urls) to be /// opened by the current app. /// /// On some platforms (e.g. macOS) you may be able to register URL schemes /// as part of app distribution, but this method exists to let you register /// schemes at runtime. pub fn register_url_scheme(&self, scheme: &str) -> Task> { self.platform.register_url_scheme(scheme) } /// Returns the full pathname of the current app bundle. /// /// Returns an error if the app is not being run from a bundle. pub fn app_path(&self) -> Result { self.platform.app_path() } /// On Linux, returns the name of the compositor in use. /// /// Returns an empty string on other platforms. pub fn compositor_name(&self) -> &'static str { self.platform.compositor_name() } /// Returns the file URL of the executable with the specified name in the application bundle pub fn path_for_auxiliary_executable(&self, name: &str) -> Result { self.platform.path_for_auxiliary_executable(name) } /// Displays a platform modal for selecting paths. /// /// When one or more paths are selected, they'll be relayed asynchronously via the returned oneshot channel. /// If cancelled, a `None` will be relayed instead. /// May return an error on Linux if the file picker couldn't be opened. pub fn prompt_for_paths( &self, options: PathPromptOptions, ) -> oneshot::Receiver>>> { self.platform.prompt_for_paths(options) } /// Displays a platform modal for selecting a new path where a file can be saved. /// /// The provided directory will be used to set the initial location. /// When a path is selected, it is relayed asynchronously via the returned oneshot channel. /// If cancelled, a `None` will be relayed instead. /// May return an error on Linux if the file picker couldn't be opened. pub fn prompt_for_new_path( &self, directory: &Path, ) -> oneshot::Receiver>> { self.platform.prompt_for_new_path(directory) } /// Reveals the specified path at the platform level, such as in Finder on macOS. pub fn reveal_path(&self, path: &Path) { self.platform.reveal_path(path) } /// Opens the specified path with the system's default application. pub fn open_with_system(&self, path: &Path) { self.platform.open_with_system(path) } /// Returns whether the user has configured scrollbars to auto-hide at the platform level. pub fn should_auto_hide_scrollbars(&self) -> bool { self.platform.should_auto_hide_scrollbars() } /// Restarts the application. pub fn restart(&self, binary_path: Option) { self.platform.restart(binary_path) } /// Returns the HTTP client for the application. pub fn http_client(&self) -> Arc { self.http_client.clone() } /// Sets the HTTP client for the application. pub fn set_http_client(&mut self, new_client: Arc) { self.http_client = new_client; } /// Returns the SVG renderer used by the application. pub fn svg_renderer(&self) -> SvgRenderer { self.svg_renderer.clone() } pub(crate) fn push_effect(&mut self, effect: Effect) { match &effect { Effect::Notify { emitter } => { if !self.pending_notifications.insert(*emitter) { return; } } Effect::NotifyGlobalObservers { global_type } => { if !self.pending_global_notifications.insert(*global_type) { return; } } _ => {} }; self.pending_effects.push_back(effect); } /// Called at the end of [`App::update`] to complete any side effects /// such as notifying observers, emitting events, etc. Effects can themselves /// cause effects, so we continue looping until all effects are processed. fn flush_effects(&mut self) { loop { self.release_dropped_entities(); self.release_dropped_focus_handles(); if let Some(effect) = self.pending_effects.pop_front() { match effect { Effect::Notify { emitter } => { self.apply_notify_effect(emitter); } Effect::Emit { emitter, event_type, event, } => self.apply_emit_effect(emitter, event_type, event), Effect::RefreshWindows => { self.apply_refresh_effect(); } Effect::NotifyGlobalObservers { global_type } => { self.apply_notify_global_observers_effect(global_type); } Effect::Defer { callback } => { self.apply_defer_effect(callback); } Effect::EntityCreated { entity, tid, window, } => { self.apply_entity_created_effect(entity, tid, window); } } } else { #[cfg(any(test, feature = "test-support"))] for window in self .windows .values() .filter_map(|window| { let window = window.as_ref()?; window.invalidator.is_dirty().then_some(window.handle) }) .collect::>() { self.update_window(window, |_, window, cx| window.draw(cx)) .unwrap(); } if self.pending_effects.is_empty() { break; } } } } /// Repeatedly called during `flush_effects` to release any entities whose /// reference count has become zero. We invoke any release observers before dropping /// each entity. fn release_dropped_entities(&mut self) { loop { let dropped = self.entities.take_dropped(); if dropped.is_empty() { break; } for (entity_id, mut entity) in dropped { self.observers.remove(&entity_id); self.event_listeners.remove(&entity_id); for release_callback in self.release_listeners.remove(&entity_id) { release_callback(entity.as_mut(), self); } } } } /// Repeatedly called during `flush_effects` to handle a focused handle being dropped. fn release_dropped_focus_handles(&mut self) { self.focus_handles .clone() .write() .retain(|handle_id, count| { if count.load(SeqCst) == 0 { for window_handle in self.windows() { window_handle .update(self, |_, window, _| { if window.focus == Some(handle_id) { window.blur(); } }) .unwrap(); } false } else { true } }); } fn apply_notify_effect(&mut self, emitter: EntityId) { self.pending_notifications.remove(&emitter); self.observers .clone() .retain(&emitter, |handler| handler(self)); } fn apply_emit_effect(&mut self, emitter: EntityId, event_type: TypeId, event: Box) { self.event_listeners .clone() .retain(&emitter, |(stored_type, handler)| { if *stored_type == event_type { handler(event.as_ref(), self) } else { true } }); } fn apply_refresh_effect(&mut self) { for window in self.windows.values_mut() { if let Some(window) = window.as_mut() { window.refreshing = true; window.invalidator.set_dirty(true); } } } fn apply_notify_global_observers_effect(&mut self, type_id: TypeId) { self.pending_global_notifications.remove(&type_id); self.global_observers .clone() .retain(&type_id, |observer| observer(self)); } fn apply_defer_effect(&mut self, callback: Box) { callback(self); } fn apply_entity_created_effect( &mut self, entity: AnyEntity, tid: TypeId, window: Option, ) { self.new_entity_observers.clone().retain(&tid, |observer| { if let Some(id) = window { self.update_window_id(id, { let entity = entity.clone(); |_, window, cx| (observer)(entity, &mut Some(window), cx) }) .expect("All windows should be off the stack when flushing effects"); } else { (observer)(entity.clone(), &mut None, self) } true }); } fn update_window_id(&mut self, id: WindowId, update: F) -> Result where F: FnOnce(AnyView, &mut Window, &mut App) -> T, { self.update(|cx| { let mut window = cx .windows .get_mut(id) .ok_or_else(|| anyhow!("window not found"))? .take() .ok_or_else(|| anyhow!("window not found"))?; let root_view = window.root.clone().unwrap(); cx.window_update_stack.push(window.handle.id); let result = update(root_view, &mut window, cx); cx.window_update_stack.pop(); if window.removed { cx.window_handles.remove(&id); cx.windows.remove(id); } else { cx.windows .get_mut(id) .ok_or_else(|| anyhow!("window not found"))? .replace(window); } Ok(result) }) } /// Creates an `AsyncApp`, which can be cloned and has a static lifetime /// so it can be held across `await` points. pub fn to_async(&self) -> AsyncApp { AsyncApp { app: self.this.clone(), background_executor: self.background_executor.clone(), foreground_executor: self.foreground_executor.clone(), } } /// Obtains a reference to the executor, which can be used to spawn futures. pub fn background_executor(&self) -> &BackgroundExecutor { &self.background_executor } /// Obtains a reference to the executor, which can be used to spawn futures. pub fn foreground_executor(&self) -> &ForegroundExecutor { &self.foreground_executor } /// Spawns the future returned by the given function on the thread pool. The closure will be invoked /// with [AsyncApp], which allows the application state to be accessed across await points. pub fn spawn(&self, f: impl FnOnce(AsyncApp) -> Fut) -> Task where Fut: Future + 'static, R: 'static, { self.foreground_executor.spawn(f(self.to_async())) } /// Schedules the given function to be run at the end of the current effect cycle, allowing entities /// that are currently on the stack to be returned to the app. pub fn defer(&mut self, f: impl FnOnce(&mut App) + 'static) { self.push_effect(Effect::Defer { callback: Box::new(f), }); } /// Accessor for the application's asset source, which is provided when constructing the `App`. pub fn asset_source(&self) -> &Arc { &self.asset_source } /// Accessor for the text system. pub fn text_system(&self) -> &Arc { &self.text_system } /// Check whether a global of the given type has been assigned. pub fn has_global(&self) -> bool { self.globals_by_type.contains_key(&TypeId::of::()) } /// Access the global of the given type. Panics if a global for that type has not been assigned. #[track_caller] pub fn global(&self) -> &G { self.globals_by_type .get(&TypeId::of::()) .map(|any_state| any_state.downcast_ref::().unwrap()) .ok_or_else(|| anyhow!("no state of type {} exists", type_name::())) .unwrap() } /// Access the global of the given type if a value has been assigned. pub fn try_global(&self) -> Option<&G> { self.globals_by_type .get(&TypeId::of::()) .map(|any_state| any_state.downcast_ref::().unwrap()) } /// Access the global of the given type mutably. Panics if a global for that type has not been assigned. #[track_caller] pub fn global_mut(&mut self) -> &mut G { let global_type = TypeId::of::(); self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.globals_by_type .get_mut(&global_type) .and_then(|any_state| any_state.downcast_mut::()) .ok_or_else(|| anyhow!("no state of type {} exists", type_name::())) .unwrap() } /// Access the global of the given type mutably. A default value is assigned if a global of this type has not /// yet been assigned. pub fn default_global(&mut self) -> &mut G { let global_type = TypeId::of::(); self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.globals_by_type .entry(global_type) .or_insert_with(|| Box::::default()) .downcast_mut::() .unwrap() } /// Sets the value of the global of the given type. pub fn set_global(&mut self, global: G) { let global_type = TypeId::of::(); self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.globals_by_type.insert(global_type, Box::new(global)); } /// Clear all stored globals. Does not notify global observers. #[cfg(any(test, feature = "test-support"))] pub fn clear_globals(&mut self) { self.globals_by_type.drain(); } /// Remove the global of the given type from the app context. Does not notify global observers. pub fn remove_global(&mut self) -> G { let global_type = TypeId::of::(); self.push_effect(Effect::NotifyGlobalObservers { global_type }); *self .globals_by_type .remove(&global_type) .unwrap_or_else(|| panic!("no global added for {}", std::any::type_name::())) .downcast() .unwrap() } /// Register a callback to be invoked when a global of the given type is updated. pub fn observe_global( &mut self, mut f: impl FnMut(&mut Self) + 'static, ) -> Subscription { let (subscription, activate) = self.global_observers.insert( TypeId::of::(), Box::new(move |cx| { f(cx); true }), ); self.defer(move |_| activate()); subscription } /// Move the global of the given type to the stack. #[track_caller] pub(crate) fn lease_global(&mut self) -> GlobalLease { GlobalLease::new( self.globals_by_type .remove(&TypeId::of::()) .ok_or_else(|| anyhow!("no global registered of type {}", type_name::())) .unwrap(), ) } /// Restore the global of the given type after it is moved to the stack. pub(crate) fn end_global_lease(&mut self, lease: GlobalLease) { let global_type = TypeId::of::(); self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.globals_by_type.insert(global_type, lease.global); } pub(crate) fn new_entity_observer( &self, key: TypeId, value: NewEntityListener, ) -> Subscription { let (subscription, activate) = self.new_entity_observers.insert(key, value); activate(); subscription } /// Arrange for the given function to be invoked whenever a view of the specified type is created. /// The function will be passed a mutable reference to the view along with an appropriate context. pub fn observe_new( &self, on_new: impl 'static + Fn(&mut T, Option<&mut Window>, &mut Context), ) -> Subscription { self.new_entity_observer( TypeId::of::(), Box::new( move |any_entity: AnyEntity, window: &mut Option<&mut Window>, cx: &mut App| { any_entity .downcast::() .unwrap() .update(cx, |entity_state, cx| { if let Some(window) = window { on_new(entity_state, Some(window), cx); } else { on_new(entity_state, None, cx); } }) }, ), ) } /// Observe the release of a entity. The callback is invoked after the entity /// has no more strong references but before it has been dropped. pub fn observe_release( &self, handle: &Entity, on_release: impl FnOnce(&mut T, &mut App) + 'static, ) -> Subscription where T: 'static, { let (subscription, activate) = self.release_listeners.insert( handle.entity_id(), Box::new(move |entity, cx| { let entity = entity.downcast_mut().expect("invalid entity type"); on_release(entity, cx) }), ); activate(); subscription } /// Observe the release of a entity. The callback is invoked after the entity /// has no more strong references but before it has been dropped. pub fn observe_release_in( &self, handle: &Entity, window: &Window, on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static, ) -> Subscription where T: 'static, { let window_handle = window.handle; self.observe_release(&handle, move |entity, cx| { let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx)); }) } /// Register a callback to be invoked when a keystroke is received by the application /// in any window. Note that this fires after all other action and event mechanisms have resolved /// and that this API will not be invoked if the event's propagation is stopped. pub fn observe_keystrokes( &mut self, mut f: impl FnMut(&KeystrokeEvent, &mut Window, &mut App) + 'static, ) -> Subscription { fn inner( keystroke_observers: &SubscriberSet<(), KeystrokeObserver>, handler: KeystrokeObserver, ) -> Subscription { let (subscription, activate) = keystroke_observers.insert((), handler); activate(); subscription } inner( &mut self.keystroke_observers, Box::new(move |event, window, cx| { f(event, window, cx); true }), ) } /// Register key bindings. pub fn bind_keys(&mut self, bindings: impl IntoIterator) { self.keymap.borrow_mut().add_bindings(bindings); self.pending_effects.push_back(Effect::RefreshWindows); } /// Clear all key bindings in the app. pub fn clear_key_bindings(&mut self) { self.keymap.borrow_mut().clear(); self.pending_effects.push_back(Effect::RefreshWindows); } /// Register a global listener for actions invoked via the keyboard. pub fn on_action(&mut self, listener: impl Fn(&A, &mut Self) + 'static) { self.global_action_listeners .entry(TypeId::of::()) .or_default() .push(Rc::new(move |action, phase, cx| { if phase == DispatchPhase::Bubble { let action = action.downcast_ref().unwrap(); listener(action, cx) } })); } /// Event handlers propagate events by default. Call this method to stop dispatching to /// event handlers with a lower z-index (mouse) or higher in the tree (keyboard). This is /// the opposite of [`Self::propagate`]. It's also possible to cancel a call to [`Self::propagate`] by /// calling this method before effects are flushed. pub fn stop_propagation(&mut self) { self.propagate_event = false; } /// Action handlers stop propagation by default during the bubble phase of action dispatch /// dispatching to action handlers higher in the element tree. This is the opposite of /// [`Self::stop_propagation`]. It's also possible to cancel a call to [`Self::stop_propagation`] by calling /// this method before effects are flushed. pub fn propagate(&mut self) { self.propagate_event = true; } /// Build an action from some arbitrary data, typically a keymap entry. pub fn build_action( &self, name: &str, data: Option, ) -> std::result::Result, ActionBuildError> { self.actions.build_action(name, data) } /// Get all action names that have been registered. Note that registration only allows for /// actions to be built dynamically, and is unrelated to binding actions in the element tree. pub fn all_action_names(&self) -> &[SharedString] { self.actions.all_action_names() } /// Returns key bindings that invoke the given action on the currently focused element, without /// checking context. Bindings are returned in the order they were added. For display, the last /// binding should take precedence. pub fn all_bindings_for_input(&self, input: &[Keystroke]) -> Vec { RefCell::borrow(&self.keymap).all_bindings_for_input(input) } /// Get all non-internal actions that have been registered, along with their schemas. pub fn action_schemas( &self, generator: &mut schemars::gen::SchemaGenerator, ) -> Vec<(SharedString, Option)> { self.actions.action_schemas(generator) } /// Get a list of all deprecated action aliases and their canonical names. pub fn action_deprecations(&self) -> &HashMap { self.actions.action_deprecations() } /// Register a callback to be invoked when the application is about to quit. /// It is not possible to cancel the quit event at this point. pub fn on_app_quit( &self, mut on_quit: impl FnMut(&mut App) -> Fut + 'static, ) -> Subscription where Fut: 'static + Future, { let (subscription, activate) = self.quit_observers.insert( (), Box::new(move |cx| { let future = on_quit(cx); future.boxed_local() }), ); activate(); subscription } pub(crate) fn clear_pending_keystrokes(&mut self) { for window in self.windows() { window .update(self, |_, window, _| { window.clear_pending_keystrokes(); }) .ok(); } } /// Checks if the given action is bound in the current context, as defined by the app's current focus, /// the bindings in the element tree, and any global action listeners. pub fn is_action_available(&mut self, action: &dyn Action) -> bool { let mut action_available = false; if let Some(window) = self.active_window() { if let Ok(window_action_available) = window.update(self, |_, window, cx| window.is_action_available(action, cx)) { action_available = window_action_available; } } action_available || self .global_action_listeners .contains_key(&action.as_any().type_id()) } /// Sets the menu bar for this application. This will replace any existing menu bar. pub fn set_menus(&self, menus: Vec) { self.platform.set_menus(menus, &self.keymap.borrow()); } /// Gets the menu bar for this application. pub fn get_menus(&self) -> Option> { self.platform.get_menus() } /// Sets the right click menu for the app icon in the dock pub fn set_dock_menu(&self, menus: Vec) { self.platform.set_dock_menu(menus, &self.keymap.borrow()); } /// Adds given path to the bottom of the list of recent paths for the application. /// The list is usually shown on the application icon's context menu in the dock, /// and allows to open the recent files via that context menu. /// If the path is already in the list, it will be moved to the bottom of the list. pub fn add_recent_document(&self, path: &Path) { self.platform.add_recent_document(path); } /// Dispatch an action to the currently active window or global action handler /// See [`crate::Action`] for more information on how actions work pub fn dispatch_action(&mut self, action: &dyn Action) { if let Some(active_window) = self.active_window() { active_window .update(self, |_, window, cx| { window.dispatch_action(action.boxed_clone(), cx) }) .log_err(); } else { self.dispatch_global_action(action); } } fn dispatch_global_action(&mut self, action: &dyn Action) { self.propagate_event = true; if let Some(mut global_listeners) = self .global_action_listeners .remove(&action.as_any().type_id()) { for listener in &global_listeners { listener(action.as_any(), DispatchPhase::Capture, self); if !self.propagate_event { break; } } global_listeners.extend( self.global_action_listeners .remove(&action.as_any().type_id()) .unwrap_or_default(), ); self.global_action_listeners .insert(action.as_any().type_id(), global_listeners); } if self.propagate_event { if let Some(mut global_listeners) = self .global_action_listeners .remove(&action.as_any().type_id()) { for listener in global_listeners.iter().rev() { listener(action.as_any(), DispatchPhase::Bubble, self); if !self.propagate_event { break; } } global_listeners.extend( self.global_action_listeners .remove(&action.as_any().type_id()) .unwrap_or_default(), ); self.global_action_listeners .insert(action.as_any().type_id(), global_listeners); } } } /// Is there currently something being dragged? pub fn has_active_drag(&self) -> bool { self.active_drag.is_some() } /// Set the prompt renderer for GPUI. This will replace the default or platform specific /// prompts with this custom implementation. pub fn set_prompt_builder( &mut self, renderer: impl Fn( PromptLevel, &str, Option<&str>, &[&str], PromptHandle, &mut Window, &mut App, ) -> RenderablePromptHandle + 'static, ) { self.prompt_builder = Some(PromptBuilder::Custom(Box::new(renderer))) } /// Remove an asset from GPUI's cache pub fn remove_asset(&mut self, source: &A::Source) { let asset_id = (TypeId::of::(), hash(source)); self.loading_assets.remove(&asset_id); } /// Asynchronously load an asset, if the asset hasn't finished loading this will return None. /// /// Note that the multiple calls to this method will only result in one `Asset::load` call at a /// time, and the results of this call will be cached pub fn fetch_asset(&mut self, source: &A::Source) -> (Shared>, bool) { let asset_id = (TypeId::of::(), hash(source)); let mut is_first = false; let task = self .loading_assets .remove(&asset_id) .map(|boxed_task| *boxed_task.downcast::>>().unwrap()) .unwrap_or_else(|| { is_first = true; let future = A::load(source.clone(), self); let task = self.background_executor().spawn(future).shared(); task }); self.loading_assets.insert(asset_id, Box::new(task.clone())); (task, is_first) } /// Obtain a new [`FocusHandle`], which allows you to track and manipulate the keyboard focus /// for elements rendered within this window. #[track_caller] pub fn focus_handle(&self) -> FocusHandle { FocusHandle::new(&self.focus_handles) } /// Tell GPUI that an entity has changed and observers of it should be notified. pub fn notify(&mut self, entity_id: EntityId) { let window_invalidators = mem::take( self.window_invalidators_by_entity .entry(entity_id) .or_default(), ); if window_invalidators.is_empty() { if self.pending_notifications.insert(entity_id) { self.pending_effects .push_back(Effect::Notify { emitter: entity_id }); } } else { for invalidator in window_invalidators.values() { invalidator.invalidate_view(entity_id, self); } } self.window_invalidators_by_entity .insert(entity_id, window_invalidators); } /// Get the name for this App. #[cfg(any(test, feature = "test-support", debug_assertions))] pub fn get_name(&self) -> &'static str { self.name.as_ref().unwrap() } /// Returns `true` if the platform file picker supports selecting a mix of files and directories. pub fn can_select_mixed_files_and_dirs(&self) -> bool { self.platform.can_select_mixed_files_and_dirs() } } impl AppContext for App { type Result = T; /// Build an entity that is owned by the application. The given function will be invoked with /// a `Context` and must return an object representing the entity. A `Entity` handle will be returned, /// which can be used to access the entity in a context. fn new( &mut self, build_entity: impl FnOnce(&mut Context<'_, T>) -> T, ) -> Entity { self.update(|cx| { let slot = cx.entities.reserve(); let handle = slot.clone(); let entity = build_entity(&mut Context::new_context(cx, slot.downgrade())); cx.push_effect(Effect::EntityCreated { entity: handle.clone().into_any(), tid: TypeId::of::(), window: cx.window_update_stack.last().cloned(), }); cx.entities.insert(slot, entity); handle }) } fn reserve_entity(&mut self) -> Self::Result> { Reservation(self.entities.reserve()) } fn insert_entity( &mut self, reservation: Reservation, build_entity: impl FnOnce(&mut Context<'_, T>) -> T, ) -> Self::Result> { self.update(|cx| { let slot = reservation.0; let entity = build_entity(&mut Context::new_context(cx, slot.downgrade())); cx.entities.insert(slot, entity) }) } /// Updates the entity referenced by the given handle. The function is passed a mutable reference to the /// entity along with a `Context` for the entity. fn update_entity( &mut self, handle: &Entity, update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R, ) -> R { self.update(|cx| { let mut entity = cx.entities.lease(handle); let result = update( &mut entity, &mut Context::new_context(cx, handle.downgrade()), ); cx.entities.end_lease(entity); result }) } fn read_entity( &self, handle: &Entity, read: impl FnOnce(&T, &App) -> R, ) -> Self::Result where T: 'static, { let entity = self.entities.read(handle); read(entity, self) } fn update_window(&mut self, handle: AnyWindowHandle, update: F) -> Result where F: FnOnce(AnyView, &mut Window, &mut App) -> T, { self.update_window_id(handle.id, update) } fn read_window( &self, window: &WindowHandle, read: impl FnOnce(Entity, &App) -> R, ) -> Result where T: 'static, { let window = self .windows .get(window.id) .ok_or_else(|| anyhow!("window not found"))? .as_ref() .expect("attempted to read a window that is already on the stack"); let root_view = window.root.clone().unwrap(); let view = root_view .downcast::() .map_err(|_| anyhow!("root view's type has changed"))?; Ok(read(view, self)) } fn background_spawn(&self, future: impl Future + Send + 'static) -> Task where R: Send + 'static, { self.background_executor.spawn(future) } fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result where G: Global, { let mut g = self.global::(); callback(&g, self) } } /// These effects are processed at the end of each application update cycle. pub(crate) enum Effect { Notify { emitter: EntityId, }, Emit { emitter: EntityId, event_type: TypeId, event: Box, }, RefreshWindows, NotifyGlobalObservers { global_type: TypeId, }, Defer { callback: Box, }, EntityCreated { entity: AnyEntity, tid: TypeId, window: Option, }, } impl std::fmt::Debug for Effect { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Effect::Notify { emitter } => write!(f, "Notify({})", emitter), Effect::Emit { emitter, .. } => write!(f, "Emit({:?})", emitter), Effect::RefreshWindows => write!(f, "RefreshWindows"), Effect::NotifyGlobalObservers { global_type } => { write!(f, "NotifyGlobalObservers({:?})", global_type) } Effect::Defer { .. } => write!(f, "Defer(..)"), Effect::EntityCreated { entity, .. } => write!(f, "EntityCreated({:?})", entity), } } } /// Wraps a global variable value during `update_global` while the value has been moved to the stack. pub(crate) struct GlobalLease { global: Box, global_type: PhantomData, } impl GlobalLease { fn new(global: Box) -> Self { GlobalLease { global, global_type: PhantomData, } } } impl Deref for GlobalLease { type Target = G; fn deref(&self) -> &Self::Target { self.global.downcast_ref().unwrap() } } impl DerefMut for GlobalLease { fn deref_mut(&mut self) -> &mut Self::Target { self.global.downcast_mut().unwrap() } } /// Contains state associated with an active drag operation, started by dragging an element /// within the window or by dragging into the app from the underlying platform. pub struct AnyDrag { /// The view used to render this drag pub view: AnyView, /// The value of the dragged item, to be dropped pub value: Arc, /// This is used to render the dragged item in the same place /// on the original element that the drag was initiated pub cursor_offset: Point, } /// Contains state associated with a tooltip. You'll only need this struct if you're implementing /// tooltip behavior on a custom element. Otherwise, use [Div::tooltip]. #[derive(Clone)] pub struct AnyTooltip { /// The view used to display the tooltip pub view: AnyView, /// The absolute position of the mouse when the tooltip was deployed. pub mouse_position: Point, /// Given the bounds of the tooltip, checks whether the tooltip should still be visible and /// updates its state accordingly. This is needed atop the hovered element's mouse move handler /// to handle the case where the element is not painted (e.g. via use of `visible_on_hover`). pub check_visible_and_update: Rc, &mut Window, &mut App) -> bool>, } /// A keystroke event, and potentially the associated action #[derive(Debug)] pub struct KeystrokeEvent { /// The keystroke that occurred pub keystroke: Keystroke, /// The action that was resolved for the keystroke, if any pub action: Option>, } struct NullHttpClient; impl HttpClient for NullHttpClient { fn send( &self, _req: http_client::Request, ) -> futures::future::BoxFuture< 'static, Result, anyhow::Error>, > { async move { Err(anyhow!("No HttpClient available")) }.boxed() } fn proxy(&self) -> Option<&http_client::Uri> { None } fn type_name(&self) -> &'static str { type_name::() } }