Checkpoint

This commit is contained in:
Antonio Scandurra 2023-10-11 10:45:57 +02:00
parent 0fb7364235
commit eebbc807e5
4 changed files with 102 additions and 6 deletions

View file

@ -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)]

View file

@ -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;

View file

@ -152,6 +152,10 @@ pub trait BorrowAppContext {
}
}
pub trait EventEmitter {
type Event: Any + Send + Sync + 'static;
}
pub trait Flatten<T> {
fn flatten(self) -> Result<T>;
}

View file

@ -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;