use crate::{ AnyView, AnyWindowHandle, AppContext, AsyncApp, DispatchPhase, Effect, EntityId, EventEmitter, FocusHandle, FocusOutEvent, Focusable, Global, KeystrokeObserver, Reservation, SubscriberSet, Subscription, Task, WeakEntity, WeakFocusHandle, Window, WindowHandle, }; use anyhow::Result; use derive_more::{Deref, DerefMut}; use futures::FutureExt; use std::{ any::{Any, TypeId}, borrow::{Borrow, BorrowMut}, future::Future, sync::Arc, }; use util::Deferred; use super::{App, AsyncWindowContext, Entity, KeystrokeEvent}; /// The app context, with specialized behavior for the given entity. #[derive(Deref, DerefMut)] pub struct Context<'a, T> { #[deref] #[deref_mut] app: &'a mut App, entity_state: WeakEntity, } impl<'a, T: 'static> Context<'a, T> { pub(crate) fn new_context(app: &'a mut App, entity_state: WeakEntity) -> Self { Self { app, entity_state } } /// The entity id of the entity backing this context. pub fn entity_id(&self) -> EntityId { self.entity_state.entity_id } /// Returns a handle to the entity belonging to this context. pub fn entity(&self) -> Entity { self.weak_entity() .upgrade() .expect("The entity must be alive if we have a entity context") } /// Returns a weak handle to the entity belonging to this context. pub fn weak_entity(&self) -> WeakEntity { self.entity_state.clone() } /// Arranges for the given function to be called whenever [`Context::notify`] is /// called with the given entity. pub fn observe( &mut self, entity: &Entity, mut on_notify: impl FnMut(&mut T, Entity, &mut Context) + 'static, ) -> Subscription where T: 'static, W: 'static, { let this = self.weak_entity(); self.app.observe_internal(entity, move |e, cx| { if let Some(this) = this.upgrade() { this.update(cx, |this, cx| on_notify(this, e, cx)); true } else { false } }) } /// Subscribe to an event type from another entity pub fn subscribe( &mut self, entity: &Entity, mut on_event: impl FnMut(&mut T, Entity, &Evt, &mut Context) + 'static, ) -> Subscription where T: 'static, T2: 'static + EventEmitter, Evt: 'static, { let this = self.weak_entity(); self.app.subscribe_internal(entity, move |e, event, cx| { if let Some(this) = this.upgrade() { this.update(cx, |this, cx| on_event(this, e, event, cx)); true } else { false } }) } /// Subscribe to an event type from ourself pub fn subscribe_self( &mut self, mut on_event: impl FnMut(&mut T, &Evt, &mut Context) + 'static, ) -> Subscription where T: 'static + EventEmitter, Evt: 'static, { let this = self.entity(); self.app.subscribe(&this, move |this, evt, cx| { this.update(cx, |this, cx| on_event(this, evt, cx)) }) } /// Register a callback to be invoked when GPUI releases this entity. pub fn on_release(&self, on_release: impl FnOnce(&mut T, &mut App) + 'static) -> Subscription where T: 'static, { let (subscription, activate) = self.app.release_listeners.insert( self.entity_state.entity_id, Box::new(move |this, cx| { let this = this.downcast_mut().expect("invalid entity type"); on_release(this, cx); }), ); activate(); subscription } /// Register a callback to be run on the release of another entity pub fn observe_release( &self, entity: &Entity, on_release: impl FnOnce(&mut T, &mut T2, &mut Context) + 'static, ) -> Subscription where T: Any, T2: 'static, { let entity_id = entity.entity_id(); let this = self.weak_entity(); let (subscription, activate) = self.app.release_listeners.insert( entity_id, Box::new(move |entity, cx| { let entity = entity.downcast_mut().expect("invalid entity type"); if let Some(this) = this.upgrade() { this.update(cx, |this, cx| on_release(this, entity, cx)); } }), ); activate(); subscription } /// Register a callback to for updates to the given global pub fn observe_global( &mut self, mut f: impl FnMut(&mut T, &mut Context) + 'static, ) -> Subscription where T: 'static, { let handle = self.weak_entity(); let (subscription, activate) = self.global_observers.insert( TypeId::of::(), Box::new(move |cx| handle.update(cx, |view, cx| f(view, cx)).is_ok()), ); self.defer(move |_| activate()); subscription } /// Register a callback to be invoked when the application is about to restart. pub fn on_app_restart( &self, mut on_restart: impl FnMut(&mut T, &mut App) + 'static, ) -> Subscription where T: 'static, { let handle = self.weak_entity(); self.app.on_app_restart(move |cx| { handle.update(cx, |entity, cx| on_restart(entity, cx)).ok(); }) } /// Arrange for the given function to be invoked whenever the application is quit. /// The future returned from this callback will be polled for up to [crate::SHUTDOWN_TIMEOUT] until the app fully quits. pub fn on_app_quit( &self, mut on_quit: impl FnMut(&mut T, &mut Context) -> Fut + 'static, ) -> Subscription where Fut: 'static + Future, T: 'static, { let handle = self.weak_entity(); self.app.on_app_quit(move |cx| { let future = handle.update(cx, |entity, cx| on_quit(entity, cx)).ok(); async move { if let Some(future) = future { future.await; } } .boxed_local() }) } /// Tell GPUI that this entity has changed and observers of it should be notified. pub fn notify(&mut self) { self.app.notify(self.entity_state.entity_id); } /// Spawn the future returned by the given function. /// The function is provided a weak handle to the entity owned by this context and a context that can be held across await points. /// The returned task must be held or detached. #[track_caller] pub fn spawn(&self, f: AsyncFn) -> Task where T: 'static, AsyncFn: AsyncFnOnce(WeakEntity, &mut AsyncApp) -> R + 'static, R: 'static, { let this = self.weak_entity(); self.app.spawn(async move |cx| f(this, cx).await) } /// Convenience method for accessing view state in an event callback. /// /// Many GPUI callbacks take the form of `Fn(&E, &mut Window, &mut App)`, /// but it's often useful to be able to access view state in these /// callbacks. This method provides a convenient way to do so. pub fn listener( &self, f: impl Fn(&mut T, &E, &mut Window, &mut Context) + 'static, ) -> impl Fn(&E, &mut Window, &mut App) + 'static { let view = self.entity().downgrade(); move |e: &E, window: &mut Window, cx: &mut App| { view.update(cx, |view, cx| f(view, e, window, cx)).ok(); } } /// Convenience method for producing view state in a closure. /// See `listener` for more details. pub fn processor( &self, f: impl Fn(&mut T, E, &mut Window, &mut Context) -> R + 'static, ) -> impl Fn(E, &mut Window, &mut App) -> R + 'static { let view = self.entity(); move |e: E, window: &mut Window, cx: &mut App| { view.update(cx, |view, cx| f(view, e, window, cx)) } } /// Run something using this entity and cx, when the returned struct is dropped pub fn on_drop( &self, f: impl FnOnce(&mut T, &mut Context) + 'static, ) -> Deferred { let this = self.weak_entity(); let mut cx = self.to_async(); util::defer(move || { this.update(&mut cx, f).ok(); }) } /// Focus the given view in the given window. View type is required to implement Focusable. pub fn focus_view(&mut self, view: &Entity, window: &mut Window) { window.focus(&view.focus_handle(self)); } /// Sets a given callback to be run on the next frame. pub fn on_next_frame( &self, window: &mut Window, f: impl FnOnce(&mut T, &mut Window, &mut Context) + 'static, ) where T: 'static, { let view = self.entity(); window.on_next_frame(move |window, cx| view.update(cx, |view, cx| f(view, window, cx))); } /// 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_in( &mut self, window: &Window, f: impl FnOnce(&mut T, &mut Window, &mut Context) + 'static, ) { let view = self.entity(); window.defer(self, move |window, cx| { view.update(cx, |view, cx| f(view, window, cx)) }); } /// Observe another entity for changes to its state, as tracked by [`Context::notify`]. pub fn observe_in( &mut self, observed: &Entity, window: &mut Window, mut on_notify: impl FnMut(&mut T, Entity, &mut Window, &mut Context) + 'static, ) -> Subscription where V2: 'static, T: 'static, { let observed_id = observed.entity_id(); let observed = observed.downgrade(); let window_handle = window.handle; let observer = self.weak_entity(); self.new_observer( observed_id, Box::new(move |cx| { window_handle .update(cx, |_, window, cx| { if let Some((observer, observed)) = observer.upgrade().zip(observed.upgrade()) { observer.update(cx, |observer, cx| { on_notify(observer, observed, window, cx); }); true } else { false } }) .unwrap_or(false) }), ) } /// Subscribe to events emitted by another entity. /// The entity to which you're subscribing must implement the [`EventEmitter`] trait. /// The callback will be invoked with a reference to the current view, a handle to the emitting `Entity`, the event, a mutable reference to the `Window`, and the context for the entity. pub fn subscribe_in( &mut self, emitter: &Entity, window: &Window, mut on_event: impl FnMut(&mut T, &Entity, &Evt, &mut Window, &mut Context) + 'static, ) -> Subscription where Emitter: EventEmitter, Evt: 'static, { let emitter = emitter.downgrade(); let window_handle = window.handle; let subscriber = self.weak_entity(); self.new_subscription( emitter.entity_id(), ( TypeId::of::(), Box::new(move |event, cx| { window_handle .update(cx, |_, window, cx| { if let Some((subscriber, emitter)) = subscriber.upgrade().zip(emitter.upgrade()) { let event = event.downcast_ref().expect("invalid event type"); subscriber.update(cx, |subscriber, cx| { on_event(subscriber, &emitter, event, window, cx); }); true } else { false } }) .unwrap_or(false) }), ), ) } /// Register a callback to be invoked when the view is released. /// /// The callback receives a handle to the view's window. This handle may be /// invalid, if the window was closed before the view was released. pub fn on_release_in( &mut self, window: &Window, on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static, ) -> Subscription { let entity = self.entity(); self.app.observe_release_in(&entity, window, on_release) } /// Register a callback to be invoked when the given Entity is released. pub fn observe_release_in( &self, observed: &Entity, window: &Window, mut on_release: impl FnMut(&mut T, &mut T2, &mut Window, &mut Context) + 'static, ) -> Subscription where T: 'static, T2: 'static, { let observer = self.weak_entity(); self.app .observe_release_in(observed, window, move |observed, window, cx| { observer .update(cx, |observer, cx| { on_release(observer, observed, window, cx) }) .ok(); }) } /// Register a callback to be invoked when the window is resized. pub fn observe_window_bounds( &self, window: &mut Window, mut callback: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, ) -> Subscription { let view = self.weak_entity(); let (subscription, activate) = window.bounds_observers.insert( (), Box::new(move |window, cx| { view.update(cx, |view, cx| callback(view, window, cx)) .is_ok() }), ); activate(); subscription } /// Register a callback to be invoked when the window is activated or deactivated. pub fn observe_window_activation( &self, window: &mut Window, mut callback: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, ) -> Subscription { let view = self.weak_entity(); let (subscription, activate) = window.activation_observers.insert( (), Box::new(move |window, cx| { view.update(cx, |view, cx| callback(view, window, cx)) .is_ok() }), ); activate(); subscription } /// Registers a callback to be invoked when the window appearance changes. pub fn observe_window_appearance( &self, window: &mut Window, mut callback: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, ) -> Subscription { let view = self.weak_entity(); let (subscription, activate) = window.appearance_observers.insert( (), Box::new(move |window, cx| { view.update(cx, |view, cx| callback(view, window, cx)) .is_ok() }), ); activate(); subscription } /// 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(&mut T, &KeystrokeEvent, &mut Window, &mut Context) + 'static, ) -> Subscription { fn inner( keystroke_observers: &SubscriberSet<(), KeystrokeObserver>, handler: KeystrokeObserver, ) -> Subscription { let (subscription, activate) = keystroke_observers.insert((), handler); activate(); subscription } let view = self.weak_entity(); inner( &self.keystroke_observers, Box::new(move |event, window, cx| { if let Some(view) = view.upgrade() { view.update(cx, |view, cx| f(view, event, window, cx)); true } else { false } }), ) } /// Register a callback to be invoked when the window's pending input changes. pub fn observe_pending_input( &self, window: &mut Window, mut callback: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, ) -> Subscription { let view = self.weak_entity(); let (subscription, activate) = window.pending_input_observers.insert( (), Box::new(move |window, cx| { view.update(cx, |view, cx| callback(view, window, cx)) .is_ok() }), ); activate(); subscription } /// Register a listener to be called when the given focus handle receives focus. /// Returns a subscription and persists until the subscription is dropped. pub fn on_focus( &mut self, handle: &FocusHandle, window: &mut Window, mut listener: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, ) -> Subscription { let view = self.weak_entity(); let focus_id = handle.id; let (subscription, activate) = window.new_focus_listener(Box::new(move |event, window, cx| { view.update(cx, |view, cx| { if event.previous_focus_path.last() != Some(&focus_id) && event.current_focus_path.last() == Some(&focus_id) { listener(view, window, cx) } }) .is_ok() })); self.defer(|_| activate()); subscription } /// Register a listener to be called when the given focus handle or one of its descendants receives focus. /// This does not fire if the given focus handle - or one of its descendants - was previously focused. /// Returns a subscription and persists until the subscription is dropped. pub fn on_focus_in( &mut self, handle: &FocusHandle, window: &mut Window, mut listener: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, ) -> Subscription { let view = self.weak_entity(); let focus_id = handle.id; let (subscription, activate) = window.new_focus_listener(Box::new(move |event, window, cx| { view.update(cx, |view, cx| { if event.is_focus_in(focus_id) { listener(view, window, cx) } }) .is_ok() })); self.defer(|_| activate()); subscription } /// Register a listener to be called when the given focus handle loses focus. /// Returns a subscription and persists until the subscription is dropped. pub fn on_blur( &mut self, handle: &FocusHandle, window: &mut Window, mut listener: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, ) -> Subscription { let view = self.weak_entity(); let focus_id = handle.id; let (subscription, activate) = window.new_focus_listener(Box::new(move |event, window, cx| { view.update(cx, |view, cx| { if event.previous_focus_path.last() == Some(&focus_id) && event.current_focus_path.last() != Some(&focus_id) { listener(view, window, cx) } }) .is_ok() })); self.defer(|_| activate()); subscription } /// Register a listener to be called when nothing in the window has focus. /// This typically happens when the node that was focused is removed from the tree, /// and this callback lets you chose a default place to restore the users focus. /// Returns a subscription and persists until the subscription is dropped. pub fn on_focus_lost( &mut self, window: &mut Window, mut listener: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, ) -> Subscription { let view = self.weak_entity(); let (subscription, activate) = window.focus_lost_listeners.insert( (), Box::new(move |window, cx| { view.update(cx, |view, cx| listener(view, window, cx)) .is_ok() }), ); self.defer(|_| activate()); subscription } /// Register a listener to be called when the given focus handle or one of its descendants loses focus. /// Returns a subscription and persists until the subscription is dropped. pub fn on_focus_out( &mut self, handle: &FocusHandle, window: &mut Window, mut listener: impl FnMut(&mut T, FocusOutEvent, &mut Window, &mut Context) + 'static, ) -> Subscription { let view = self.weak_entity(); let focus_id = handle.id; let (subscription, activate) = window.new_focus_listener(Box::new(move |event, window, cx| { view.update(cx, |view, cx| { if let Some(blurred_id) = event.previous_focus_path.last().copied() && event.is_focus_out(focus_id) { let event = FocusOutEvent { blurred: WeakFocusHandle { id: blurred_id, handles: Arc::downgrade(&cx.focus_handles), }, }; listener(view, event, window, cx) } }) .is_ok() })); self.defer(|_| activate()); subscription } /// Schedule a future to be run asynchronously. /// The given callback is invoked with a [`WeakEntity`] to avoid leaking the entity for a long-running process. /// It's also given an [`AsyncWindowContext`], which can be used to access the state of the entity across await points. /// The returned future will be polled on the main thread. #[track_caller] pub fn spawn_in(&self, window: &Window, f: AsyncFn) -> Task where R: 'static, AsyncFn: AsyncFnOnce(WeakEntity, &mut AsyncWindowContext) -> R + 'static, { let view = self.weak_entity(); window.spawn(self, async move |cx| f(view, cx).await) } /// Register a callback to be invoked when the given global state changes. pub fn observe_global_in( &mut self, window: &Window, mut f: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, ) -> Subscription { let window_handle = window.handle; let view = self.weak_entity(); let (subscription, activate) = self.global_observers.insert( TypeId::of::(), Box::new(move |cx| { window_handle .update(cx, |_, window, cx| { view.update(cx, |view, cx| f(view, window, cx)).is_ok() }) .unwrap_or(false) }), ); self.defer(move |_| activate()); subscription } /// Register a callback to be invoked when the given Action type is dispatched to the window. pub fn on_action( &mut self, action_type: TypeId, window: &mut Window, listener: impl Fn(&mut T, &dyn Any, DispatchPhase, &mut Window, &mut Context) + 'static, ) { let handle = self.weak_entity(); window.on_action(action_type, move |action, phase, window, cx| { handle .update(cx, |view, cx| { listener(view, action, phase, window, cx); }) .ok(); }); } /// Move focus to the current view, assuming it implements [`Focusable`]. pub fn focus_self(&mut self, window: &mut Window) where T: Focusable, { let view = self.entity(); window.defer(self, move |window, cx| { view.read(cx).focus_handle(cx).focus(window) }) } } impl Context<'_, T> { /// Emit an event of the specified type, which can be handled by other entities that have subscribed via `subscribe` methods on their respective contexts. pub fn emit(&mut self, event: Evt) where T: EventEmitter, Evt: 'static, { self.app.pending_effects.push_back(Effect::Emit { emitter: self.entity_state.entity_id, event_type: TypeId::of::(), event: Box::new(event), }); } } impl AppContext for Context<'_, T> { type Result = U; fn new(&mut self, build_entity: impl FnOnce(&mut Context) -> U) -> Entity { self.app.new(build_entity) } fn reserve_entity(&mut self) -> Reservation { self.app.reserve_entity() } fn insert_entity( &mut self, reservation: Reservation, build_entity: impl FnOnce(&mut Context) -> U, ) -> Self::Result> { self.app.insert_entity(reservation, build_entity) } fn update_entity( &mut self, handle: &Entity, update: impl FnOnce(&mut U, &mut Context) -> R, ) -> R { self.app.update_entity(handle, update) } fn as_mut<'a, E>(&'a mut self, handle: &Entity) -> Self::Result> where E: 'static, { self.app.as_mut(handle) } fn read_entity( &self, handle: &Entity, read: impl FnOnce(&U, &App) -> R, ) -> Self::Result where U: 'static, { self.app.read_entity(handle, read) } fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result where F: FnOnce(AnyView, &mut Window, &mut App) -> R, { self.app.update_window(window, update) } fn read_window( &self, window: &WindowHandle, read: impl FnOnce(Entity, &App) -> R, ) -> Result where U: 'static, { self.app.read_window(window, read) } fn background_spawn(&self, future: impl Future + Send + 'static) -> Task where R: Send + 'static, { self.app.background_executor.spawn(future) } fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result where G: Global, { self.app.read_global(callback) } } impl Borrow for Context<'_, T> { fn borrow(&self) -> &App { self.app } } impl BorrowMut for Context<'_, T> { fn borrow_mut(&mut self) -> &mut App { self.app } }