1674 lines · 58.9 KB · Rust
RawDownload
use std::{    any::{type_name, TypeId},    cell::{Ref, RefCell, RefMut},    marker::PhantomData,    ops::{Deref, DerefMut},    path::{Path, PathBuf},    rc::{Rc, Weak},    sync::{        atomic::{AtomicUsize, 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 entity_map::*;use http_client::HttpClient;pub use model_context::*;#[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, Asset, AssetSource, BackgroundExecutor, Bounds, ClipboardItem, Context,    DispatchPhase, DisplayId, Entity, EventEmitter, FocusHandle, FocusId, 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, View, ViewContext, Window, WindowAppearance,    WindowContext, WindowHandle, WindowId,}; mod async_context;mod entity_map;mod model_context;#[cfg(any(test, feature = "test-support"))]mod test_context; /// The duration for which futures returned from [AppContext::on_app_context] or [ModelContext::on_app_quit] can run before the application fully quits.pub const SHUTDOWN_TIMEOUT: Duration = Duration::from_millis(100); /// Temporary(?) wrapper around [`RefCell<AppContext>`] to help us debug any double borrows./// Strongly consider removing after stabilization.#[doc(hidden)]pub struct AppCell {    app: RefCell<AppContext>,} 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, AppContext>); 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, AppContext>); 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 App(Rc<AppCell>); /// Represents an application before it is fully launched. Once your app is/// configured, you'll start the app with `App::run`.impl App {    /// 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(AppContext::new(            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(AppContext::new(            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<dyn HttpClient>) -> 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<F>(self, on_finish_launching: F)    where        F: 'static + FnOnce(&mut AppContext),    {        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<F>(&self, mut callback: F) -> &Self    where        F: 'static + FnMut(Vec<String>),    {        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<F>(&self, mut callback: F) -> &Self    where        F: 'static + FnMut(&mut AppContext),    {        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<TextSystem> {        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<PathBuf> {        self.0.borrow().path_for_auxiliary_executable(name)    }} type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;pub(crate) type KeystrokeObserver =    Box<dyn FnMut(&KeystrokeEvent, &mut WindowContext) -> bool + 'static>;type QuitHandler = Box<dyn FnOnce(&mut AppContext) -> LocalBoxFuture<'static, ()> + 'static>;type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;type NewViewListener = Box<dyn FnMut(AnyView, &mut WindowContext) + 'static>;type NewModelListener = Box<dyn FnMut(AnyModel, &mut AppContext) + 'static>; /// Contains the state of the full application, and passed as a reference to a variety of callbacks./// Other contexts such as [ModelContext], [WindowContext], and [ViewContext] deref to this type, making it the most general context type./// You need a reference to an `AppContext` to access the state of a [Model].pub struct AppContext {    pub(crate) this: Weak<AppCell>,    pub(crate) platform: Rc<dyn Platform>,    text_system: Arc<TextSystem>,    flushing_effects: bool,    pending_updates: usize,    pub(crate) actions: Rc<ActionRegistry>,    pub(crate) active_drag: Option<AnyDrag>,    pub(crate) background_executor: BackgroundExecutor,    pub(crate) foreground_executor: ForegroundExecutor,    pub(crate) loading_assets: FxHashMap<(TypeId, u64), Box<dyn Any>>,    asset_source: Arc<dyn AssetSource>,    pub(crate) svg_renderer: SvgRenderer,    http_client: Arc<dyn HttpClient>,    pub(crate) globals_by_type: FxHashMap<TypeId, Box<dyn Any>>,    pub(crate) entities: EntityMap,    pub(crate) new_model_observers: SubscriberSet<TypeId, NewModelListener>,    pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,    pub(crate) windows: SlotMap<WindowId, Option<Window>>,    pub(crate) window_handles: FxHashMap<WindowId, AnyWindowHandle>,    pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,    pub(crate) keymap: Rc<RefCell<Keymap>>,    pub(crate) keyboard_layout: SharedString,    pub(crate) global_action_listeners:        FxHashMap<TypeId, Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,    pending_effects: VecDeque<Effect>,    pub(crate) pending_notifications: FxHashSet<EntityId>,    pub(crate) pending_global_notifications: FxHashSet<TypeId>,    pub(crate) observers: SubscriberSet<EntityId, Handler>,    // TypeId is the type of the event that the listener callback expects    pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>,    pub(crate) keystroke_observers: SubscriberSet<(), KeystrokeObserver>,    pub(crate) keyboard_layout_observers: SubscriberSet<(), Handler>,    pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>,    pub(crate) global_observers: SubscriberSet<TypeId, Handler>,    pub(crate) quit_observers: SubscriberSet<(), QuitHandler>,    pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.    pub(crate) propagate_event: bool,    pub(crate) prompt_builder: Option<PromptBuilder>,     #[cfg(any(test, feature = "test-support", debug_assertions))]    pub(crate) name: Option<&'static str>,} impl AppContext {    #[allow(clippy::new_ret_no_self)]    pub(crate) fn new(        platform: Rc<dyn Platform>,        asset_source: Arc<dyn AssetSource>,        http_client: Arc<dyn HttpClient>,    ) -> Rc<AppCell> {        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(AppContext {                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_view_observers: SubscriberSet::new(),                new_model_observers: SubscriberSet::new(),                windows: SlotMap::with_key(),                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(),                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 [`ModelContext::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<F>(&self, mut callback: F) -> Subscription    where        F: 'static + FnMut(&mut AppContext),    {        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(&mut self) {        self.pending_effects.push_back(Effect::Refresh);    }     pub(crate) fn update<R>(&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 model or view calls `notify` on its respective context.    pub fn observe<W, E>(        &mut self,        entity: &E,        mut on_notify: impl FnMut(E, &mut AppContext) + 'static,    ) -> Subscription    where        W: 'static,        E: Entity<W>,    {        self.observe_internal(entity, move |e, cx| {            on_notify(e, cx);            true        })    }     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<W, E>(        &mut self,        entity: &E,        mut on_notify: impl FnMut(E, &mut AppContext) -> bool + 'static,    ) -> Subscription    where        W: 'static,        E: Entity<W>,    {        let entity_id = entity.entity_id();        let handle = entity.downgrade();        self.new_observer(            entity_id,            Box::new(move |cx| {                if let Some(handle) = E::upgrade_from(&handle) {                    on_notify(handle, cx)                } else {                    false                }            }),        )    }     /// Arrange for the given callback to be invoked whenever the given model or view 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<T, E, Event>(        &mut self,        entity: &E,        mut on_event: impl FnMut(E, &Event, &mut AppContext) + 'static,    ) -> Subscription    where        T: 'static + EventEmitter<Event>,        E: Entity<T>,        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<T, E, Evt>(

Lines 1–500 of 1674.