@@ -68,6 +68,7 @@ impl App {
windows: SlotMap::with_key(),
pending_effects: Default::default(),
observers: Default::default(),
+ event_handlers: Default::default(),
layout_id_buffer: Default::default(),
})
}))
@@ -88,6 +89,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 FrameCallback = Box<dyn FnOnce(&mut WindowContext) + Send>;
pub struct AppContext {
@@ -107,6 +110,7 @@ pub struct AppContext {
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) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
}
@@ -149,6 +153,7 @@ impl AppContext {
while let Some(effect) = self.pending_effects.pop_front() {
match effect {
Effect::Notify(entity_id) => self.apply_notify_effect(entity_id),
+ Effect::Emit { entity_id, event } => self.apply_emit_effect(entity_id, event),
}
}
@@ -180,6 +185,16 @@ impl AppContext {
}
}
+ 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);
+ }
+ }
+
pub fn to_async(&self) -> AsyncAppContext {
AsyncAppContext(unsafe { mem::transmute(self.this.clone()) })
}
@@ -367,6 +382,10 @@ impl MainThread<AppContext> {
pub(crate) enum Effect {
Notify(EntityId),
+ Emit {
+ entity_id: EntityId,
+ event: Box<dyn Any + Send + Sync + 'static>,
+ },
}
#[cfg(test)]
@@ -1,4 +1,4 @@
-use crate::{AppContext, Context, Effect, EntityId, Handle, Reference, WeakHandle};
+use crate::{AppContext, Context, Effect, EntityId, EventEmitter, Handle, Reference, WeakHandle};
use std::{marker::PhantomData, sync::Arc};
pub struct ModelContext<'a, T> {
@@ -59,6 +59,31 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
}));
}
+ pub fn subscribe<E: EventEmitter + Send + Sync + 'static>(
+ &mut self,
+ handle: &Handle<E>,
+ on_event: impl Fn(&mut T, Handle<E>, &E::Event, &mut ModelContext<'_, T>)
+ + Send
+ + Sync
+ + 'static,
+ ) {
+ let this = self.handle();
+ let handle = handle.downgrade();
+ self.app
+ .event_handlers
+ .entry(handle.id)
+ .or_default()
+ .push(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));
+ true
+ } else {
+ false
+ }
+ }));
+ }
+
pub fn notify(&mut self) {
self.app
.pending_effects
@@ -66,6 +91,15 @@ 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.pending_effects.push_back(Effect::Emit {
+ entity_id: self.entity_id,
+ event: Box::new(event),
+ });
+ }
+}
+
impl<'a, T: 'static> Context for ModelContext<'a, T> {
type EntityContext<'b, 'c, U: Send + Sync + 'static> = ModelContext<'b, U>;
type Result<U> = U;
@@ -1,11 +1,11 @@
use crate::{
px, size, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, BorrowAppContext,
Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, Edges, Effect, Element, EntityId,
- Event, FontId, GlobalElementId, GlyphId, Handle, Hsla, ImageData, IsZero, 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,
+ Event, EventEmitter, FontId, GlobalElementId, GlyphId, Handle, Hsla, ImageData, IsZero,
+ 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,
};
use anyhow::Result;
@@ -930,6 +930,35 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
}));
}
+ pub fn subscribe<E: EventEmitter + Send + Sync + 'static>(
+ &mut self,
+ handle: &Handle<E>,
+ on_event: impl Fn(&mut S, Handle<E>, &E::Event, &mut ViewContext<'_, '_, S>)
+ + Send
+ + Sync
+ + 'static,
+ ) {
+ 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| {
+ cx.update_window(window_handle.id, |cx| {
+ if let Some(handle) = handle.upgrade(cx) {
+ let event = event.downcast_ref().expect("invalid event type");
+ this.update(cx, |this, cx| on_event(this, handle, event, cx))
+ .is_ok()
+ } else {
+ false
+ }
+ })
+ .unwrap_or(false)
+ }));
+ }
+
pub fn notify(&mut self) {
self.window_cx.notify();
self.window_cx
@@ -983,6 +1012,16 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
}
}
+impl<'a, 'w, S: EventEmitter + Send + Sync + 'static> ViewContext<'a, 'w, S> {
+ pub fn emit(&mut self, event: S::Event) {
+ let entity_id = self.entity_id;
+ self.app.pending_effects.push_back(Effect::Emit {
+ entity_id,
+ event: Box::new(event),
+ });
+ }
+}
+
impl<'a, 'w, S> Context for ViewContext<'a, 'w, S> {
type EntityContext<'b, 'c, U: 'static + Send + Sync> = ViewContext<'b, 'c, U>;
type Result<U> = U;