use crate::{
AnyView, AnyWindowHandle, AppContext, AsyncApp, DispatchPhase, Effect, EntityId, EventEmitter,
FocusHandle, FocusOutEvent, Focusable, Global, KeystrokeObserver, Priority, Reservation,
SubscriberSet, Subscription, Task, WeakEntity, WeakFocusHandle, Window, WindowHandle,
};
use anyhow::Result;
use futures::FutureExt;
use std::{
any::{Any, TypeId},
borrow::{Borrow, BorrowMut},
future::Future,
ops,
sync::Arc,
};
use util::Deferred;
use super::{App, AsyncWindowContext, Entity, KeystrokeEvent};
pub struct Context<'a, T> {
app: &'a mut App,
entity_state: WeakEntity<T>,
}
impl<'a, T> ops::Deref for Context<'a, T> {
type Target = App;
fn deref(&self) -> &Self::Target {
self.app
}
}
impl<'a, T> ops::DerefMut for Context<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.app
}
}
impl<'a, T: 'static> Context<'a, T> {
pub(crate) fn new_context(app: &'a mut App, entity_state: WeakEntity<T>) -> Self {
Self { app, entity_state }
}
pub fn entity_id(&self) -> EntityId {
self.entity_state.entity_id
}
pub fn entity(&self) -> Entity<T> {
self.weak_entity()
.upgrade()
.expect("The entity must be alive if we have a entity context")
}
pub fn weak_entity(&self) -> WeakEntity<T> {
self.entity_state.clone()
}
pub fn observe<W>(
&mut self,
entity: &Entity<W>,
mut on_notify: impl FnMut(&mut T, Entity<W>, &mut Context<T>) + '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
}
})
}
pub fn observe_self(
&mut self,
mut on_event: impl FnMut(&mut T, &mut Context<T>) + 'static,
) -> Subscription
where
T: 'static,
{
let this = self.entity();
self.app.observe(&this, move |this, cx| {
this.update(cx, |this, cx| on_event(this, cx))
})
}
pub fn subscribe<T2, Evt>(
&mut self,
entity: &Entity<T2>,
mut on_event: impl FnMut(&mut T, Entity<T2>, &Evt, &mut Context<T>) + 'static,
) -> Subscription
where
T: 'static,
T2: 'static + EventEmitter<Evt>,
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
}
})
}
pub fn subscribe_self<Evt>(
&mut self,
mut on_event: impl FnMut(&mut T, &Evt, &mut Context<T>) + 'static,
) -> Subscription
where
T: 'static + EventEmitter<Evt>,
Evt: 'static,
{
let this = self.entity();
self.app.subscribe(&this, move |this, evt, cx| {
this.update(cx, |this, cx| on_event(this, evt, cx))
})
}
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
}
pub fn observe_release<T2>(
&self,
entity: &Entity<T2>,
on_release: impl FnOnce(&mut T, &mut T2, &mut Context<T>) + '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
}
pub fn observe_global<G: 'static>(
&mut self,
mut f: impl FnMut(&mut T, &mut Context<T>) + 'static,
) -> Subscription
where
T: 'static,
{
let handle = self.weak_entity();
let (subscription, activate) = self.global_observers.insert(
TypeId::of::<G>(),
Box::new(move |cx| handle.update(cx, |view, cx| f(view, cx)).is_ok()),
);
self.defer(move |_| activate());
subscription
}
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();
})
}
pub fn on_app_quit<Fut>(
&self,
mut on_quit: impl FnMut(&mut T, &mut Context<T>) -> Fut + 'static,
) -> Subscription
where
Fut: 'static + Future<Output = ()>,
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()
})
}
pub fn notify(&mut self) {
self.app.notify(self.entity_state.entity_id);
}
#[track_caller]
pub fn spawn<AsyncFn, R>(&self, f: AsyncFn) -> Task<R>
where
T: 'static,
AsyncFn: AsyncFnOnce(WeakEntity<T>, &mut AsyncApp) -> R + 'static,
R: 'static,
{
let this = self.weak_entity();
self.app.spawn(async move |cx| f(this, cx).await)
}
pub fn listener<E: ?Sized>(
&self,
f: impl Fn(&mut T, &E, &mut Window, &mut Context<T>) + '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();
}
}
pub fn processor<E, R>(
&self,
f: impl Fn(&mut T, E, &mut Window, &mut Context<T>) -> 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))
}
}
pub fn on_drop(
&self,
f: impl FnOnce(&mut T, &mut Context<T>) + 'static,
) -> Deferred<impl FnOnce()> {
let this = self.weak_entity();
let mut cx = self.to_async();
util::defer(move || {
this.update(&mut cx, f).ok();
})
}
pub fn focus_view<W: Focusable>(&mut self, view: &Entity<W>, window: &mut Window) {
window.focus(&view.focus_handle(self), self);
}
pub fn on_next_frame(
&self,
window: &mut Window,
f: impl FnOnce(&mut T, &mut Window, &mut Context<T>) + 'static,
) where
T: 'static,
{
let view = self.entity();
window.on_next_frame(move |window, cx| view.update(cx, |view, cx| f(view, window, cx)));
}
pub fn defer_in(
&mut self,
window: &Window,
f: impl FnOnce(&mut T, &mut Window, &mut Context<T>) + 'static,
) {
let view = self.entity();
window.defer(self, move |window, cx| {
view.update(cx, |view, cx| f(view, window, cx))
});
}
pub fn observe_in<V2>(
&mut self,
observed: &Entity<V2>,
window: &mut Window,
mut on_notify: impl FnMut(&mut T, Entity<V2>, &mut Window, &mut Context<T>) + '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)
}),
)
}
pub fn subscribe_in<Emitter, Evt>(
&mut self,
emitter: &Entity<Emitter>,
window: &Window,
mut on_event: impl FnMut(&mut T, &Entity<Emitter>, &Evt, &mut Window, &mut Context<T>) + 'static,
) -> Subscription
where
Emitter: EventEmitter<Evt>,
Evt: 'static,
{
let emitter = emitter.downgrade();
let window_handle = window.handle;
let subscriber = self.weak_entity();
self.new_subscription(
emitter.entity_id(),
(
TypeId::of::<Evt>(),
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)
}),
),
)
}
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)
}
pub fn observe_release_in<T2>(
&self,
observed: &Entity<T2>,
window: &Window,
mut on_release: impl FnMut(&mut T, &mut T2, &mut Window, &mut Context<T>) + '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();
})
}
pub fn observe_window_bounds(
&self,
window: &mut Window,
mut callback: impl FnMut(&mut T, &mut Window, &mut Context<T>) + '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
}
pub fn observe_window_activation(
&self,
window: &mut Window,
mut callback: impl FnMut(&mut T, &mut Window, &mut Context<T>) + '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
}
pub fn observe_window_appearance(
&self,
window: &mut Window,
mut callback: impl FnMut(&mut T, &mut Window, &mut Context<T>) + '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
}
pub fn observe_keystrokes(
&mut self,
mut f: impl FnMut(&mut T, &KeystrokeEvent, &mut Window, &mut Context<T>) + '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
}
}),
)
}
pub fn observe_pending_input(
&self,
window: &mut Window,
mut callback: impl FnMut(&mut T, &mut Window, &mut Context<T>) + '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
}
pub fn on_focus(
&mut self,
handle: &FocusHandle,
window: &mut Window,
mut listener: impl FnMut(&mut T, &mut Window, &mut Context<T>) + '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
}
pub fn on_focus_in(
&mut self,
handle: &FocusHandle,
window: &mut Window,
mut listener: impl FnMut(&mut T, &mut Window, &mut Context<T>) + '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
}
pub fn on_blur(
&mut self,
handle: &FocusHandle,
window: &mut Window,
mut listener: impl FnMut(&mut T, &mut Window, &mut Context<T>) + '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
}
pub fn on_focus_lost(
&mut self,
window: &mut Window,
mut listener: impl FnMut(&mut T, &mut Window, &mut Context<T>) + '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
}
pub fn on_focus_out(
&mut self,
handle: &FocusHandle,
window: &mut Window,
mut listener: impl FnMut(&mut T, FocusOutEvent, &mut Window, &mut Context<T>) + '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
}
#[track_caller]
pub fn spawn_in<AsyncFn, R>(&self, window: &Window, f: AsyncFn) -> Task<R>
where
R: 'static,
AsyncFn: AsyncFnOnce(WeakEntity<T>, &mut AsyncWindowContext) -> R + 'static,
{
let view = self.weak_entity();
window.spawn(self, async move |cx| f(view, cx).await)
}
#[track_caller]
pub fn spawn_in_with_priority<AsyncFn, R>(
&self,
priority: Priority,
window: &Window,
f: AsyncFn,
) -> Task<R>
where
R: 'static,
AsyncFn: AsyncFnOnce(WeakEntity<T>, &mut AsyncWindowContext) -> R + 'static,
{
let view = self.weak_entity();
window.spawn_with_priority(priority, self, async move |cx| f(view, cx).await)
}
pub fn observe_global_in<G: Global>(
&mut self,
window: &Window,
mut f: impl FnMut(&mut T, &mut Window, &mut Context<T>) + 'static,
) -> Subscription {
let window_handle = window.handle;
let view = self.weak_entity();
let (subscription, activate) = self.global_observers.insert(
TypeId::of::<G>(),
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
}
pub fn on_action(
&mut self,
action_type: TypeId,
window: &mut Window,
listener: impl Fn(&mut T, &dyn Any, DispatchPhase, &mut Window, &mut Context<T>) + '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();
});
}
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, cx)
})
}
}
impl<T> Context<'_, T> {
pub fn emit<Evt>(&mut self, event: Evt)
where
T: EventEmitter<Evt>,
Evt: 'static,
{
self.app.pending_effects.push_back(Effect::Emit {
emitter: self.entity_state.entity_id,
event_type: TypeId::of::<Evt>(),
event: Box::new(event),
});
}
}
impl<T> AppContext for Context<'_, T> {
type Result<U> = U;
#[inline]
fn new<U: 'static>(&mut self, build_entity: impl FnOnce(&mut Context<U>) -> U) -> Entity<U> {
self.app.new(build_entity)
}
#[inline]
fn reserve_entity<U: 'static>(&mut self) -> Reservation<U> {
self.app.reserve_entity()
}
#[inline]
fn insert_entity<U: 'static>(
&mut self,
reservation: Reservation<U>,
build_entity: impl FnOnce(&mut Context<U>) -> U,
) -> Self::Result<Entity<U>> {
self.app.insert_entity(reservation, build_entity)
}
#[inline]
fn update_entity<U: 'static, R>(
&mut self,
handle: &Entity<U>,
update: impl FnOnce(&mut U, &mut Context<U>) -> R,
) -> R {
self.app.update_entity(handle, update)
}
#[inline]
fn as_mut<'a, E>(&'a mut self, handle: &Entity<E>) -> Self::Result<super::GpuiBorrow<'a, E>>
where
E: 'static,
{
self.app.as_mut(handle)
}
#[inline]
fn read_entity<U, R>(
&self,
handle: &Entity<U>,
read: impl FnOnce(&U, &App) -> R,
) -> Self::Result<R>
where
U: 'static,
{
self.app.read_entity(handle, read)
}
#[inline]
fn update_window<R, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<R>
where
F: FnOnce(AnyView, &mut Window, &mut App) -> R,
{
self.app.update_window(window, update)
}
#[inline]
fn read_window<U, R>(
&self,
window: &WindowHandle<U>,
read: impl FnOnce(Entity<U>, &App) -> R,
) -> Result<R>
where
U: 'static,
{
self.app.read_window(window, read)
}
#[inline]
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
where
R: Send + 'static,
{
self.app.background_executor.spawn(future)
}
#[inline]
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
where
G: Global,
{
self.app.read_global(callback)
}
}
impl<T> Borrow<App> for Context<'_, T> {
fn borrow(&self) -> &App {
self.app
}
}
impl<T> BorrowMut<App> for Context<'_, T> {
fn borrow_mut(&mut self) -> &mut App {
self.app
}
}