From baaeb6b0279a123b1f3c3c1ab4d831000c00c1a7 Mon Sep 17 00:00:00 2001 From: shadow cat Date: Thu, 11 Dec 2025 01:21:36 -0500 Subject: [PATCH] FINALLY transition events to global; slow text sending bug tho --- core/Cargo.toml | 1 - core/src/layout/event.rs | 309 +++++++++++++++-------------- core/src/layout/painter.rs | 41 ++-- core/src/layout/primitive/layer.rs | 2 + core/src/layout/ui.rs | 49 +++-- core/src/layout/widget/data.rs | 40 ++++ core/src/layout/widget/handle.rs | 133 +++++-------- core/src/layout/widget/mod.rs | 2 + core/src/layout/widget/widgets.rs | 12 +- core/src/util/arena.rs | 8 +- core/src/util/handle.rs | 164 ++++++++++++++- core/src/util/id.rs | 54 ++++- core/src/util/refcount.rs | 6 + macro/src/lib.rs | 42 ++-- src/bin/test/main.rs | 8 +- src/default/attr.rs | 37 +++- src/default/event.rs | 16 +- src/default/mod.rs | 6 +- src/event.rs | 54 ++--- src/widget/event.rs | 0 src/widget/sense.rs | 223 ++++++--------------- 21 files changed, 650 insertions(+), 557 deletions(-) create mode 100644 core/src/layout/widget/data.rs delete mode 100644 src/widget/event.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 37b7960..a85a26d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -10,4 +10,3 @@ bytemuck ={ workspace = true } image = { workspace = true } cosmic-text = { workspace = true } fxhash = { workspace = true } - diff --git a/core/src/layout/event.rs b/core/src/layout/event.rs index a7abdb0..3a59ba0 100644 --- a/core/src/layout/event.rs +++ b/core/src/layout/event.rs @@ -1,187 +1,192 @@ -use std::{hash::Hash, rc::Rc}; - use crate::{ - layout::{Ui, UiModule, WidgetId, WidgetRef}, - util::HashMap, + layout::{LayerId, UiId, Widget, WidgetId, WidgetInstance, WidgetRef}, + util::{Handle, HashMap, RefGuardMut}, +}; +use std::{ + any::{Any, TypeId}, + ops::DerefMut, + sync::{Arc, LazyLock, Mutex}, }; -pub trait Event: Sized { - type Module: EventModule; - type Data: Clone; -} +pub trait Async: Send + Sync + 'static {} +impl Async for T {} -pub struct EventCtx<'a, Ctx, Data> { - pub ui: &'a mut Ui, - pub state: &'a mut Ctx, - pub data: Data, -} - -pub type ECtx<'a, Ctx, Data, W> = EventIdCtx<'a, Ctx, Data, W>; -pub struct EventIdCtx<'a, Ctx, Data, W: ?Sized> { - pub widget: WidgetRef, - pub ui: &'a mut Ui, - pub state: &'a mut Ctx, - pub data: Data, -} - -pub trait EventFn: Fn(EventCtx) + 'static {} -impl) + 'static, Ctx, Data> EventFn for F {} - -pub trait WidgetEventFn: Fn(EventIdCtx) + 'static {} -impl) + 'static, Ctx, Data, W: ?Sized> WidgetEventFn - for F -{ -} - -impl Ui { - pub fn register_event( - &mut self, - id: WidgetRef, - event: E, - f: impl EventFn, - ) { - self.data - .modules - .get_mut::>() - .register(id.id(), event, f); +pub trait Event: Sized + Async + Clone { + type Data<'a> = (); + type State: Sync + Send + Default = (); + #[allow(unused_variables)] + fn should_run(&self, data: &mut Self::Data<'_>) -> bool { + true } - pub fn register_widget_event( - &mut self, - id: WidgetRef, - event: E, - f: impl WidgetEventFn, - ) { - self.data - .modules - .get_mut::>() - .register(id.id(), event, move |ctx| { - f(EventIdCtx { - widget: id, - ui: ctx.ui, - state: ctx.state, - data: ctx.data, - }) - }); + /// THIS SHOULD NOT BE OVERWRITTEN; the default impl runs this event + fn run(id: WidgetRef, data: &mut Self::Data<'_>, state: &mut Ctx) { + Events::::run(id.id(), data, state); } } -pub trait DefaultEvent: Hash + Eq + 'static { - type Data: Clone; +pub trait EventAlias { + type Event: Event; + fn into_event(self) -> Self::Event; } -impl Event for E { - type Module = DefaultEventModule; - type Data = E::Data; +impl EventAlias for E { + type Event = Self; + + fn into_event(self) -> Self::Event { + self + } } -pub trait EventModule: UiModule + Default { - fn register(&mut self, id: WidgetId, event: E, f: impl EventFn); - fn run<'a>( - &mut self, - id: WidgetId, - event: E, - ) -> Option) + use<'a, Self, Ctx, E>>; +type EventData = (E, Arc EventFn::Data<'a>>>); +pub struct Events { + active: HashMap>>, + map: HashMap>>, } -type EventFnMap = HashMap>>>; -pub struct DefaultEventModule { - map: HashMap::Data>>, +pub static EVENT_TYPES: LazyLock>>> = + LazyLock::new(|| Mutex::new(HashMap::default())); + +pub trait EventManager: Any + Send + Sync { + fn remove(&mut self, id: WidgetId) -> Box; + fn draw(&mut self, ui: UiId, inst: &WidgetInstance); + fn undraw(&mut self, ui: UiId, inst: &WidgetInstance); } -impl UiModule for DefaultEventModule { - fn on_remove(&mut self, id: &WidgetId) { - for map in self.map.values_mut() { - map.remove(id); +impl EventManager for Events { + /// so... there is a deadlock if this is called in WidgetData::drop + /// and some function in here owns another WidgetId + /// so for now you need to drop the lock on self, THEN drop the contents + fn remove(&mut self, id: WidgetId) -> Box { + let contents = self.map.remove(&id); + for ui in self.active.values_mut() { + for layer in ui.values_mut() { + layer.remove(&id); + } + } + Box::new(contents) + } + fn draw(&mut self, ui: UiId, inst: &WidgetInstance) { + self.active + .entry(ui) + .or_default() + .entry(inst.layer) + .or_default() + .entry(inst.id) + .or_default(); + } + fn undraw(&mut self, ui: UiId, inst: &WidgetInstance) { + // rust pls + (|| { + self.active + .get_mut(&ui)? + .get_mut(&inst.layer)? + .remove(&inst.id); + Some(()) + })(); + } +} + +pub type UiActive = HashMap>; + +impl Events { + fn id() -> TypeId { + TypeId::of::() + } + + fn new() -> Self { + Self { + map: Default::default(), + active: Default::default(), } } -} -pub trait HashableEvent: Event + Hash + Eq + 'static {} -impl HashableEvent for E {} - -impl EventModule for DefaultEventModule { - fn register(&mut self, id: WidgetId, event: E, f: impl EventFn::Data>) { - self.map - .entry(event) - .or_default() - .entry(id) - .or_default() - .push(Rc::new(f)); + pub fn active(id: UiId) -> impl DerefMut> { + RefGuardMut::map(Self::get(), |s| s.active.entry(id).or_default()) } - fn run<'a>( - &mut self, - id: WidgetId, - event: E, - ) -> Option) + use<'a, Ctx, E>> { - if let Some(map) = self.map.get(&event) - && let Some(fs) = map.get(&id) - { - let fs = fs.clone(); - Some(move |ctx: EventCtx::Data>| { - for f in fs { - f(EventCtx { - ui: ctx.ui, - state: ctx.state, - data: ctx.data.clone(), - }) - } + fn register_( + id: WidgetRef, + event: impl Into, + f: impl for<'a> EventFn::Data<'a>>, + ) { + let Some(mut data) = id.data_mut() else { + return; + }; + data.event_managers.insert(Self::id()); + Self::get() + .map + .entry(id.id()) + .or_default() + .push((event.into(), Arc::new(f))); + } + + pub fn register( + id: WidgetRef, + event: impl Into, + f: impl for<'a> EventIdFn::Data<'a>, W>, + ) { + Self::register_(id, event, move |ctx| { + f(EventIdCtx { + widget: id, + state: ctx.state, + data: ctx.data, }) - } else { - None - } + }); } -} -impl DefaultEventModule { - pub fn run_all(&mut self, event: E, ctx: EventCtx) - where - E::Data: Clone, - { - if let Some(map) = self.map.get(&event) { - for fs in map.values() { - let fs = fs.clone(); - for f in fs { - f(EventCtx { - ui: ctx.ui, - state: ctx.state, - data: ctx.data.clone(), - }) + pub fn run(id: WidgetId, data: &mut E::Data<'_>, state: &mut Ctx) { + let s = Self::get(); + if let Some(fs) = s.map.get(&id) { + let fs = fs.clone(); + drop(s); + for (e, f) in fs { + if e.should_run(data) { + f(EventCtx { state, data }) } } } } -} -impl Default for DefaultEventModule { - fn default() -> Self { - Self { - map: Default::default(), + pub fn run_all(data: &mut E::Data<'_>, state: &mut Ctx) { + let s = Self::get(); + // TODO: should probably document that added events won't run same frame + let mut map = s.map.clone(); + drop(s); + for fs in map.values_mut() { + for (e, f) in fs { + if e.should_run(data) { + f(EventCtx { state, data }) + } + } } } + + /// this some bull + fn get<'a>() -> RefGuardMut<'a, Self> { + let any = EVENT_TYPES + .lock() + .unwrap() + .entry(Self::id()) + .or_insert_with(|| Handle::from(Self::new())) + .clone(); + unsafe { any.downcast() }.get_take_mut() + } } -impl Ui { - pub fn run_event( - &mut self, - ctx: &mut Ctx, - id: WidgetRef, - event: E, - data: E::Data, - ) { - if let Some(f) = self - .data - .modules - .get_mut::>() - .run(id.id(), event) - { - eprintln!("FIXME"); - f(EventCtx { - ui: self, - state: ctx, - data, - }); - } - } +pub struct EventCtx<'a, Ctx, Data> { + pub state: &'a mut Ctx, + pub data: &'a mut Data, } + +pub struct EventIdCtx<'a, Ctx, Data, W: ?Sized> { + pub widget: WidgetRef, + pub state: &'a mut Ctx, + pub data: &'a mut Data, +} + +pub trait EventFn: Fn(EventCtx) + Async {} +impl) + Async, Ctx, Data> EventFn for F {} + +pub trait EventIdFn: Fn(EventIdCtx) + Async {} +impl) + Async, Ctx, Data, W: ?Sized> EventIdFn for F {} diff --git a/core/src/layout/painter.rs b/core/src/layout/painter.rs index 2777612..deb2b8c 100644 --- a/core/src/layout/painter.rs +++ b/core/src/layout/painter.rs @@ -2,8 +2,8 @@ use std::{marker::Unsize, sync::mpsc::Sender}; use crate::{ layout::{ - Axis, Len, Modules, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData, - TextureHandle, Textures, UiRegion, UiVec2, Vec2, Widget, WidgetHandle, WidgetId, + Axis, LayerId, Len, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData, + TextureHandle, Textures, UiId, UiRegion, UiVec2, Vec2, Widget, WidgetHandle, WidgetId, WidgetUpdate, }, render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst}, @@ -27,13 +27,13 @@ pub struct Painter<'a, 'c> { /// context for a painter; lets you draw and redraw widgets struct PainterCtx<'a> { + ui_id: UiId, pub active: &'a mut HashMap, pub layers: &'a mut PrimitiveLayers, pub textures: &'a mut Textures, pub masks: &'a mut TrackedArena, pub text: &'a mut TextData, pub output_size: Vec2, - pub modules: &'a mut Modules, send: &'a Sender, pub cache_width: HashMap, pub cache_height: HashMap, @@ -60,32 +60,32 @@ pub struct WidgetInstance { pub children: Vec, pub resize: ResizeRef, pub mask: MaskIdx, - pub layer: usize, + pub layer: LayerId, } /// data to be stored in Ui to create PainterCtxs easily /// TODO: actually use this LMAO pub struct PainterData { + ui_id: UiId, pub active: HashMap, pub layers: PrimitiveLayers, pub textures: Textures, pub text: TextData, pub output_size: Vec2, - pub modules: Modules, pub px_dependent: HashSet, pub masks: TrackedArena, send: Sender, } impl PainterData { - pub fn new(send: Sender) -> Self { + pub fn new(ui_id: UiId, send: Sender) -> Self { Self { + ui_id, active: Default::default(), layers: Default::default(), textures: Default::default(), text: Default::default(), output_size: Default::default(), - modules: Default::default(), px_dependent: Default::default(), masks: Default::default(), send, @@ -175,7 +175,7 @@ impl<'a> PainterCtx<'a> { return finish(self, resize); } - let Some(active) = self.remove(id) else { + let Some(active) = self.remove(id, false) else { return; }; @@ -231,7 +231,7 @@ impl<'a> PainterCtx<'a> { return; } // if not, then maintain resize and track old children to remove unneeded - let active = self.remove(id).unwrap(); + let active = self.remove(id, false).unwrap(); old_children = active.children; resize = active.resize; } @@ -293,10 +293,10 @@ impl<'a> PainterCtx<'a> { } } - // update modules - for m in self.modules.iter_mut() { - m.on_draw(&instance); - } + // run events + id.map_event_managers(|m| { + m.draw(self.ui_id, &instance); + }); self.active.insert(id, instance); } @@ -307,9 +307,6 @@ impl<'a> PainterCtx<'a> { *region = region.outside(&from).within(&to); } active.region = active.region.outside(&from).within(&to); - for m in self.modules.iter_mut() { - m.on_move(active); - } // children will not be changed, so this technically should not be needed // probably need unsafe let children = active.children.clone(); @@ -319,7 +316,7 @@ impl<'a> PainterCtx<'a> { } /// NOTE: instance textures are cleared and self.textures freed - fn remove(&mut self, id: WidgetId) -> Option { + fn remove(&mut self, id: WidgetId, undraw: bool) -> Option { let mut inst = self.active.remove(&id); if let Some(inst) = &mut inst { for h in &inst.primitives { @@ -330,15 +327,17 @@ impl<'a> PainterCtx<'a> { } inst.textures.clear(); self.textures.free(); - for m in self.modules.iter_mut() { - m.on_undraw(inst); + if undraw { + id.map_event_managers(|m| { + m.undraw(self.ui_id, inst); + }); } } inst } fn remove_rec(&mut self, id: WidgetId) -> Option { - let inst = self.remove(id); + let inst = self.remove(id, true); if let Some(inst) = &inst { for c in &inst.children { self.remove_rec(*c); @@ -351,12 +350,12 @@ impl<'a> PainterCtx<'a> { impl PainterData { fn ctx(&mut self, needs_redraw: HashSet) -> PainterCtx<'_> { PainterCtx { + ui_id: self.ui_id, active: &mut self.active, layers: &mut self.layers, textures: &mut self.textures, text: &mut self.text, output_size: self.output_size, - modules: &mut self.modules, masks: &mut self.masks, send: &self.send, cache_width: Default::default(), diff --git a/core/src/layout/primitive/layer.rs b/core/src/layout/primitive/layer.rs index c0a0603..93bee08 100644 --- a/core/src/layout/primitive/layer.rs +++ b/core/src/layout/primitive/layer.rs @@ -2,6 +2,8 @@ use std::ops::{Index, IndexMut}; use crate::render::{MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst, Primitives}; +pub type LayerId = usize; + struct LayerNode { next: Ptr, prev: Ptr, diff --git a/core/src/layout/ui.rs b/core/src/layout/ui.rs index 09ca882..0c142ba 100644 --- a/core/src/layout/ui.rs +++ b/core/src/layout/ui.rs @@ -5,21 +5,27 @@ use crate::{ IdLike, PainterData, PixelRegion, TextureHandle, Vec2, WidgetHandle, WidgetId, WidgetInstance, WidgetLike, WidgetUpdate, }, - util::HashSet, + util::{HashMap, HashSet, Id, StaticIdTracker}, }; use std::sync::mpsc::{Receiver, channel}; +static ID_TRACKER: StaticIdTracker = StaticIdTracker::new(); +pub type UiId = Id; + pub struct Ui { + id: UiId, pub(crate) data: PainterData, root: Option, updates: HashSet, - free: Vec, recv: Receiver, full_redraw: bool, resized: bool, } impl Ui { + pub fn id(&self) -> UiId { + self.id + } pub fn set_root(&mut self, w: impl WidgetLike) { self.root = Some(w.add(self)); self.full_redraw = true; @@ -39,10 +45,10 @@ impl Ui { } fn redraw_all(&mut self) { - for (_, inst) in self.data.active.drain() { - for m in self.data.modules.iter_mut() { - m.on_undraw(&inst); - } + for (id, inst) in self.data.active.drain() { + id.map_event_managers(|m| { + m.undraw(self.id, &inst); + }); } // free before bc nothing should exist self.free(); @@ -52,15 +58,8 @@ impl Ui { } pub fn update(&mut self) -> bool { - for update in self.recv.try_iter() { - match update { - WidgetUpdate::Drop(id) => { - self.free.push(id); - } - WidgetUpdate::Mutate(id) => { - self.updates.insert(id); - } - } + for id in self.recv.try_iter() { + self.updates.insert(id); } if self.full_redraw { self.redraw_all(); @@ -85,11 +84,6 @@ impl Ui { /// free any resources that don't have references anymore fn free(&mut self) { - for id in self.free.drain(..) { - for m in self.data.modules.iter_mut() { - m.on_remove(&id); - } - } self.data.textures.free(); } @@ -101,6 +95,10 @@ impl Ui { self.data.active.len() } + pub fn active(&self) -> &HashMap { + &self.data.active + } + pub fn debug_layers(&self) { for ((idx, depth), primitives) in self.data.layers.iter_depth() { let indent = " ".repeat(depth * 2); @@ -141,14 +139,21 @@ impl Ui { impl Default for Ui { fn default() -> Self { let (send, recv) = channel(); + let id = ID_TRACKER.next(); Self { - data: PainterData::new(send), + id, + data: PainterData::new(id, send), root: Default::default(), updates: Default::default(), - free: Default::default(), full_redraw: false, recv, resized: false, } } } + +impl Drop for Ui { + fn drop(&mut self) { + ID_TRACKER.free(self.id); + } +} diff --git a/core/src/layout/widget/data.rs b/core/src/layout/widget/data.rs new file mode 100644 index 0000000..a72795a --- /dev/null +++ b/core/src/layout/widget/data.rs @@ -0,0 +1,40 @@ +use std::{any::TypeId, sync::mpsc::Sender}; + +use crate::{ + layout::{EVENT_TYPES, EventManager, WIDGETS, WidgetId, WidgetUpdate}, + util::{HashSet, RefGuardMut}, +}; + +pub struct WidgetData { + pub id: WidgetId, + pub send: Option>, + pub label: String, + pub event_managers: HashSet, + pub widget: W, +} + +impl WidgetData { + pub(crate) fn event_managers(&self) -> impl Iterator> { + self.event_managers.iter().map(|id| { + EVENT_TYPES + .lock() + .unwrap() + .get_mut(id) + .unwrap() + .clone() + .get_take_mut() + }) + } +} + +impl Drop for WidgetData { + fn drop(&mut self) { + WIDGETS.remove(self.id); + for mut m in self.event_managers() { + // refer to src/layout/event.rs for why this is like this + let stuff = m.remove(self.id); + drop(m); + drop(stuff); + } + } +} diff --git a/core/src/layout/widget/handle.rs b/core/src/layout/widget/handle.rs index 21ed679..20b2016 100644 --- a/core/src/layout/widget/handle.rs +++ b/core/src/layout/widget/handle.rs @@ -1,13 +1,11 @@ -use std::{ - marker::Unsize, - mem::MaybeUninit, - ops::{CoerceUnsized, Deref, DerefMut}, - sync::mpsc::Sender, -}; +use std::{marker::Unsize, mem::MaybeUninit, ops::CoerceUnsized}; use crate::{ - layout::{IdFnTag, IdTag, Ui, WIDGETS, Widget, WidgetLike}, - util::{Handle, Ref, RefMap, RefMapMut, RefMut, WeakHandle}, + layout::{EventManager, IdFnTag, IdTag, Ui, WIDGETS, Widget, WidgetData, WidgetLike}, + util::{ + Handle, MappedRefGuard, MappedRefGuardMut, Ref, RefGuard, RefGuardMut, RefMap, RefMapMut, + RefMut, WeakHandle, + }, }; pub(super) type SlotTy = u32; @@ -24,17 +22,7 @@ pub struct WidgetHandle(WidgetId, Handle>) /// A weak handle to a widget. Implements Copy so you can easily pass into closures pub struct WidgetRef(WidgetId, *const W); -pub(crate) struct WidgetData { - pub id: WidgetId, - pub send: Option>, - pub label: String, - pub widget: W, -} - -pub enum WidgetUpdate { - Drop(WidgetId), - Mutate(WidgetId), -} +pub type WidgetUpdate = WidgetId; impl WidgetHandle { pub(super) fn new(id: WidgetId, widget: W) -> Self { @@ -49,6 +37,7 @@ impl WidgetHandle { widget, send: None, label, + event_managers: Default::default(), } .into(), ) @@ -82,16 +71,20 @@ impl WidgetHandle { pub fn get_mut(&self) -> RefMapMut<'_, W> { let inner = self.1.get_mut(); if let Some(send) = &inner.send { - let _ = send.send(WidgetUpdate::Mutate(self.0)); + let _ = send.send(self.0); } RefMut::map(inner, |i| &mut i.widget) } + pub fn data(&self) -> Ref<'_, WidgetData> { + self.1.get() + } + pub(crate) fn data_mut(&self) -> RefMut<'_, WidgetData> { self.1.get_mut() } - pub fn get_mut_quiet(&self) -> RefMapMut<'_, W> { + pub(crate) fn get_mut_quiet(&self) -> RefMapMut<'_, W> { RefMut::map(self.1.get_mut(), |i| &mut i.widget) } @@ -109,65 +102,53 @@ impl WidgetHandle { } pub fn weak(&self) -> WidgetRef { - WidgetRef(self.0, null_ptr()) + WidgetRef::new(self.0) } } +pub type WRef<'a, W> = MappedRefGuard<'a, WidgetData, W>; +pub type WRefMut<'a, W> = MappedRefGuardMut<'a, WidgetData, W>; + impl WidgetRef { fn handle(&self) -> Handle> { WIDGETS.get_type(self.0).unwrap() } pub fn get<'a>(&self) -> WRef<'a, W> { - let handle = self.handle(); - WRef { - val: unsafe { - std::mem::transmute::, RefMap<'_, W>>(Ref::map(handle.get(), |i| { - &mut i.widget - })) - }, - _handle: handle, - } + RefGuard::map(self.handle().get_take(), |i| &mut i.widget) } pub fn get_mut<'a>(&self) -> WRefMut<'a, W> { - let handle = self.handle(); - if let Some(send) = &handle.get().send { - let _ = send.send(WidgetUpdate::Mutate(self.0)); - } - WRefMut { - val: unsafe { - std::mem::transmute::, RefMapMut<'_, W>>(RefMut::map( - handle.get_mut(), - |i| &mut i.widget, - )) - }, - _handle: handle, + let inner = self.handle().get_take_mut(); + + if let Some(send) = &inner.send { + let _ = send.send(self.0); } + RefGuardMut::map(inner, |i| &mut i.widget) } } -pub struct WRef<'a, W> { - _handle: Handle>, - val: RefMap<'a, W>, -} +impl WidgetRef { + pub fn data<'a>(&self) -> Option>> { + Some(WIDGETS.get(self.0)?.get_take()) + } -pub struct WRefMut<'a, W> { - _handle: Handle>, - val: RefMapMut<'a, W>, + pub(crate) fn data_mut(&self) -> Option>> { + Some(WIDGETS.get(self.0)?.get_take_mut()) + } } impl> WidgetRef { pub fn any(self) -> WidgetRef { - WidgetRef(self.0, null_ptr()) + WidgetRef::new(self.0) } } -fn null_ptr() -> *const T { - unsafe { MaybeUninit::zeroed().assume_init() } -} - impl WidgetRef { + fn new(id: WidgetId) -> Self { + Self(id, unsafe { MaybeUninit::zeroed().assume_init() }) + } + pub fn id(&self) -> WidgetId { self.0 } @@ -178,13 +159,17 @@ impl WidgetId { pub(crate) fn strong(self) -> Option { Some(WidgetHandle(self, WIDGETS.get(self)?)) } -} -impl Drop for WidgetData { - fn drop(&mut self) { - if let Some(send) = &self.send { - WIDGETS.remove(self.id); - let _ = send.send(WidgetUpdate::Drop(self.id)); + pub fn weak(self) -> WidgetRef { + WidgetRef::new(self) + } + + pub(crate) fn map_event_managers(&self, f: impl Fn(&mut dyn EventManager)) { + let Some(data) = self.weak().data() else { + return; + }; + for mut m in data.event_managers() { + f(&mut *m) } } } @@ -252,28 +237,8 @@ impl Copy for WidgetRef {} impl PartialEq for WidgetRef { fn eq(&self, other: &Self) -> bool { - self.0 == other.0 && self.1 == other.1 - } -} - -impl Deref for WRef<'_, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.val - } -} - -impl Deref for WRefMut<'_, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.val - } -} - -impl DerefMut for WRefMut<'_, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.val + self.0 == other.0 } } +unsafe impl Send for WidgetRef {} +unsafe impl Sync for WidgetRef {} diff --git a/core/src/layout/widget/mod.rs b/core/src/layout/widget/mod.rs index 6b959ac..b2c0758 100644 --- a/core/src/layout/widget/mod.rs +++ b/core/src/layout/widget/mod.rs @@ -1,8 +1,10 @@ +mod data; mod handle; mod like; mod tag; mod widgets; +pub use data::*; pub use handle::*; pub use like::*; pub use tag::*; diff --git a/core/src/layout/widget/widgets.rs b/core/src/layout/widget/widgets.rs index aa1d11d..28da3be 100644 --- a/core/src/layout/widget/widgets.rs +++ b/core/src/layout/widget/widgets.rs @@ -1,7 +1,4 @@ -use std::{ - any::Any, - sync::{Mutex, MutexGuard}, -}; +use std::sync::{Mutex, MutexGuard}; use crate::{ layout::{ @@ -47,12 +44,7 @@ impl Widgets { if slot.genr != id.genr { None } else { - Some( - unsafe { slot.data.clone().downcast() } - .unwrap() - .clone() - .strong()?, - ) + Some(unsafe { slot.data.clone().downcast() }.clone().strong()?) } } diff --git a/core/src/util/arena.rs b/core/src/util/arena.rs index 9ddfd99..3ffde9c 100644 --- a/core/src/util/arena.rs +++ b/core/src/util/arena.rs @@ -7,7 +7,7 @@ pub struct Arena { tracker: IdTracker, } -impl Arena { +impl Arena { pub fn new() -> Self { Self { data: Vec::new(), @@ -36,7 +36,7 @@ impl Arena { } } -impl Default for Arena { +impl Default for Arena { fn default() -> Self { Self::new() } @@ -48,7 +48,7 @@ pub struct TrackedArena { pub changed: bool, } -impl TrackedArena { +impl TrackedArena { pub fn new() -> Self { Self { inner: Arena::default(), @@ -86,7 +86,7 @@ impl TrackedArena { } } -impl Default for TrackedArena { +impl Default for TrackedArena { fn default() -> Self { Self::new() } diff --git a/core/src/util/handle.rs b/core/src/util/handle.rs index 2a9116a..551e340 100644 --- a/core/src/util/handle.rs +++ b/core/src/util/handle.rs @@ -1,18 +1,21 @@ +//! A helper type for Arc> +//! Currently there are mut versions (of Ref stuff) which don't do anything different under the hood, +//! leaving them in for now in case I decide RwLock is a desired feature + use std::{ - any::{Any, TypeId}, marker::Unsize, - ops::CoerceUnsized, - sync::{Arc, Mutex, Weak}, + ops::{CoerceUnsized, Deref, DerefMut}, + sync::{Arc, MappedMutexGuard, Mutex, MutexGuard, Weak}, }; pub struct Handle(Arc>); pub struct WeakHandle(Weak>); -pub type Ref<'a, T> = std::sync::MutexGuard<'a, T>; -pub type RefMut<'a, T> = std::sync::MutexGuard<'a, T>; +pub type Ref<'a, T> = MutexGuard<'a, T>; +pub type RefMut<'a, T> = MutexGuard<'a, T>; -pub type RefMap<'a, T> = std::sync::MappedMutexGuard<'a, T>; -pub type RefMapMut<'a, T> = std::sync::MappedMutexGuard<'a, T>; +pub type RefMap<'a, T> = MappedMutexGuard<'a, T>; +pub type RefMapMut<'a, T> = MappedMutexGuard<'a, T>; impl Handle { pub fn get(&self) -> Ref<'_, T> { @@ -23,6 +26,30 @@ impl Handle { self.0.lock().unwrap() } + pub fn get_take<'a>(self) -> RefGuard<'a, T> { + let handle = self; + RefGuard { + guard: unsafe { + std::mem::transmute::, MutexGuard<'_, T>>( + handle.0.lock().unwrap(), + ) + }, + handle, + } + } + + pub fn get_take_mut<'a>(self) -> RefGuardMut<'a, T> { + let handle = self; + RefGuardMut { + guard: unsafe { + std::mem::transmute::, MutexGuard<'_, T>>( + handle.0.lock().unwrap(), + ) + }, + handle, + } + } + pub fn refs(&self) -> usize { Arc::strong_count(&self.0) } @@ -30,6 +57,20 @@ impl Handle { pub fn weak(&self) -> WeakHandle { WeakHandle(Arc::downgrade(&self.0)) } + + /// # Safety + /// you must guarantee the type outside + /// ideally you check the typeid, but this is often used + /// when the trait object is wrapped one or more times + /// and you'd have to implement each one individually + pub unsafe fn downcast(self) -> Handle + where + T: 'static, + { + let raw: *const Mutex = Arc::into_raw(self.0); + let raw: *const Mutex = raw.cast(); + Handle(unsafe { Arc::from_raw(raw) }) + } } impl WeakHandle { @@ -43,13 +84,13 @@ impl WeakHandle { /// # Safety /// you must guarantee the type outside - pub unsafe fn downcast(self) -> Option> + pub unsafe fn downcast(self) -> WeakHandle where T: 'static, { let raw: *const Mutex = self.0.into_raw(); let raw: *const Mutex = raw.cast(); - Some(WeakHandle(unsafe { Weak::from_raw(raw) })) + WeakHandle(unsafe { Weak::from_raw(raw) }) } } @@ -79,3 +120,108 @@ impl From for Handle { impl, U: ?Sized> CoerceUnsized> for Handle {} impl, U: ?Sized> CoerceUnsized> for WeakHandle {} + +// yucky + +// TODO: is drop order important here? +// something stupid could happen that I don't know about +// if handle is dropped before guard +pub struct RefGuard<'a, T: ?Sized> { + guard: MutexGuard<'a, T>, + handle: Handle, +} + +pub struct RefGuardMut<'a, T: ?Sized> { + guard: MutexGuard<'a, T>, + handle: Handle, +} + +pub struct MappedRefGuard<'a, T: ?Sized, U> { + guard: MappedMutexGuard<'a, U>, + handle: Handle, +} + +pub struct MappedRefGuardMut<'a, T: ?Sized, U> { + guard: MappedMutexGuard<'a, U>, + handle: Handle, +} + +impl<'a, T: ?Sized> RefGuard<'a, T> { + pub fn map(s: Self, f: impl FnOnce(&mut T) -> &mut U) -> MappedRefGuard<'a, T, U> { + MappedRefGuard { + guard: MutexGuard::map(s.guard, f), + handle: s.handle, + } + } +} + +impl<'a, T: ?Sized> RefGuardMut<'a, T> { + pub fn map(s: Self, f: impl FnOnce(&mut T) -> &mut U) -> MappedRefGuardMut<'a, T, U> { + MappedRefGuardMut { + guard: MutexGuard::map(s.guard, f), + handle: s.handle, + } + } +} + +impl<'a, T: ?Sized, U> MappedRefGuard<'a, T, U> { + pub fn map(s: Self, f: impl FnOnce(&mut U) -> &mut U2) -> MappedRefGuard<'a, T, U2> { + MappedRefGuard { + guard: MappedMutexGuard::map(s.guard, f), + handle: s.handle, + } + } +} + +impl<'a, T: ?Sized, U> MappedRefGuardMut<'a, T, U> { + pub fn map(s: Self, f: impl FnOnce(&mut U) -> &mut U2) -> MappedRefGuardMut<'a, T, U2> { + MappedRefGuardMut { + guard: MappedMutexGuard::map(s.guard, f), + handle: s.handle, + } + } +} + +impl Deref for RefGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.guard + } +} + +impl Deref for RefGuardMut<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.guard + } +} + +impl DerefMut for RefGuardMut<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.guard + } +} + +impl Deref for MappedRefGuard<'_, T, U> { + type Target = U; + + fn deref(&self) -> &Self::Target { + &self.guard + } +} + +impl Deref for MappedRefGuardMut<'_, T, U> { + type Target = U; + + fn deref(&self) -> &Self::Target { + &self.guard + } +} + +impl DerefMut for MappedRefGuardMut<'_, T, U> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.guard + } +} diff --git a/core/src/util/id.rs b/core/src/util/id.rs index 815d635..b58ea94 100644 --- a/core/src/util/id.rs +++ b/core/src/util/id.rs @@ -1,3 +1,5 @@ +use std::sync::Mutex; + #[repr(C)] #[derive(Eq, Hash, PartialEq, Debug, Clone, Copy, bytemuck::Zeroable)] pub struct Id(I); @@ -9,7 +11,14 @@ pub struct IdTracker { cur: Id, } -impl IdTracker { +impl IdTracker { + pub const fn new() -> Self { + Self { + free: Vec::new(), + cur: Id(I::first()), + } + } + #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Id { if let Some(id) = self.free.pop() { @@ -43,22 +52,19 @@ impl Id { } } -impl Default for IdTracker { +impl Default for IdTracker { fn default() -> Self { - Self { - free: Vec::new(), - cur: Id(I::first()), - } + Self::new() } } -pub trait IdNum { +pub const trait IdNum { fn first() -> Self; fn next(&self) -> Self; fn idx(&self) -> usize; } -impl IdNum for u64 { +impl const IdNum for u64 { fn first() -> Self { 0 } @@ -72,7 +78,7 @@ impl IdNum for u64 { } } -impl IdNum for u32 { +impl const IdNum for u32 { fn first() -> Self { 0 } @@ -85,3 +91,33 @@ impl IdNum for u32 { *self as usize } } + +pub struct StaticIdTracker(Mutex>); + +impl StaticIdTracker { + pub const fn new() -> Self { + Self(Mutex::new(IdTracker::new())) + } + + #[allow(clippy::should_implement_trait)] + pub fn next(&self) -> Id { + let mut s = self.0.lock().unwrap(); + if let Some(id) = s.free.pop() { + return id; + } + let next = s.cur.next(); + std::mem::replace(&mut s.cur, next) + } + + #[allow(dead_code)] + pub fn free(&self, id: Id) { + let mut s = self.0.lock().unwrap(); + s.free.push(id); + } +} + +impl Default for StaticIdTracker { + fn default() -> Self { + Self::new() + } +} diff --git a/core/src/util/refcount.rs b/core/src/util/refcount.rs index 9d0f97c..7cb5ee0 100644 --- a/core/src/util/refcount.rs +++ b/core/src/util/refcount.rs @@ -24,6 +24,12 @@ impl RefCounter { } } +impl Default for RefCounter { + fn default() -> Self { + Self::new() + } +} + impl Clone for RefCounter { fn clone(&self) -> Self { self.0.fetch_add(1, Ordering::Release); diff --git a/macro/src/lib.rs b/macro/src/lib.rs index e13029b..ede2261 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -2,12 +2,13 @@ extern crate proc_macro; use proc_macro::TokenStream; use quote::quote; use syn::{ - Block, Ident, Signature, Token, Visibility, + Attribute, Block, Ident, ItemTrait, Signature, Token, Visibility, parse::{Parse, ParseStream, Result}, - parse_macro_input, + parse_macro_input, parse_quote, }; struct Input { + attrs: Vec, vis: Visibility, name: Ident, fns: Vec, @@ -20,6 +21,7 @@ struct InputFn { impl Parse for Input { fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_outer)?; let vis = input.parse()?; input.parse::()?; let name = input.parse()?; @@ -33,27 +35,23 @@ impl Parse for Input { if !input.is_empty() { input.error("function expected"); } - Ok(Input { vis, name, fns }) + Ok(Input { + attrs, + vis, + name, + fns, + }) } } -// pub trait $name, Tag> { -// $( -// fn $fn $(<$($T $(: $TT $(+ $TL)?)?,)*>)?($self $(, $arg: $ty)*) -> $ret; -// )* -// } -// -// impl, Tag> $name for W { -// $( -// fn $fn $(<$($T $(: $TT $(+ $TL)?)?,)*>)?($self $(, $arg: $ty)*) -> $ret { -// $code -// } -// )* -// } - #[proc_macro] pub fn widget_trait(input: TokenStream) -> TokenStream { - let Input { vis, name, fns } = parse_macro_input!(input as Input); + let Input { + attrs, + vis, + name, + fns, + } = parse_macro_input!(input as Input); let sigs: Vec<_> = fns.iter().map(|f| f.sig.clone()).collect(); let impls: Vec<_> = fns @@ -61,10 +59,16 @@ pub fn widget_trait(input: TokenStream) -> TokenStream { .map(|InputFn { sig, body }| quote! { #sig #body }) .collect(); - TokenStream::from(quote! { + let mut trai: ItemTrait = parse_quote!( #vis trait #name, Tag> { #(#sigs;)* } + ); + + trai.attrs = attrs; + + TokenStream::from(quote! { + #trai impl, Tag> #name for WL { #(#impls)* diff --git a/src/bin/test/main.rs b/src/bin/test/main.rs index d21b628..84b7e0e 100644 --- a/src/bin/test/main.rs +++ b/src/bin/test/main.rs @@ -58,7 +58,7 @@ impl DefaultAppState for Client { .on(CursorSense::click(), move |ctx| { let child = image(include_bytes!("assets/sungals.png")) .center() - .add(ctx.ui); + .add(ctx.data.ui); span_add_.get_mut().children.push(child); }) .sized((150, 150)) @@ -113,7 +113,9 @@ impl DefaultAppState for Client { .text_align(Align::LEFT) .wrap(true) .attr::(()); - let msg_box = text.background(rect(Color::WHITE.darker(0.5))).add(ctx.ui); + let msg_box = text + .background(rect(Color::WHITE.darker(0.5))) + .add(&mut Ui::new()); texts_.get_mut().children.push(msg_box); }) .add(ui); @@ -126,7 +128,7 @@ impl DefaultAppState for Client { add_text.width(rest(1)), Rect::new(Color::GREEN) .on(CursorSense::click(), move |ctx| { - ctx.ui.run_event(ctx.state, add_text_, Submit, ()); + Events::::run(add_text_.id(), &mut (), ctx.state); }) .sized((40, 40)), ) diff --git a/src/default/attr.rs b/src/default/attr.rs index 2148c0f..591f4cf 100644 --- a/src/default/attr.rs +++ b/src/default/attr.rs @@ -2,20 +2,22 @@ use crate::{default::UiState, prelude::*}; use std::time::{Duration, Instant}; use winit::dpi::{LogicalPosition, LogicalSize}; +event_ctx!(UiState); + pub struct Selector; -impl WidgetAttr for Selector { +impl WidgetAttr for Selector { type Input = WidgetRef; fn run(ui: &mut Ui, container: WidgetRef, id: Self::Input) { - ui.register_event(container, CursorSense::click_or_drag(), move |mut ctx| { - let ui = ctx.ui; + container.on(CursorSense::click_or_drag(), move |ctx| { + let ui = &mut ctx.data.ui; let region = ui.window_region(&id).unwrap(); let id_pos = region.top_left; let container_pos = ui.window_region(&container).unwrap().top_left; - ctx.data.cursor += container_pos - id_pos; - ctx.data.size = region.size(); - select(ui, id, ctx.state, ctx.data); + let pos = ctx.data.pos + container_pos - id_pos; + let size = region.size(); + select(ui, id, ctx.state, pos, size, ctx.data.sense.is_dragging()); }); } } @@ -26,18 +28,31 @@ impl WidgetAttr for Selectable { type Input = (); fn run(ui: &mut Ui, id: WidgetRef, _: Self::Input) { - ui.register_event(id, CursorSense::click_or_drag(), move |ctx| { - select(ctx.ui, id, ctx.state, ctx.data); + id.on(CursorSense::click_or_drag(), move |ctx| { + select( + ctx.data.ui, + id, + ctx.state, + ctx.data.pos, + ctx.data.size, + ctx.data.sense.is_dragging(), + ); }); } } -fn select(ui: &mut Ui, id: WidgetRef, state: &mut UiState, data: CursorData) { +fn select( + ui: &mut Ui, + id: WidgetRef, + state: &mut UiState, + pos: Vec2, + size: Vec2, + dragging: bool, +) { let now = Instant::now(); let recent = (now - state.last_click) < Duration::from_millis(300); state.last_click = now; - id.get_mut() - .select(data.cursor, data.size, data.sense.is_dragging(), recent); + id.get_mut().select(pos, size, dragging, recent); if let Some(region) = ui.window_region(&id) { state.window.set_ime_allowed(true); state.window.set_ime_cursor_area( diff --git a/src/default/event.rs b/src/default/event.rs index 51cc925..4ef77eb 100644 --- a/src/default/event.rs +++ b/src/default/event.rs @@ -1,15 +1,9 @@ -use crate::layout::DefaultEvent; +use iris_core::layout::Event; -#[derive(Eq, PartialEq, Hash, Clone)] +#[derive(Clone)] pub struct Submit; +impl Event for Submit {} -#[derive(Eq, PartialEq, Hash, Clone)] +#[derive(Clone)] pub struct Edited; - -impl DefaultEvent for Submit { - type Data = (); -} - -impl DefaultEvent for Edited { - type Data = (); -} +impl Event for Edited {} diff --git a/src/default/mod.rs b/src/default/mod.rs index db00ca2..76ae579 100644 --- a/src/default/mod.rs +++ b/src/default/mod.rs @@ -130,13 +130,13 @@ impl AppState for DefaultState { ui_state.window.set_ime_allowed(false); } TextInputResult::Submit => { - ui.run_event(app_state, sel, Submit, ()); + Events::::run(sel.id(), &mut (), app_state); } TextInputResult::Paste => { if let Ok(t) = ui_state.clipboard.get_text() { sel.get_mut().insert(&t); } - ui.run_event(app_state, sel, Edited, ()); + Events::::run(sel.id(), &mut (), app_state); } TextInputResult::Copy(text) => { if let Err(err) = ui_state.clipboard.set_text(text) { @@ -144,7 +144,7 @@ impl AppState for DefaultState { } } TextInputResult::Used => { - ui.run_event(app_state, sel, Edited, ()); + Events::::run(sel.id(), &mut (), app_state); } TextInputResult::Unused => {} } diff --git a/src/event.rs b/src/event.rs index a28ac51..7ff7660 100644 --- a/src/event.rs +++ b/src/event.rs @@ -6,14 +6,14 @@ pub mod eventable { widget_trait! { pub trait Eventable; - fn on( + fn on( self, event: E, - f: impl WidgetEventFn, + f: impl for<'a> EventIdFn::Data<'a>, WL::Widget>, ) -> impl WidgetIdFn { move |ui| { let id = self.add(ui); - ui.register_widget_event(id.weak(), event, f); + Events::::register(id.weak(), event.into_event(), f); id } } @@ -26,53 +26,37 @@ macro_rules! event_ctx { ($ty: ty) => { mod local_event_trait { use super::*; - use std::marker::Sized; #[allow(unused_imports)] use $crate::prelude::*; - #[allow(unused)] - pub trait EventableCtx { - fn on( + widget_trait! { + #[allow(unused)] + pub trait EventableCtx; + fn on( self, event: E, - f: impl WidgetEventFn, - ) -> impl WidgetIdFn + EventableCtx; - } - - impl, Tag> EventableCtx for WL { - fn on( - self, - event: E, - f: impl WidgetEventFn<$ty, E::Data, WL::Widget>, - ) -> impl WidgetIdFn + EventableCtx { + f: impl for<'a> EventIdFn<$ty, ::Data<'a>, WL::Widget>, + ) -> impl WidgetIdFn { eventable::Eventable::on(self, event, f) } } - + use std::marker::Sized; #[allow(unused)] - pub trait EventableCtxUi - where - WidgetRef: EventableCtx, - { - fn on( - &mut self, - widget: WidgetRef, + pub trait EventableCtxRef { + fn on( + self, event: E, - f: impl WidgetEventFn, + f: impl for<'a> EventIdFn<$ty, ::Data<'a>, W>, ); } - impl EventableCtxUi for Ui - where - WidgetRef: EventableCtx, - { - fn on( - &mut self, - widget: WidgetRef, + impl EventableCtxRef for WidgetRef { + fn on( + self, event: E, - f: impl WidgetEventFn<$ty, E::Data, W>, + f: impl for<'a> EventIdFn<$ty, ::Data<'a>, W>, ) { - self.register_widget_event(widget, event, f); + Events::::register(self, event.into_event(), f); } } } diff --git a/src/widget/event.rs b/src/widget/event.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/widget/sense.rs b/src/widget/sense.rs index f687f91..13bb46a 100644 --- a/src/widget/sense.rs +++ b/src/widget/sense.rs @@ -1,8 +1,5 @@ +use crate::layout::{UiRegion, Vec2}; use crate::prelude::*; -use crate::{ - layout::{UiModule, UiRegion, Vec2}, - util::HashMap, -}; use std::{ ops::{BitOr, Deref, DerefMut}, rc::Rc, @@ -26,8 +23,22 @@ pub enum CursorSense { Scroll, } +#[derive(Clone)] pub struct CursorSenses(Vec); +impl Event for CursorSenses { + type Data<'a> = CursorData<'a>; + type State = SensorState; + fn should_run(&self, data: &mut Self::Data<'_>) -> bool { + if let Some(sense) = should_run(self, data.cursor, data.hover) { + data.sense = sense; + true + } else { + false + } + } +} + impl CursorSense { pub fn click() -> Self { Self::PressStart(CursorButton::Left) @@ -72,6 +83,16 @@ impl CursorButtons { self.middle.end_frame(); self.right.end_frame(); } + + pub fn iter(&self) -> impl Iterator { + [ + CursorButton::Left, + CursorButton::Middle, + CursorButton::Right, + ] + .into_iter() + .map(|b| (b, self.select(&b))) + } } impl CursorState { @@ -100,70 +121,25 @@ pub struct Sensor { pub f: Rc>, } -pub type SensorMap = HashMap>; pub type SenseShape = UiRegion; -pub struct SensorGroup { + +#[derive(Default, Debug)] +pub struct SensorState { pub hover: ActivationState, - pub sensors: Vec>, } -#[derive(Clone)] -pub struct CursorData { - pub cursor: Vec2, +pub struct CursorData<'a> { + /// where this widget was hit + pub pos: Vec2, pub size: Vec2, pub scroll_delta: Vec2, - /// the (first) sense that triggered this event - /// the senses are checked in order + pub hover: ActivationState, + pub cursor: &'a CursorState, + pub ui: &'a mut Ui, + /// the first sense that triggered this pub sense: CursorSense, } -pub struct CursorModule { - map: SensorMap, - active: HashMap>, -} - -impl UiModule for CursorModule { - fn on_draw(&mut self, inst: &WidgetInstance) { - if self.map.contains_key(&inst.id) { - self.active - .entry(inst.layer) - .or_default() - .insert(inst.id, inst.region); - } - } - - fn on_undraw(&mut self, inst: &WidgetInstance) { - if let Some(layer) = self.active.get_mut(&inst.layer) { - layer.remove(&inst.id); - } - } - - fn on_remove(&mut self, id: &WidgetId) { - self.map.remove(id); - for layer in self.active.values_mut() { - layer.remove(id); - } - } - - fn on_move(&mut self, inst: &WidgetInstance) { - if let Some(map) = self.active.get_mut(&inst.layer) - && let Some(region) = map.get_mut(&inst.id) - { - *region = inst.region; - } - } -} - -impl CursorModule { - pub fn merge(&mut self, other: Self) { - for (id, group) in other.map { - for sensor in group.sensors { - self.map.entry(id).or_default().sensors.push(sensor); - } - } - } -} - pub trait SensorUi { fn run_sensors(&mut self, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2); } @@ -175,55 +151,41 @@ impl SensorUi for Ui { cursor: &CursorState, window_size: Vec2, ) { - CursorModule::::run(self, ctx, cursor, window_size); - } -} - -impl CursorModule { - pub fn run(ui: &mut Ui, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) { - let layers = std::mem::take(&mut ui.data_mut().layers); - let mut module = std::mem::take(ui.data_mut().modules.get_mut::()); - + let ui_id = self.id(); + let layers = std::mem::take(&mut self.data_mut().layers); + let mut active = std::mem::take(&mut *Events::::active(ui_id)); for i in layers.indices().rev() { - let Some(list) = module.active.get_mut(&i) else { - continue; - }; let mut sensed = false; - for (id, shape) in list.iter() { - let group = module.map.get_mut(id).unwrap(); + for (id, state) in active.get_mut(&i).into_iter().flatten() { + let shape = self.active().get(id).unwrap().region; let region = shape.to_px(window_size); let in_shape = cursor.exists && region.contains(cursor.pos); - group.hover.update(in_shape); - if group.hover == ActivationState::Off { + state.hover.update(in_shape); + if state.hover == ActivationState::Off { continue; } sensed = true; - for sensor in &mut group.sensors { - if let Some(sense) = should_run(&sensor.senses, cursor, group.hover) { - let data = CursorData { - cursor: cursor.pos - region.top_left, - size: region.bot_right - region.top_left, - scroll_delta: cursor.scroll_delta, - sense, - }; - (sensor.f)(EventCtx { - ui, - state: ctx, - data, - }); - } - } + let mut data = CursorData { + pos: cursor.pos - region.top_left, + size: region.bot_right - region.top_left, + scroll_delta: cursor.scroll_delta, + hover: state.hover, + cursor, + ui: self, + // this does not have any meaning; + // might wanna set up Event to have a prepare stage + sense: CursorSense::Hovering, + }; + Events::::run(*id, &mut data, ctx); } if sensed { break; } } - let ui_mod = ui.data_mut().modules.get_mut::(); - std::mem::swap(ui_mod, &mut module); - ui_mod.merge(module); - ui.data_mut().layers = layers; + *Events::::active(ui_id) = active; + self.data_mut().layers = layers; } } @@ -291,66 +253,10 @@ impl ActivationState { } } -impl Event for CursorSenses { - type Module = CursorModule; - type Data = CursorData; -} - -impl Event for CursorSense { - type Module = CursorModule; - type Data = CursorData; -} - -impl::Data> + Into, Ctx: 'static> - EventModule for CursorModule -{ - fn register(&mut self, id: WidgetId, senses: E, f: impl EventFn::Data>) { - // TODO: does not add to active if currently active - self.map.entry(id).or_default().sensors.push(Sensor { - senses: senses.into(), - f: Rc::new(f), - }); - } - - fn run<'a>( - &mut self, - id: WidgetId, - event: E, - ) -> Option::Data>) + use<'a, Ctx, E>> { - let senses = event.into(); - if let Some(group) = self.map.get_mut(&id) { - let fs: Vec<_> = group - .sensors - .iter_mut() - .filter_map(|sensor| { - if sensor.senses.iter().any(|s| senses.contains(s)) { - Some(sensor.f.clone()) - } else { - None - } - }) - .collect(); - Some(move |ctx: EventCtx| { - for f in fs { - f(EventCtx { - state: ctx.state, - ui: ctx.ui, - data: ctx.data.clone(), - }); - } - }) - } else { - None - } - } -} - -impl Default for SensorGroup { - fn default() -> Self { - Self { - hover: Default::default(), - sensors: Default::default(), - } +impl EventAlias for CursorSense { + type Event = CursorSenses; + fn into_event(self) -> Self::Event { + CursorSenses::from(self) } } @@ -390,12 +296,3 @@ impl BitOr for CursorSenses { self } } - -impl Default for CursorModule { - fn default() -> Self { - Self { - map: Default::default(), - active: Default::default(), - } - } -}