This commit is contained in:
Antonio Scandurra 2023-10-22 18:01:00 +02:00
parent 50bbdd5cab
commit db6a3e1783
6 changed files with 174 additions and 126 deletions

View file

@ -25,16 +25,18 @@ impl FeatureFlag for ChannelsAlpha {
pub trait FeatureFlagViewExt<V: 'static> { pub trait FeatureFlagViewExt<V: 'static> {
fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
where where
F: Fn(bool, &mut V, &mut ViewContext<V>) + 'static; F: Fn(bool, &mut V, &mut ViewContext<V>) + Send + Sync + 'static;
} }
impl<V: 'static> FeatureFlagViewExt<V> for ViewContext<'_, '_, V> { impl<V> FeatureFlagViewExt<V> for ViewContext<'_, '_, V>
where
V: 'static + Send + Sync,
{
fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
where where
F: Fn(bool, &mut V, &mut ViewContext<V>) + 'static, F: Fn(bool, &mut V, &mut ViewContext<V>) + Send + Sync + 'static,
{ {
self.observe_global::<FeatureFlags, _>(move |v, cx| { self.observe_global::<FeatureFlags>(move |v, feature_flags, cx| {
let feature_flags = cx.global::<FeatureFlags>();
callback(feature_flags.has_flag(<T as FeatureFlag>::NAME), v, cx); callback(feature_flags.has_flag(<T as FeatureFlag>::NAME), v, cx);
}) })
} }
@ -49,16 +51,14 @@ pub trait FeatureFlagAppExt {
impl FeatureFlagAppExt for AppContext { impl FeatureFlagAppExt for AppContext {
fn update_flags(&mut self, staff: bool, flags: Vec<String>) { fn update_flags(&mut self, staff: bool, flags: Vec<String>) {
self.update_default_global::<FeatureFlags, _, _>(|feature_flags, _| { let feature_flags = self.default_global::<FeatureFlags>();
feature_flags.staff = staff; feature_flags.staff = staff;
feature_flags.flags = flags; feature_flags.flags = flags;
})
} }
fn set_staff(&mut self, staff: bool) { fn set_staff(&mut self, staff: bool) {
self.update_default_global::<FeatureFlags, _, _>(|feature_flags, _| { let feature_flags = self.default_global::<FeatureFlags>();
feature_flags.staff = staff; feature_flags.staff = staff;
})
} }
fn has_flag<T: FeatureFlag>(&self) -> bool { fn has_flag<T: FeatureFlag>(&self) -> bool {

View file

@ -11,7 +11,7 @@ use smallvec::SmallVec;
use crate::{ use crate::{
current_platform, image_cache::ImageCache, Action, AppMetadata, AssetSource, Context, current_platform, image_cache::ImageCache, Action, AppMetadata, AssetSource, Context,
DispatchPhase, DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap, DispatchPhase, DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap,
LayoutId, MainThread, MainThreadOnly, Platform, SemanticVersion, SharedString, SubscriberSet, LayoutId, MainThread, MainThreadOnly, Platform, SharedString, SubscriberSet, Subscription,
SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext,
WindowHandle, WindowId, WindowHandle, WindowId,
}; };
@ -75,18 +75,20 @@ impl App {
svg_renderer: SvgRenderer::new(asset_source), svg_renderer: SvgRenderer::new(asset_source),
image_cache: ImageCache::new(http_client), image_cache: ImageCache::new(http_client),
text_style_stack: Vec::new(), text_style_stack: Vec::new(),
global_stacks_by_type: HashMap::default(), globals_by_type: HashMap::default(),
unit_entity, unit_entity,
entities, entities,
windows: SlotMap::with_key(), windows: SlotMap::with_key(),
keymap: Arc::new(RwLock::new(Keymap::default())), keymap: Arc::new(RwLock::new(Keymap::default())),
global_action_listeners: HashMap::default(), global_action_listeners: HashMap::default(),
action_builders: HashMap::default(), action_builders: HashMap::default(),
pending_notifications: Default::default(), pending_effects: VecDeque::new(),
pending_effects: Default::default(), pending_notifications: HashSet::default(),
pending_global_notifications: HashSet::default(),
observers: SubscriberSet::new(), observers: SubscriberSet::new(),
event_handlers: SubscriberSet::new(), event_listeners: SubscriberSet::new(),
release_handlers: SubscriberSet::new(), release_listeners: SubscriberSet::new(),
global_observers: SubscriberSet::new(),
layout_id_buffer: Default::default(), layout_id_buffer: Default::default(),
propagate_event: true, propagate_event: true,
}) })
@ -154,8 +156,8 @@ impl App {
} }
type Handler = Box<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>; type Handler = Box<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>;
type EventHandler = Box<dyn Fn(&dyn Any, &mut AppContext) -> bool + Send + Sync + 'static>; type Listener = Box<dyn Fn(&dyn Any, &mut AppContext) -> bool + Send + Sync + 'static>;
type ReleaseHandler = Box<dyn Fn(&mut dyn Any, &mut AppContext) + Send + Sync + 'static>; type ReleaseListener = Box<dyn Fn(&mut dyn Any, &mut AppContext) + Send + Sync + 'static>;
type FrameCallback = Box<dyn FnOnce(&mut WindowContext) + Send>; type FrameCallback = Box<dyn FnOnce(&mut WindowContext) + Send>;
type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>; type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
@ -171,7 +173,7 @@ pub struct AppContext {
pub(crate) svg_renderer: SvgRenderer, pub(crate) svg_renderer: SvgRenderer,
pub(crate) image_cache: ImageCache, pub(crate) image_cache: ImageCache,
pub(crate) text_style_stack: Vec<TextStyleRefinement>, pub(crate) text_style_stack: Vec<TextStyleRefinement>,
pub(crate) global_stacks_by_type: HashMap<TypeId, Vec<Box<dyn Any + Send + Sync>>>, pub(crate) globals_by_type: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
pub(crate) unit_entity: Handle<()>, pub(crate) unit_entity: Handle<()>,
pub(crate) entities: EntityMap, pub(crate) entities: EntityMap,
pub(crate) windows: SlotMap<WindowId, Option<Window>>, pub(crate) windows: SlotMap<WindowId, Option<Window>>,
@ -179,11 +181,13 @@ pub struct AppContext {
pub(crate) global_action_listeners: pub(crate) global_action_listeners:
HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self) + Send + Sync>>>, HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self) + Send + Sync>>>,
action_builders: HashMap<SharedString, ActionBuilder>, action_builders: HashMap<SharedString, ActionBuilder>,
pub(crate) pending_notifications: HashSet<EntityId>,
pending_effects: VecDeque<Effect>, pending_effects: VecDeque<Effect>,
pub(crate) pending_notifications: HashSet<EntityId>,
pub(crate) pending_global_notifications: HashSet<TypeId>,
pub(crate) observers: SubscriberSet<EntityId, Handler>, pub(crate) observers: SubscriberSet<EntityId, Handler>,
pub(crate) event_handlers: SubscriberSet<EntityId, EventHandler>, pub(crate) event_listeners: SubscriberSet<EntityId, Listener>,
pub(crate) release_handlers: SubscriberSet<EntityId, ReleaseHandler>, pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>,
pub(crate) global_observers: SubscriberSet<TypeId, Listener>,
pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests. pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
pub(crate) propagate_event: bool, pub(crate) propagate_event: bool,
} }
@ -194,7 +198,7 @@ impl AppContext {
} }
pub fn refresh(&mut self) { pub fn refresh(&mut self) {
self.push_effect(Effect::Refresh); self.pending_effects.push_back(Effect::Refresh);
} }
pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R { pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
@ -250,15 +254,20 @@ impl AppContext {
pub(crate) fn push_effect(&mut self, effect: Effect) { pub(crate) fn push_effect(&mut self, effect: Effect) {
match &effect { match &effect {
Effect::Notify { emitter } => { Effect::Notify { emitter } => {
if self.pending_notifications.insert(*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); self.pending_effects.push_back(effect);
} }
}
Effect::Emit { .. } => self.pending_effects.push_back(effect),
Effect::FocusChanged { .. } => self.pending_effects.push_back(effect),
Effect::Refresh => self.pending_effects.push_back(effect),
}
}
fn flush_effects(&mut self) { fn flush_effects(&mut self) {
loop { loop {
@ -266,13 +275,18 @@ impl AppContext {
self.release_dropped_focus_handles(); self.release_dropped_focus_handles();
if let Some(effect) = self.pending_effects.pop_front() { if let Some(effect) = self.pending_effects.pop_front() {
match effect { match effect {
Effect::Notify { emitter } => self.apply_notify_effect(emitter), Effect::Notify { emitter } => {
self.apply_notify_effect(emitter);
}
Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event), Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event),
Effect::FocusChanged { window_id, focused } => { Effect::FocusChanged { window_id, focused } => {
self.apply_focus_changed(window_id, focused) self.apply_focus_changed_effect(window_id, focused);
} }
Effect::Refresh => { Effect::Refresh => {
self.apply_refresh(); self.apply_refresh_effect();
}
Effect::NotifyGlobalObservers { global_type } => {
self.apply_notify_global_observers_effect(global_type);
} }
} }
} else { } else {
@ -307,8 +321,8 @@ impl AppContext {
for (entity_id, mut entity) in dropped { for (entity_id, mut entity) in dropped {
self.observers.remove(&entity_id); self.observers.remove(&entity_id);
self.event_handlers.remove(&entity_id); self.event_listeners.remove(&entity_id);
for release_callback in self.release_handlers.remove(&entity_id) { for release_callback in self.release_listeners.remove(&entity_id) {
release_callback(&mut entity, self); release_callback(&mut entity, self);
} }
} }
@ -348,12 +362,12 @@ impl AppContext {
} }
fn apply_emit_effect(&mut self, emitter: EntityId, event: Box<dyn Any>) { fn apply_emit_effect(&mut self, emitter: EntityId, event: Box<dyn Any>) {
self.event_handlers self.event_listeners
.clone() .clone()
.retain(&emitter, |handler| handler(&event, self)); .retain(&emitter, |handler| handler(&event, self));
} }
fn apply_focus_changed(&mut self, window_id: WindowId, focused: Option<FocusId>) { fn apply_focus_changed_effect(&mut self, window_id: WindowId, focused: Option<FocusId>) {
self.update_window(window_id, |cx| { self.update_window(window_id, |cx| {
if cx.window.focus == focused { if cx.window.focus == focused {
let mut listeners = mem::take(&mut cx.window.focus_listeners); let mut listeners = mem::take(&mut cx.window.focus_listeners);
@ -379,7 +393,7 @@ impl AppContext {
.ok(); .ok();
} }
fn apply_refresh(&mut self) { fn apply_refresh_effect(&mut self) {
for window in self.windows.values_mut() { for window in self.windows.values_mut() {
if let Some(window) = window.as_mut() { if let Some(window) = window.as_mut() {
window.dirty = true; window.dirty = true;
@ -387,6 +401,15 @@ impl AppContext {
} }
} }
fn apply_notify_global_observers_effect(&mut self, type_id: TypeId) {
self.pending_global_notifications.insert(type_id);
let global = self.globals_by_type.remove(&type_id).unwrap();
self.global_observers
.clone()
.retain(&type_id, |observer| observer(global.as_ref(), self));
self.globals_by_type.insert(type_id, global);
}
pub fn to_async(&self) -> AsyncAppContext { pub fn to_async(&self) -> AsyncAppContext {
AsyncAppContext { AsyncAppContext {
app: unsafe { mem::transmute(self.this.clone()) }, app: unsafe { mem::transmute(self.this.clone()) },
@ -460,94 +483,89 @@ impl AppContext {
} }
pub fn has_global<G: 'static>(&self) -> bool { pub fn has_global<G: 'static>(&self) -> bool {
self.global_stacks_by_type self.globals_by_type.contains_key(&TypeId::of::<G>())
.get(&TypeId::of::<G>())
.map_or(false, |stack| !stack.is_empty())
} }
pub fn global<G: 'static>(&self) -> &G { pub fn global<G: 'static>(&self) -> &G {
self.global_stacks_by_type self.globals_by_type
.get(&TypeId::of::<G>()) .get(&TypeId::of::<G>())
.and_then(|stack| stack.last())
.map(|any_state| any_state.downcast_ref::<G>().unwrap()) .map(|any_state| any_state.downcast_ref::<G>().unwrap())
.ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>())) .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
.unwrap() .unwrap()
} }
pub fn try_global<G: 'static>(&self) -> Option<&G> { pub fn try_global<G: 'static>(&self) -> Option<&G> {
self.global_stacks_by_type self.globals_by_type
.get(&TypeId::of::<G>()) .get(&TypeId::of::<G>())
.and_then(|stack| stack.last())
.map(|any_state| any_state.downcast_ref::<G>().unwrap()) .map(|any_state| any_state.downcast_ref::<G>().unwrap())
} }
pub fn global_mut<G: 'static>(&mut self) -> &mut G { pub fn global_mut<G: 'static>(&mut self) -> &mut G {
self.global_stacks_by_type let global_type = TypeId::of::<G>();
.get_mut(&TypeId::of::<G>()) self.push_effect(Effect::NotifyGlobalObservers { global_type });
.and_then(|stack| stack.last_mut()) self.globals_by_type
.get_mut(&global_type)
.and_then(|any_state| any_state.downcast_mut::<G>()) .and_then(|any_state| any_state.downcast_mut::<G>())
.ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>())) .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
.unwrap() .unwrap()
} }
pub fn default_global<G: 'static + Default + Sync + Send>(&mut self) -> &mut G { pub fn default_global<G: 'static + Default + Sync + Send>(&mut self) -> &mut G {
let stack = self let global_type = TypeId::of::<G>();
.global_stacks_by_type self.push_effect(Effect::NotifyGlobalObservers { global_type });
.entry(TypeId::of::<G>()) self.globals_by_type
.or_default(); .insert(global_type, Box::new(G::default()));
if stack.is_empty() { self.globals_by_type
stack.push(Box::new(G::default())); .get_mut(&global_type)
} .unwrap()
stack.last_mut().unwrap().downcast_mut::<G>().unwrap() .downcast_mut::<G>()
.unwrap()
} }
pub fn set_global<T: Send + Sync + 'static>(&mut self, global: T) { pub fn set_global<T: Send + Sync + 'static>(&mut self, global: T) {
let global = Box::new(global); let global_type = TypeId::of::<T>();
let stack = self self.push_effect(Effect::NotifyGlobalObservers { global_type });
.global_stacks_by_type self.globals_by_type.insert(global_type, Box::new(global));
.entry(TypeId::of::<T>())
.or_default();
if let Some(last) = stack.last_mut() {
*last = global;
} else {
stack.push(global)
}
} }
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
where where
G: 'static + Send + Sync, G: 'static + Send + Sync,
{ {
let mut global = self let mut global = self.lease_global::<G>();
.global_stacks_by_type let result = f(global.as_mut(), self);
.get_mut(&TypeId::of::<G>()) self.restore_global(global);
.and_then(|stack| stack.pop())
.ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
.unwrap();
let result = f(global.downcast_mut().unwrap(), self);
self.global_stacks_by_type
.get_mut(&TypeId::of::<G>())
.unwrap()
.push(global);
result result
} }
pub(crate) fn push_global<T: Send + Sync + 'static>(&mut self, global: T) { pub fn observe_global<G: 'static>(
self.global_stacks_by_type &mut self,
.entry(TypeId::of::<T>()) f: impl Fn(&G, &mut Self) + Send + Sync + 'static,
.or_default() ) -> Subscription {
.push(Box::new(global)); self.global_observers.insert(
TypeId::of::<G>(),
Box::new(move |global, cx| {
f(global.downcast_ref::<G>().unwrap(), cx);
true
}),
)
} }
pub(crate) fn pop_global<T: 'static>(&mut self) -> Box<T> { pub(crate) fn lease_global<G: 'static + Send + Sync>(&mut self) -> Box<G> {
self.global_stacks_by_type self.globals_by_type
.get_mut(&TypeId::of::<T>()) .remove(&TypeId::of::<G>())
.and_then(|stack| stack.pop()) .ok_or_else(|| anyhow!("no global registered of type {}", type_name::<G>()))
.expect("state stack underflow") .unwrap()
.downcast() .downcast()
.unwrap() .unwrap()
} }
pub(crate) fn restore_global<G: 'static + Send + Sync>(&mut self, global: Box<G>) {
let global_type = TypeId::of::<G>();
self.push_effect(Effect::NotifyGlobalObservers { global_type });
self.globals_by_type.insert(global_type, global);
}
pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) { pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) {
self.text_style_stack.push(text_style); self.text_style_stack.push(text_style);
} }
@ -558,7 +576,7 @@ impl AppContext {
pub fn bind_keys(&mut self, bindings: impl IntoIterator<Item = KeyBinding>) { pub fn bind_keys(&mut self, bindings: impl IntoIterator<Item = KeyBinding>) {
self.keymap.write().add_bindings(bindings); self.keymap.write().add_bindings(bindings);
self.push_effect(Effect::Refresh); self.pending_effects.push_back(Effect::Refresh);
} }
pub fn on_action<A: Action>( pub fn on_action<A: Action>(
@ -711,6 +729,9 @@ pub(crate) enum Effect {
focused: Option<FocusId>, focused: Option<FocusId>,
}, },
Refresh, Refresh,
NotifyGlobalObservers {
global_type: TypeId,
},
} }
#[cfg(test)] #[cfg(test)]

View file

@ -53,7 +53,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
) -> Subscription { ) -> Subscription {
let this = self.handle(); let this = self.handle();
let handle = handle.downgrade(); let handle = handle.downgrade();
self.app.event_handlers.insert( self.app.event_listeners.insert(
handle.id, handle.id,
Box::new(move |event, cx| { Box::new(move |event, cx| {
let event = event.downcast_ref().expect("invalid event type"); let event = event.downcast_ref().expect("invalid event type");
@ -71,7 +71,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
&mut self, &mut self,
on_release: impl Fn(&mut T, &mut AppContext) + Send + Sync + 'static, on_release: impl Fn(&mut T, &mut AppContext) + Send + Sync + 'static,
) -> Subscription { ) -> Subscription {
self.app.release_handlers.insert( self.app.release_listeners.insert(
self.entity_id, self.entity_id,
Box::new(move |this, cx| { Box::new(move |this, cx| {
let this = this.downcast_mut().expect("invalid entity type"); let this = this.downcast_mut().expect("invalid entity type");
@ -86,7 +86,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
on_release: impl Fn(&mut T, &mut E, &mut ModelContext<'_, T>) + Send + Sync + 'static, on_release: impl Fn(&mut T, &mut E, &mut ModelContext<'_, T>) + Send + Sync + 'static,
) -> Subscription { ) -> Subscription {
let this = self.handle(); let this = self.handle();
self.app.release_handlers.insert( self.app.release_listeners.insert(
handle.id, handle.id,
Box::new(move |entity, cx| { Box::new(move |entity, cx| {
let entity = entity.downcast_mut().expect("invalid entity type"); let entity = entity.downcast_mut().expect("invalid entity type");
@ -98,18 +98,20 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
} }
pub fn notify(&mut self) { pub fn notify(&mut self) {
self.app.push_effect(Effect::Notify { if self.app.pending_notifications.insert(self.entity_id) {
self.app.pending_effects.push_back(Effect::Notify {
emitter: self.entity_id, emitter: self.entity_id,
}); });
} }
}
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
where where
G: 'static + Send + Sync, G: 'static + Send + Sync,
{ {
let mut global = self.app.pop_global::<G>(); let mut global = self.app.lease_global::<G>();
let result = f(global.as_mut(), self); let result = f(global.as_mut(), self);
self.app.push_global(global); self.app.restore_global(global);
result result
} }
@ -128,7 +130,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
impl<'a, T: EventEmitter + Send + Sync + 'static> ModelContext<'a, T> { impl<'a, T: EventEmitter + Send + Sync + 'static> ModelContext<'a, T> {
pub fn emit(&mut self, event: T::Event) { pub fn emit(&mut self, event: T::Event) {
self.app.push_effect(Effect::Emit { self.app.pending_effects.push_back(Effect::Emit {
emitter: self.entity_id, emitter: self.entity_id,
event: Box::new(event), event: Box::new(event),
}); });

View file

@ -153,16 +153,6 @@ pub trait BorrowAppContext {
result result
} }
fn with_global<T: Send + Sync + 'static, F, R>(&mut self, global: T, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
self.app_mut().push_global(global);
let result = f(self);
self.app_mut().pop_global::<T>();
result
}
fn set_global<T: Send + Sync + 'static>(&mut self, global: T) { fn set_global<T: Send + Sync + 'static>(&mut self, global: T) {
self.app_mut().set_global(global) self.app_mut().set_global(global)
} }

View file

@ -32,21 +32,21 @@ where
}))) })))
} }
pub fn insert(&self, emitter: EmitterKey, callback: Callback) -> Subscription { pub fn insert(&self, emitter_key: EmitterKey, callback: Callback) -> Subscription {
let mut lock = self.0.lock(); let mut lock = self.0.lock();
let subscriber_id = post_inc(&mut lock.next_subscriber_id); let subscriber_id = post_inc(&mut lock.next_subscriber_id);
lock.subscribers lock.subscribers
.entry(emitter.clone()) .entry(emitter_key.clone())
.or_default() .or_default()
.insert(subscriber_id, callback); .insert(subscriber_id, callback);
let this = self.0.clone(); let this = self.0.clone();
Subscription { Subscription {
unsubscribe: Some(Box::new(move || { unsubscribe: Some(Box::new(move || {
let mut lock = this.lock(); let mut lock = this.lock();
if let Some(subscribers) = lock.subscribers.get_mut(&emitter) { if let Some(subscribers) = lock.subscribers.get_mut(&emitter_key) {
subscribers.remove(&subscriber_id); subscribers.remove(&subscriber_id);
if subscribers.is_empty() { if subscribers.is_empty() {
lock.subscribers.remove(&emitter); lock.subscribers.remove(&emitter_key);
return; return;
} }
} }
@ -54,7 +54,8 @@ where
// We didn't manage to remove the subscription, which means it was dropped // We didn't manage to remove the subscription, which means it was dropped
// while invoking the callback. Mark it as dropped so that we can remove it // while invoking the callback. Mark it as dropped so that we can remove it
// later. // later.
lock.dropped_subscribers.insert((emitter, subscriber_id)); lock.dropped_subscribers
.insert((emitter_key, subscriber_id));
})), })),
} }
} }

View file

@ -325,7 +325,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
let window_id = self.window.handle.id; let window_id = self.window.handle.id;
self.window.focus = Some(handle.id); self.window.focus = Some(handle.id);
self.push_effect(Effect::FocusChanged { self.app.push_effect(Effect::FocusChanged {
window_id, window_id,
focused: Some(handle.id), focused: Some(handle.id),
}); });
@ -339,7 +339,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
let window_id = self.window.handle.id; let window_id = self.window.handle.id;
self.window.focus = None; self.window.focus = None;
self.push_effect(Effect::FocusChanged { self.app.push_effect(Effect::FocusChanged {
window_id, window_id,
focused: None, focused: None,
}); });
@ -430,9 +430,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
where where
G: 'static + Send + Sync, G: 'static + Send + Sync,
{ {
let mut global = self.app.pop_global::<G>(); let global_type = TypeId::of::<G>();
let mut global = self.app.lease_global::<G>();
let result = f(global.as_mut(), self); let result = f(global.as_mut(), self);
self.app.push_global(global); self.app.set_global(global);
result result
} }
@ -1017,6 +1018,20 @@ impl<'a, 'w> WindowContext<'a, 'w> {
key_match key_match
} }
pub fn observe_global<G: 'static>(
&mut self,
f: impl Fn(&G, &mut WindowContext<'_, '_>) + Send + Sync + 'static,
) -> Subscription {
let window_id = self.window.handle.id;
self.global_observers.insert(
TypeId::of::<G>(),
Box::new(move |global, cx| {
let global = global.downcast_ref::<G>().unwrap();
cx.update_window(window_id, |cx| f(global, cx)).is_ok()
}),
)
}
fn dispatch_action( fn dispatch_action(
&mut self, &mut self,
action: Box<dyn Action>, action: Box<dyn Action>,
@ -1364,7 +1379,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
let this = self.handle(); let this = self.handle();
let handle = handle.downgrade(); let handle = handle.downgrade();
let window_handle = self.window.handle; let window_handle = self.window.handle;
self.app.event_handlers.insert( self.app.event_listeners.insert(
handle.id, handle.id,
Box::new(move |event, cx| { Box::new(move |event, cx| {
cx.update_window(window_handle.id, |cx| { cx.update_window(window_handle.id, |cx| {
@ -1386,7 +1401,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
on_release: impl Fn(&mut V, &mut WindowContext) + Send + Sync + 'static, on_release: impl Fn(&mut V, &mut WindowContext) + Send + Sync + 'static,
) -> Subscription { ) -> Subscription {
let window_handle = self.window.handle; let window_handle = self.window.handle;
self.app.release_handlers.insert( self.app.release_listeners.insert(
self.entity_id, self.entity_id,
Box::new(move |this, cx| { Box::new(move |this, cx| {
let this = this.downcast_mut().expect("invalid entity type"); let this = this.downcast_mut().expect("invalid entity type");
@ -1403,7 +1418,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
) -> Subscription { ) -> Subscription {
let this = self.handle(); let this = self.handle();
let window_handle = self.window.handle; let window_handle = self.window.handle;
self.app.release_handlers.insert( self.app.release_listeners.insert(
handle.id, handle.id,
Box::new(move |entity, cx| { Box::new(move |entity, cx| {
let entity = entity.downcast_mut().expect("invalid entity type"); let entity = entity.downcast_mut().expect("invalid entity type");
@ -1556,12 +1571,30 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
where where
G: 'static + Send + Sync, G: 'static + Send + Sync,
{ {
let mut global = self.app.pop_global::<G>(); let mut global = self.app.lease_global::<G>();
let result = f(global.as_mut(), self); let result = f(global.as_mut(), self);
self.app.push_global(global); self.app.restore_global(global);
result result
} }
pub fn observe_global<G: 'static>(
&mut self,
f: impl Fn(&mut V, &G, &mut ViewContext<'_, '_, V>) + Send + Sync + 'static,
) -> Subscription {
let window_id = self.window.handle.id;
let handle = self.handle();
self.global_observers.insert(
TypeId::of::<G>(),
Box::new(move |global, cx| {
let global = global.downcast_ref::<G>().unwrap();
cx.update_window(window_id, |cx| {
handle.update(cx, |view, cx| f(view, global, cx)).is_ok()
})
.unwrap_or(false)
}),
)
}
pub fn on_mouse_event<Event: 'static>( pub fn on_mouse_event<Event: 'static>(
&mut self, &mut self,
handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static, handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static,
@ -1575,10 +1608,11 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
} }
} }
impl<'a, 'w, S: EventEmitter + Send + Sync + 'static> ViewContext<'a, 'w, S> { impl<'a, 'w, V: EventEmitter + Send + Sync + 'static> ViewContext<'a, 'w, V> {
pub fn emit(&mut self, event: S::Event) { pub fn emit(&mut self, event: V::Event) {
self.window_cx.app.push_effect(Effect::Emit { let emitter = self.entity_id;
emitter: self.entity_id, self.app.push_effect(Effect::Emit {
emitter,
event: Box::new(event), event: Box::new(event),
}); });
} }