Detailed changes
@@ -9,15 +9,15 @@ use refineable::Refineable;
use crate::{
current_platform, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor, LayoutId,
- MainThread, MainThreadOnly, Platform, PlatformDisplayLinker, RootView, SvgRenderer, Task,
- TextStyle, TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
+ MainThread, MainThreadOnly, Platform, PlatformDisplayLinker, RootView, SubscriberSet,
+ SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, Window, WindowContext,
+ WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, VecDeque};
use futures::Future;
use parking_lot::Mutex;
use slotmap::SlotMap;
-use smallvec::SmallVec;
use std::{
any::{type_name, Any, TypeId},
mem,
@@ -67,8 +67,8 @@ impl App {
entities,
windows: SlotMap::with_key(),
pending_effects: Default::default(),
- observers: Default::default(),
- event_handlers: Default::default(),
+ observers: SubscriberSet::new(),
+ event_handlers: SubscriberSet::new(),
layout_id_buffer: Default::default(),
})
}))
@@ -88,9 +88,8 @@ impl App {
}
}
-type Handlers = SmallVec<[Arc<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>; 2]>;
-type EventHandlers =
- SmallVec<[Arc<dyn Fn(&dyn Any, &mut AppContext) -> bool + Send + Sync + 'static>; 2]>;
+type Handler = Arc<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>;
+type EventHandler = Arc<dyn Fn(&dyn Any, &mut AppContext) -> bool + Send + Sync + 'static>;
type FrameCallback = Box<dyn FnOnce(&mut WindowContext) + Send>;
pub struct AppContext {
@@ -109,8 +108,8 @@ pub struct AppContext {
pub(crate) entities: EntityMap,
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
pub(crate) pending_effects: VecDeque<Effect>,
- pub(crate) observers: HashMap<EntityId, Handlers>,
- pub(crate) event_handlers: HashMap<EntityId, EventHandlers>,
+ pub(crate) observers: SubscriberSet<EntityId, Handler>,
+ pub(crate) event_handlers: SubscriberSet<EntityId, EventHandler>,
pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
}
@@ -176,23 +175,15 @@ impl AppContext {
}
fn apply_notify_effect(&mut self, updated_entity: EntityId) {
- if let Some(mut handlers) = self.observers.remove(&updated_entity) {
- handlers.retain(|handler| handler(self));
- if let Some(new_handlers) = self.observers.remove(&updated_entity) {
- handlers.extend(new_handlers);
- }
- self.observers.insert(updated_entity, handlers);
- }
+ self.observers
+ .clone()
+ .retain(&updated_entity, |handler| handler(self));
}
fn apply_emit_effect(&mut self, updated_entity: EntityId, event: Box<dyn Any>) {
- if let Some(mut handlers) = self.event_handlers.remove(&updated_entity) {
- handlers.retain(|handler| handler(&event, self));
- if let Some(new_handlers) = self.event_handlers.remove(&updated_entity) {
- handlers.extend(new_handlers);
- }
- self.event_handlers.insert(updated_entity, handlers);
- }
+ self.event_handlers
+ .clone()
+ .retain(&updated_entity, |handler| handler(&event, self));
}
pub fn to_async(&self) -> AsyncAppContext {
@@ -1,4 +1,7 @@
-use crate::{AppContext, Context, Effect, EntityId, EventEmitter, Handle, Reference, WeakHandle};
+use crate::{
+ AppContext, Context, Effect, EntityId, EventEmitter, Handle, Reference, Subscription,
+ WeakHandle,
+};
use std::{marker::PhantomData, sync::Arc};
pub struct ModelContext<'a, T> {
@@ -42,21 +45,20 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
&mut self,
handle: &Handle<E>,
on_notify: impl Fn(&mut T, Handle<E>, &mut ModelContext<'_, T>) + Send + Sync + 'static,
- ) {
+ ) -> Subscription {
let this = self.handle();
let handle = handle.downgrade();
- self.app
- .observers
- .entry(handle.id)
- .or_default()
- .push(Arc::new(move |cx| {
+ self.app.observers.insert(
+ handle.id,
+ Arc::new(move |cx| {
if let Some((this, handle)) = this.upgrade(cx).zip(handle.upgrade(cx)) {
this.update(cx, |this, cx| on_notify(this, handle, cx));
true
} else {
false
}
- }));
+ }),
+ )
}
pub fn subscribe<E: EventEmitter + Send + Sync + 'static>(
@@ -66,14 +68,12 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
+ Send
+ Sync
+ 'static,
- ) {
+ ) -> Subscription {
let this = self.handle();
let handle = handle.downgrade();
- self.app
- .event_handlers
- .entry(handle.id)
- .or_default()
- .push(Arc::new(move |event, cx| {
+ self.app.event_handlers.insert(
+ handle.id,
+ Arc::new(move |event, cx| {
let event = event.downcast_ref().expect("invalid event type");
if let Some((this, handle)) = this.upgrade(cx).zip(handle.upgrade(cx)) {
this.update(cx, |this, cx| on_event(this, handle, event, cx));
@@ -81,7 +81,8 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
} else {
false
}
- }));
+ }),
+ )
}
pub fn notify(&mut self) {
@@ -13,6 +13,7 @@ mod scene;
mod style;
mod style_helpers;
mod styled;
+mod subscription;
mod svg_renderer;
mod taffy;
mod text_system;
@@ -42,6 +43,7 @@ pub use smol::Timer;
pub use style::*;
pub use style_helpers::*;
pub use styled::*;
+pub use subscription::*;
pub use svg_renderer::*;
pub use taffy::{AvailableSpace, LayoutId};
pub use text_system::*;
@@ -0,0 +1,103 @@
+use collections::{BTreeMap, BTreeSet};
+use parking_lot::Mutex;
+use std::{fmt::Debug, mem, sync::Arc};
+use util::post_inc;
+
+#[derive(Clone)]
+pub(crate) struct SubscriberSet<EmitterKey, Callback>(
+ Arc<Mutex<SubscriberSetState<EmitterKey, Callback>>>,
+);
+
+struct SubscriberSetState<EmitterKey, Callback> {
+ subscribers: BTreeMap<EmitterKey, BTreeMap<usize, Callback>>,
+ dropped_subscribers: BTreeSet<(EmitterKey, usize)>,
+ next_subscriber_id: usize,
+}
+
+impl<EmitterKey, Callback> SubscriberSet<EmitterKey, Callback>
+where
+ EmitterKey: 'static + Ord + Clone + Debug,
+ Callback: 'static,
+{
+ pub fn new() -> Self {
+ Self(Arc::new(Mutex::new(SubscriberSetState {
+ subscribers: Default::default(),
+ dropped_subscribers: Default::default(),
+ next_subscriber_id: 0,
+ })))
+ }
+
+ pub fn insert(&self, emitter: 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())
+ .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) {
+ subscribers.remove(&subscriber_id);
+ if subscribers.is_empty() {
+ lock.subscribers.remove(&emitter);
+ return;
+ }
+ }
+
+ // 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));
+ })),
+ }
+ }
+
+ pub fn retain<F>(&self, emitter: &EmitterKey, mut f: F)
+ where
+ F: FnMut(&mut Callback) -> bool,
+ {
+ let entry = self.0.lock().subscribers.remove_entry(emitter);
+ if let Some((emitter, mut subscribers)) = entry {
+ subscribers.retain(|_, callback| f(callback));
+ let mut lock = self.0.lock();
+
+ // Add any new subscribers that were added while invoking the callback.
+ if let Some(new_subscribers) = lock.subscribers.remove(&emitter) {
+ subscribers.extend(new_subscribers);
+ }
+
+ // Remove any dropped subscriptions that were dropped while invoking the callback.
+ for (dropped_emitter, dropped_subscription_id) in
+ mem::take(&mut lock.dropped_subscribers)
+ {
+ debug_assert_eq!(emitter, dropped_emitter);
+ subscribers.remove(&dropped_subscription_id);
+ }
+
+ if !subscribers.is_empty() {
+ lock.subscribers.insert(emitter, subscribers);
+ }
+ }
+ }
+}
+
+#[must_use]
+pub struct Subscription {
+ unsubscribe: Option<Box<dyn FnOnce()>>,
+}
+
+impl Subscription {
+ pub fn detach(mut self) {
+ self.unsubscribe.take();
+ }
+}
+
+impl Drop for Subscription {
+ fn drop(&mut self) {
+ if let Some(unsubscribe) = self.unsubscribe.take() {
+ unsubscribe();
+ }
+ }
+}
@@ -5,8 +5,8 @@ use crate::{
LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels,
PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams,
RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
- Style, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, WindowOptions,
- SUBPIXEL_VARIANTS,
+ Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle,
+ WindowOptions, SUBPIXEL_VARIANTS,
};
use anyhow::Result;
use collections::HashMap;
@@ -903,15 +903,13 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
&mut self,
handle: &Handle<E>,
on_notify: impl Fn(&mut S, Handle<E>, &mut ViewContext<'_, '_, S>) + Send + Sync + 'static,
- ) {
+ ) -> Subscription {
let this = self.handle();
let handle = handle.downgrade();
let window_handle = self.window.handle;
- self.app
- .observers
- .entry(handle.id)
- .or_default()
- .push(Arc::new(move |cx| {
+ self.app.observers.insert(
+ handle.id,
+ Arc::new(move |cx| {
cx.update_window(window_handle.id, |cx| {
if let Some(handle) = handle.upgrade(cx) {
this.update(cx, |this, cx| on_notify(this, handle, cx))
@@ -921,7 +919,8 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
}
})
.unwrap_or(false)
- }));
+ }),
+ )
}
pub fn subscribe<E: EventEmitter + Send + Sync + 'static>(
@@ -931,15 +930,13 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
+ Send
+ Sync
+ 'static,
- ) {
+ ) -> Subscription {
let this = self.handle();
let handle = handle.downgrade();
let window_handle = self.window.handle;
- self.app
- .event_handlers
- .entry(handle.id)
- .or_default()
- .push(Arc::new(move |event, cx| {
+ self.app.event_handlers.insert(
+ handle.id,
+ Arc::new(move |event, cx| {
cx.update_window(window_handle.id, |cx| {
if let Some(handle) = handle.upgrade(cx) {
let event = event.downcast_ref().expect("invalid event type");
@@ -950,7 +947,8 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
}
})
.unwrap_or(false)
- }));
+ }),
+ )
}
pub fn notify(&mut self) {