diff --git a/crates/feature_flags2/src/feature_flags2.rs b/crates/feature_flags2/src/feature_flags2.rs index 671205553d..d5f10ea587 100644 --- a/crates/feature_flags2/src/feature_flags2.rs +++ b/crates/feature_flags2/src/feature_flags2.rs @@ -25,16 +25,18 @@ impl FeatureFlag for ChannelsAlpha { pub trait FeatureFlagViewExt { fn observe_flag(&mut self, callback: F) -> Subscription where - F: Fn(bool, &mut V, &mut ViewContext) + 'static; + F: Fn(bool, &mut V, &mut ViewContext) + Send + Sync + 'static; } -impl FeatureFlagViewExt for ViewContext<'_, '_, V> { +impl FeatureFlagViewExt for ViewContext<'_, '_, V> +where + V: 'static + Send + Sync, +{ fn observe_flag(&mut self, callback: F) -> Subscription where - F: Fn(bool, &mut V, &mut ViewContext) + 'static, + F: Fn(bool, &mut V, &mut ViewContext) + Send + Sync + 'static, { - self.observe_global::(move |v, cx| { - let feature_flags = cx.global::(); + self.observe_global::(move |v, feature_flags, cx| { callback(feature_flags.has_flag(::NAME), v, cx); }) } @@ -49,16 +51,14 @@ pub trait FeatureFlagAppExt { impl FeatureFlagAppExt for AppContext { fn update_flags(&mut self, staff: bool, flags: Vec) { - self.update_default_global::(|feature_flags, _| { - feature_flags.staff = staff; - feature_flags.flags = flags; - }) + let feature_flags = self.default_global::(); + feature_flags.staff = staff; + feature_flags.flags = flags; } fn set_staff(&mut self, staff: bool) { - self.update_default_global::(|feature_flags, _| { - feature_flags.staff = staff; - }) + let feature_flags = self.default_global::(); + feature_flags.staff = staff; } fn has_flag(&self) -> bool { diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index a3b4615445..516cdbc46a 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -11,7 +11,7 @@ use smallvec::SmallVec; use crate::{ current_platform, image_cache::ImageCache, Action, AppMetadata, AssetSource, Context, 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, WindowHandle, WindowId, }; @@ -75,18 +75,20 @@ impl App { svg_renderer: SvgRenderer::new(asset_source), image_cache: ImageCache::new(http_client), text_style_stack: Vec::new(), - global_stacks_by_type: HashMap::default(), + globals_by_type: HashMap::default(), unit_entity, entities, windows: SlotMap::with_key(), keymap: Arc::new(RwLock::new(Keymap::default())), global_action_listeners: HashMap::default(), action_builders: HashMap::default(), - pending_notifications: Default::default(), - pending_effects: Default::default(), + pending_effects: VecDeque::new(), + pending_notifications: HashSet::default(), + pending_global_notifications: HashSet::default(), observers: SubscriberSet::new(), - event_handlers: SubscriberSet::new(), - release_handlers: SubscriberSet::new(), + event_listeners: SubscriberSet::new(), + release_listeners: SubscriberSet::new(), + global_observers: SubscriberSet::new(), layout_id_buffer: Default::default(), propagate_event: true, }) @@ -154,8 +156,8 @@ impl App { } type Handler = Box bool + Send + Sync + 'static>; -type EventHandler = Box bool + Send + Sync + 'static>; -type ReleaseHandler = Box; +type Listener = Box bool + Send + Sync + 'static>; +type ReleaseListener = Box; type FrameCallback = Box; type ActionBuilder = fn(json: Option) -> anyhow::Result>; @@ -171,7 +173,7 @@ pub struct AppContext { pub(crate) svg_renderer: SvgRenderer, pub(crate) image_cache: ImageCache, pub(crate) text_style_stack: Vec, - pub(crate) global_stacks_by_type: HashMap>>, + pub(crate) globals_by_type: HashMap>, pub(crate) unit_entity: Handle<()>, pub(crate) entities: EntityMap, pub(crate) windows: SlotMap>, @@ -179,11 +181,13 @@ pub struct AppContext { pub(crate) global_action_listeners: HashMap>>, action_builders: HashMap, - pub(crate) pending_notifications: HashSet, pending_effects: VecDeque, + pub(crate) pending_notifications: HashSet, + pub(crate) pending_global_notifications: HashSet, pub(crate) observers: SubscriberSet, - pub(crate) event_handlers: SubscriberSet, - pub(crate) release_handlers: SubscriberSet, + pub(crate) event_listeners: SubscriberSet, + pub(crate) release_listeners: SubscriberSet, + pub(crate) global_observers: SubscriberSet, pub(crate) layout_id_buffer: Vec, // We recycle this memory across layout requests. pub(crate) propagate_event: bool, } @@ -194,7 +198,7 @@ impl AppContext { } pub fn refresh(&mut self) { - self.push_effect(Effect::Refresh); + self.pending_effects.push_back(Effect::Refresh); } pub(crate) fn update(&mut self, update: impl FnOnce(&mut Self) -> R) -> R { @@ -250,14 +254,19 @@ impl AppContext { pub(crate) fn push_effect(&mut self, effect: Effect) { match &effect { Effect::Notify { emitter } => { - if self.pending_notifications.insert(*emitter) { - self.pending_effects.push_back(effect); + if !self.pending_notifications.insert(*emitter) { + return; } } - Effect::Emit { .. } => self.pending_effects.push_back(effect), - Effect::FocusChanged { .. } => self.pending_effects.push_back(effect), - Effect::Refresh => self.pending_effects.push_back(effect), - } + Effect::NotifyGlobalObservers { global_type } => { + if !self.pending_global_notifications.insert(*global_type) { + return; + } + } + _ => {} + }; + + self.pending_effects.push_back(effect); } fn flush_effects(&mut self) { @@ -266,13 +275,18 @@ impl AppContext { 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::Notify { emitter } => { + self.apply_notify_effect(emitter); + } Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event), Effect::FocusChanged { window_id, focused } => { - self.apply_focus_changed(window_id, focused) + self.apply_focus_changed_effect(window_id, focused); } Effect::Refresh => { - self.apply_refresh(); + self.apply_refresh_effect(); + } + Effect::NotifyGlobalObservers { global_type } => { + self.apply_notify_global_observers_effect(global_type); } } } else { @@ -307,8 +321,8 @@ impl AppContext { for (entity_id, mut entity) in dropped { self.observers.remove(&entity_id); - self.event_handlers.remove(&entity_id); - for release_callback in self.release_handlers.remove(&entity_id) { + self.event_listeners.remove(&entity_id); + for release_callback in self.release_listeners.remove(&entity_id) { release_callback(&mut entity, self); } } @@ -348,12 +362,12 @@ impl AppContext { } fn apply_emit_effect(&mut self, emitter: EntityId, event: Box) { - self.event_handlers + self.event_listeners .clone() .retain(&emitter, |handler| handler(&event, self)); } - fn apply_focus_changed(&mut self, window_id: WindowId, focused: Option) { + fn apply_focus_changed_effect(&mut self, window_id: WindowId, focused: Option) { self.update_window(window_id, |cx| { if cx.window.focus == focused { let mut listeners = mem::take(&mut cx.window.focus_listeners); @@ -379,7 +393,7 @@ impl AppContext { .ok(); } - fn apply_refresh(&mut self) { + fn apply_refresh_effect(&mut self) { for window in self.windows.values_mut() { if let Some(window) = window.as_mut() { 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 { AsyncAppContext { app: unsafe { mem::transmute(self.this.clone()) }, @@ -460,94 +483,89 @@ impl AppContext { } pub fn has_global(&self) -> bool { - self.global_stacks_by_type - .get(&TypeId::of::()) - .map_or(false, |stack| !stack.is_empty()) + self.globals_by_type.contains_key(&TypeId::of::()) } pub fn global(&self) -> &G { - self.global_stacks_by_type + self.globals_by_type .get(&TypeId::of::()) - .and_then(|stack| stack.last()) .map(|any_state| any_state.downcast_ref::().unwrap()) .ok_or_else(|| anyhow!("no state of type {} exists", type_name::())) .unwrap() } pub fn try_global(&self) -> Option<&G> { - self.global_stacks_by_type + self.globals_by_type .get(&TypeId::of::()) - .and_then(|stack| stack.last()) .map(|any_state| any_state.downcast_ref::().unwrap()) } pub fn global_mut(&mut self) -> &mut G { - self.global_stacks_by_type - .get_mut(&TypeId::of::()) - .and_then(|stack| stack.last_mut()) + 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() } pub fn default_global(&mut self) -> &mut G { - let stack = self - .global_stacks_by_type - .entry(TypeId::of::()) - .or_default(); - if stack.is_empty() { - stack.push(Box::new(G::default())); - } - stack.last_mut().unwrap().downcast_mut::().unwrap() + let global_type = TypeId::of::(); + self.push_effect(Effect::NotifyGlobalObservers { global_type }); + self.globals_by_type + .insert(global_type, Box::new(G::default())); + self.globals_by_type + .get_mut(&global_type) + .unwrap() + .downcast_mut::() + .unwrap() } pub fn set_global(&mut self, global: T) { - let global = Box::new(global); - let stack = self - .global_stacks_by_type - .entry(TypeId::of::()) - .or_default(); - if let Some(last) = stack.last_mut() { - *last = global; - } else { - stack.push(global) - } + let global_type = TypeId::of::(); + self.push_effect(Effect::NotifyGlobalObservers { global_type }); + self.globals_by_type.insert(global_type, Box::new(global)); } pub fn update_global(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R where G: 'static + Send + Sync, { - let mut global = self - .global_stacks_by_type - .get_mut(&TypeId::of::()) - .and_then(|stack| stack.pop()) - .ok_or_else(|| anyhow!("no state of type {} exists", type_name::())) - .unwrap(); - let result = f(global.downcast_mut().unwrap(), self); - self.global_stacks_by_type - .get_mut(&TypeId::of::()) - .unwrap() - .push(global); + let mut global = self.lease_global::(); + let result = f(global.as_mut(), self); + self.restore_global(global); result } - pub(crate) fn push_global(&mut self, global: T) { - self.global_stacks_by_type - .entry(TypeId::of::()) - .or_default() - .push(Box::new(global)); + pub fn observe_global( + &mut self, + f: impl Fn(&G, &mut Self) + Send + Sync + 'static, + ) -> Subscription { + self.global_observers.insert( + TypeId::of::(), + Box::new(move |global, cx| { + f(global.downcast_ref::().unwrap(), cx); + true + }), + ) } - pub(crate) fn pop_global(&mut self) -> Box { - self.global_stacks_by_type - .get_mut(&TypeId::of::()) - .and_then(|stack| stack.pop()) - .expect("state stack underflow") + pub(crate) fn lease_global(&mut self) -> Box { + self.globals_by_type + .remove(&TypeId::of::()) + .ok_or_else(|| anyhow!("no global registered of type {}", type_name::())) + .unwrap() .downcast() .unwrap() } + pub(crate) fn restore_global(&mut self, global: Box) { + let global_type = TypeId::of::(); + 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) { self.text_style_stack.push(text_style); } @@ -558,7 +576,7 @@ impl AppContext { pub fn bind_keys(&mut self, bindings: impl IntoIterator) { self.keymap.write().add_bindings(bindings); - self.push_effect(Effect::Refresh); + self.pending_effects.push_back(Effect::Refresh); } pub fn on_action( @@ -711,6 +729,9 @@ pub(crate) enum Effect { focused: Option, }, Refresh, + NotifyGlobalObservers { + global_type: TypeId, + }, } #[cfg(test)] diff --git a/crates/gpui2/src/app/model_context.rs b/crates/gpui2/src/app/model_context.rs index 2e01f664b4..5d2c01e295 100644 --- a/crates/gpui2/src/app/model_context.rs +++ b/crates/gpui2/src/app/model_context.rs @@ -53,7 +53,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> { ) -> Subscription { let this = self.handle(); let handle = handle.downgrade(); - self.app.event_handlers.insert( + self.app.event_listeners.insert( handle.id, Box::new(move |event, cx| { let event = event.downcast_ref().expect("invalid event type"); @@ -71,7 +71,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> { &mut self, on_release: impl Fn(&mut T, &mut AppContext) + Send + Sync + 'static, ) -> Subscription { - self.app.release_handlers.insert( + self.app.release_listeners.insert( self.entity_id, Box::new(move |this, cx| { 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, ) -> Subscription { let this = self.handle(); - self.app.release_handlers.insert( + self.app.release_listeners.insert( handle.id, Box::new(move |entity, cx| { 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) { - self.app.push_effect(Effect::Notify { - emitter: self.entity_id, - }); + if self.app.pending_notifications.insert(self.entity_id) { + self.app.pending_effects.push_back(Effect::Notify { + emitter: self.entity_id, + }); + } } pub fn update_global(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R where G: 'static + Send + Sync, { - let mut global = self.app.pop_global::(); + let mut global = self.app.lease_global::(); let result = f(global.as_mut(), self); - self.app.push_global(global); + self.app.restore_global(global); result } @@ -128,7 +130,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> { impl<'a, T: EventEmitter + Send + Sync + 'static> ModelContext<'a, T> { 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, event: Box::new(event), }); diff --git a/crates/gpui2/src/gpui2.rs b/crates/gpui2/src/gpui2.rs index 701f1c0f83..c3bbd1b2e9 100644 --- a/crates/gpui2/src/gpui2.rs +++ b/crates/gpui2/src/gpui2.rs @@ -153,16 +153,6 @@ pub trait BorrowAppContext { result } - fn with_global(&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::(); - result - } - fn set_global(&mut self, global: T) { self.app_mut().set_global(global) } diff --git a/crates/gpui2/src/subscription.rs b/crates/gpui2/src/subscription.rs index 73ce4b253d..884a28649b 100644 --- a/crates/gpui2/src/subscription.rs +++ b/crates/gpui2/src/subscription.rs @@ -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 subscriber_id = post_inc(&mut lock.next_subscriber_id); lock.subscribers - .entry(emitter.clone()) + .entry(emitter_key.clone()) .or_default() .insert(subscriber_id, callback); let this = self.0.clone(); Subscription { unsubscribe: Some(Box::new(move || { 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); if subscribers.is_empty() { - lock.subscribers.remove(&emitter); + lock.subscribers.remove(&emitter_key); return; } } @@ -54,7 +54,8 @@ where // 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 // later. - lock.dropped_subscribers.insert((emitter, subscriber_id)); + lock.dropped_subscribers + .insert((emitter_key, subscriber_id)); })), } } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index fc281c537e..b4ef23edc5 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -325,7 +325,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { let window_id = self.window.handle.id; self.window.focus = Some(handle.id); - self.push_effect(Effect::FocusChanged { + self.app.push_effect(Effect::FocusChanged { window_id, focused: Some(handle.id), }); @@ -339,7 +339,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { let window_id = self.window.handle.id; self.window.focus = None; - self.push_effect(Effect::FocusChanged { + self.app.push_effect(Effect::FocusChanged { window_id, focused: None, }); @@ -430,9 +430,10 @@ impl<'a, 'w> WindowContext<'a, 'w> { where G: 'static + Send + Sync, { - let mut global = self.app.pop_global::(); + let global_type = TypeId::of::(); + let mut global = self.app.lease_global::(); let result = f(global.as_mut(), self); - self.app.push_global(global); + self.app.set_global(global); result } @@ -1017,6 +1018,20 @@ impl<'a, 'w> WindowContext<'a, 'w> { key_match } + pub fn observe_global( + &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::(), + Box::new(move |global, cx| { + let global = global.downcast_ref::().unwrap(); + cx.update_window(window_id, |cx| f(global, cx)).is_ok() + }), + ) + } + fn dispatch_action( &mut self, action: Box, @@ -1364,7 +1379,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { let this = self.handle(); let handle = handle.downgrade(); let window_handle = self.window.handle; - self.app.event_handlers.insert( + self.app.event_listeners.insert( handle.id, Box::new(move |event, 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, ) -> Subscription { let window_handle = self.window.handle; - self.app.release_handlers.insert( + self.app.release_listeners.insert( self.entity_id, Box::new(move |this, cx| { 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 { let this = self.handle(); let window_handle = self.window.handle; - self.app.release_handlers.insert( + self.app.release_listeners.insert( handle.id, Box::new(move |entity, cx| { 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 G: 'static + Send + Sync, { - let mut global = self.app.pop_global::(); + let mut global = self.app.lease_global::(); let result = f(global.as_mut(), self); - self.app.push_global(global); + self.app.restore_global(global); result } + pub fn observe_global( + &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::(), + Box::new(move |global, cx| { + let global = global.downcast_ref::().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( &mut self, handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext) + 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> { - pub fn emit(&mut self, event: S::Event) { - self.window_cx.app.push_effect(Effect::Emit { - emitter: self.entity_id, +impl<'a, 'w, V: EventEmitter + Send + Sync + 'static> ViewContext<'a, 'w, V> { + pub fn emit(&mut self, event: V::Event) { + let emitter = self.entity_id; + self.app.push_effect(Effect::Emit { + emitter, event: Box::new(event), }); }