From 37b1987aa8e7a40b3bb983924c5e6a2d6e89f9eb Mon Sep 17 00:00:00 2001 From: shadowcat Date: Fri, 12 Dec 2025 01:46:24 -0500 Subject: [PATCH] remove modules and have single event manager (atomics feature parity + preparation for local state) --- core/src/event.rs | 280 ++++++++++++++++++------------- core/src/lib.rs | 2 - core/src/module.rs | 32 ---- core/src/painter.rs | 45 +++-- core/src/primitive/layer.rs | 26 +-- core/src/ui.rs | 34 ++-- core/src/widget/data.rs | 26 +++ core/src/widget/handle.rs | 14 ++ core/src/widget/mod.rs | 2 + core/src/widget/widgets.rs | 39 ++--- src/bin/test/main.rs | 3 +- src/default/attr.rs | 49 ++++-- src/default/event.rs | 6 +- src/default/mod.rs | 8 +- src/{widget => default}/sense.rs | 220 +++++++----------------- src/event.rs | 34 ++-- src/widget/mod.rs | 2 - 17 files changed, 390 insertions(+), 432 deletions(-) delete mode 100644 core/src/module.rs create mode 100644 core/src/widget/data.rs rename src/{widget => default}/sense.rs (52%) diff --git a/core/src/event.rs b/core/src/event.rs index 2a4cd6c..a3b38cc 100644 --- a/core/src/event.rs +++ b/core/src/event.rs @@ -1,26 +1,172 @@ -use crate::{Ui, UiModule, Widget, WidgetId, WidgetRef, util::HashMap}; +use crate::{ + ActiveData, IdLike, LayerId, Ui, Widget, WidgetData, WidgetId, WidgetRef, util::HashMap, +}; use std::{ - hash::Hash, + any::{Any, TypeId}, ops::{Index, IndexMut}, rc::Rc, }; -pub trait Event: Sized { - type Module: EventModule; - type Data: Clone; +pub trait Event: Sized + 'static + Clone { + type Data<'a> = (); + type State: Default = (); + #[allow(unused_variables)] + fn should_run(&self, data: &mut Self::Data<'_>) -> bool { + true + } +} + +pub trait EventLike { + type Event: Event; + fn into_event(self) -> Self::Event; +} + +impl EventLike for E { + type Event = Self; + + fn into_event(self) -> Self::Event { + self + } } pub struct EventCtx<'a, Ctx, Data> { pub ui: &'a mut Ui, pub state: &'a mut Ctx, - pub data: Data, + pub data: &'a mut Data, } 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 data: &'a mut Data, +} + +#[derive(Default)] +pub struct EventManager { + types: HashMap>, +} + +impl EventManager { + pub fn get_type(&mut self) -> &mut TypeEventManager { + self.types + .entry(Self::type_key::()) + .or_insert(Box::new(TypeEventManager::::default())) + .as_any() + .downcast_mut() + .unwrap() + } + + pub fn register( + &mut self, + id: I, + event: E, + f: impl for<'a> EventFn::Data<'a>>, + ) { + self.get_type::().register(id, event, f); + } + + pub fn type_key() -> TypeId { + TypeId::of::>() + } + + pub fn remove(&mut self, id: WidgetId) { + for m in self.types.values_mut() { + m.remove(id); + } + } + + pub fn draw(&mut self, data: &WidgetData, active: &ActiveData) { + for t in &data.event_mgrs { + self.types.get_mut(t).unwrap().draw(active); + } + } + + pub fn undraw(&mut self, data: &WidgetData, active: &ActiveData) { + for t in &data.event_mgrs { + self.types.get_mut(t).unwrap().undraw(active); + } + } +} + +pub trait EventManagerLike { + fn remove(&mut self, id: WidgetId); + fn draw(&mut self, data: &ActiveData); + fn undraw(&mut self, data: &ActiveData); + fn as_any(&mut self) -> &mut dyn Any; +} + +type EventData = (E, Rc EventFn::Data<'a>>>); +pub struct TypeEventManager { + // TODO: reduce visiblity!! + pub active: HashMap>, + map: HashMap>>, +} + +impl EventManagerLike for TypeEventManager { + fn remove(&mut self, id: WidgetId) { + self.map.remove(&id); + for layer in self.active.values_mut() { + layer.remove(&id); + } + } + fn draw(&mut self, data: &ActiveData) { + self.active + .entry(data.layer) + .or_default() + .entry(data.id) + .or_default(); + } + fn undraw(&mut self, data: &ActiveData) { + if let Some(layer) = self.active.get_mut(&data.layer) { + layer.remove(&data.id); + } + } + fn as_any(&mut self) -> &mut dyn Any { + self + } +} + +impl Default for TypeEventManager { + fn default() -> Self { + Self { + active: Default::default(), + map: Default::default(), + } + } +} + +impl TypeEventManager { + fn register( + &mut self, + id: impl IdLike, + event: impl EventLike, + f: impl for<'a> EventFn>, + ) { + let event = event.into_event(); + self.map + .entry(id.id()) + .or_default() + .push((event, Rc::new(f))); + } + + fn run_fn<'a>( + &mut self, + id: impl IdLike, + ) -> impl for<'b> FnOnce(EventCtx>) + 'a { + let fs = self.map.get(&id.id()).cloned().unwrap_or_default(); + move |ctx| { + for (e, f) in fs { + if e.should_run(ctx.data) { + f(EventCtx { + ui: ctx.ui, + state: ctx.state, + data: ctx.data, + }) + } + } + } + } } impl<'a, Ctx, Data, W2, W: Widget> Index> for EventIdCtx<'a, Ctx, Data, W2> { @@ -52,120 +198,18 @@ impl) + 'static, Ctx, Data, W: ?Sized> WidgetEven { } -pub trait DefaultEvent: Hash + Eq + 'static { - type Data: Clone = (); -} - -impl Event for E { - type Module = DefaultEventModule; - type Data = E::Data; -} - -pub trait EventModule: UiModule + Default { - fn register(&mut self, id: WidgetId, event: E, f: impl EventFn); - fn run<'a>( - &self, - id: WidgetId, - event: E, - ) -> Option) + use<'a, Self, E, Ctx>>; -} - -type EventFnMap = HashMap>>>; -pub struct DefaultEventModule { - map: HashMap::Data>>, -} - -impl UiModule for DefaultEventModule { - fn on_remove(&mut self, id: WidgetId) { - for map in self.map.values_mut() { - map.remove(&id); - } - } -} - -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)); - } - - fn run<'a>( - &self, - id: WidgetId, - event: E, - ) -> Option) + use<'a, E, Ctx>> { - 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(), - }) - } - }) - } else { - None - } - } -} - -impl DefaultEventModule { - pub fn run_all(&self, event: E, ctx: EventCtx) - where - E::Data: Clone, - { - if let Some(map) = self.map.get(&event) { - for fs in map.values() { - for f in fs { - f(EventCtx { - ui: ctx.ui, - state: ctx.state, - data: ctx.data.clone(), - }) - } - } - } - } -} - -impl Default for DefaultEventModule { - fn default() -> Self { - Self { - map: Default::default(), - } - } -} - impl Ui { - pub fn run_event( + pub fn run_event( &mut self, ctx: &mut Ctx, - id: WidgetRef, - event: E, - data: E::Data, + id: impl IdLike, + data: &mut ::Data<'_>, ) { - if let Some(f) = self - .data - .modules - .get_mut::>() - .run(id.id(), event) - { - f(EventCtx { - ui: self, - state: ctx, - data, - }); - } + let f = self.data.events.get_type::().run_fn(id); + f(EventCtx { + ui: self, + state: ctx, + data, + }) } } diff --git a/core/src/lib.rs b/core/src/lib.rs index 9cd5f7e..935fb6c 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -14,7 +14,6 @@ mod attr; mod event; -mod module; mod num; mod orientation; mod painter; @@ -27,7 +26,6 @@ pub mod util; pub use attr::*; pub use event::*; -pub use module::*; pub use num::*; pub use orientation::*; pub use painter::*; diff --git a/core/src/module.rs b/core/src/module.rs deleted file mode 100644 index 41570b4..0000000 --- a/core/src/module.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::any::{Any, TypeId}; - -use crate::{ActiveData, WidgetId, util::HashMap}; - -#[allow(unused_variables)] -pub trait UiModule: Any { - fn on_draw(&mut self, inst: &ActiveData) {} - fn on_undraw(&mut self, inst: &ActiveData) {} - fn on_remove(&mut self, id: WidgetId) {} - fn on_move(&mut self, inst: &ActiveData) {} -} - -#[derive(Default)] -pub struct Modules { - map: HashMap>, -} - -impl Modules { - pub fn iter_mut(&mut self) -> impl Iterator { - self.map.values_mut().map(|m| m.as_mut()) - } - - pub fn get_mut(&mut self) -> &mut M { - let rf = self - .map - .entry(TypeId::of::()) - .or_insert_with(|| Box::new(M::default())) - .as_mut(); - let any: &mut dyn Any = &mut *rf; - any.downcast_mut().unwrap() - } -} diff --git a/core/src/painter.rs b/core/src/painter.rs index 4645eea..8d37277 100644 --- a/core/src/painter.rs +++ b/core/src/painter.rs @@ -1,5 +1,5 @@ use crate::{ - Axis, Len, Modules, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData, + Axis, EventManager, Len, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData, TextureHandle, Textures, UiRegion, UiVec2, WidgetHandle, WidgetId, Widgets, render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst}, util::{HashMap, HashSet, TrackedArena, Vec2}, @@ -28,7 +28,7 @@ struct PainterCtx<'a> { pub masks: &'a mut TrackedArena, pub text: &'a mut TextData, pub output_size: Vec2, - pub modules: &'a mut Modules, + pub events: &'a mut EventManager, pub cache_width: HashMap, pub cache_height: HashMap, pub needs_redraw: &'a mut HashSet, @@ -66,7 +66,7 @@ pub struct PainterData { pub textures: Textures, pub text: TextData, pub output_size: Vec2, - pub modules: Modules, + pub events: EventManager, pub px_dependent: HashSet, pub masks: TrackedArena, } @@ -150,7 +150,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; }; @@ -204,7 +204,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; } @@ -231,7 +231,7 @@ impl<'a> PainterCtx<'a> { let children_height = painter.children_height; // add to active - let instance = ActiveData { + let active = ActiveData { id, region, parent, @@ -260,16 +260,15 @@ impl<'a> PainterCtx<'a> { // remove old children that weren't kept for c in &old_children { - if !instance.children.contains(c) { + if !active.children.contains(c) { self.remove_rec(*c); } } // update modules - for m in self.modules.iter_mut() { - m.on_draw(&instance); - } - self.active.insert(id, instance); + let data = self.widgets.data(id).unwrap(); + self.events.draw(data, &active); + self.active.insert(id, active); } fn mov(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) { @@ -279,9 +278,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(); @@ -291,26 +287,27 @@ impl<'a> PainterCtx<'a> { } /// NOTE: instance textures are cleared and self.textures freed - fn remove(&mut self, id: WidgetId) -> Option { - let mut inst = self.active.remove(&id); - if let Some(inst) = &mut inst { - for h in &inst.primitives { + fn remove(&mut self, id: WidgetId, undraw: bool) -> Option { + let mut active = self.active.remove(&id); + if let Some(active) = &mut active { + for h in &active.primitives { let mask = self.layers.free(h); if mask != MaskIdx::NONE { self.masks.remove(mask); } } - inst.textures.clear(); + active.textures.clear(); self.textures.free(); - for m in self.modules.iter_mut() { - m.on_undraw(inst); + if undraw { + let data = self.widgets.data(id).unwrap(); + self.events.undraw(data, active); } } - inst + active } 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); @@ -329,7 +326,7 @@ impl PainterData { textures: &mut self.textures, text: &mut self.text, output_size: self.output_size, - modules: &mut self.modules, + events: &mut self.events, masks: &mut self.masks, cache_width: Default::default(), cache_height: Default::default(), diff --git a/core/src/primitive/layer.rs b/core/src/primitive/layer.rs index c0a0603..b841602 100644 --- a/core/src/primitive/layer.rs +++ b/core/src/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, @@ -49,13 +51,13 @@ impl Layers { self.vec.push(LayerNode::head()); } - fn push(&mut self, node: LayerNode) -> usize { + fn push(&mut self, node: LayerNode) -> LayerId { let i = self.vec.len(); self.vec.push(node); i } - pub fn next(&mut self, i: usize) -> usize { + pub fn next(&mut self, i: LayerId) -> LayerId { if let Ptr::Next(i) = self.vec[i].next { return i; } @@ -75,7 +77,7 @@ impl Layers { i_new } - pub fn child(&mut self, i: usize) -> usize { + pub fn child(&mut self, i: LayerId) -> LayerId { if let Some(c) = self.vec[i].child { return c.head; } @@ -100,11 +102,11 @@ impl Layers { self.vec.iter_mut().map(|n| &mut n.data).enumerate() } - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.indices().map(|i| (i, &self.vec[i].data)) } - pub fn iter_depth(&self) -> impl Iterator { + pub fn iter_depth(&self) -> impl Iterator { self.indices() .map(|i| ((i, self.vec[i].depth), &self.vec[i].data)) } @@ -115,7 +117,11 @@ impl Layers { } impl PrimitiveLayers { - pub fn write(&mut self, layer: usize, info: PrimitiveInst

) -> PrimitiveHandle { + pub fn write( + &mut self, + layer: LayerId, + info: PrimitiveInst

, + ) -> PrimitiveHandle { self[layer].write(layer, info) } @@ -130,16 +136,16 @@ impl Default for Layers { } } -impl Index for Layers { +impl Index for Layers { type Output = T; - fn index(&self, index: usize) -> &Self::Output { + fn index(&self, index: LayerId) -> &Self::Output { &self.vec[index].data } } -impl IndexMut for Layers { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { +impl IndexMut for Layers { + fn index_mut(&mut self, index: LayerId) -> &mut Self::Output { &mut self.vec[index].data } } diff --git a/core/src/ui.rs b/core/src/ui.rs index 3c3ee43..a5fcdff 100644 --- a/core/src/ui.rs +++ b/core/src/ui.rs @@ -1,6 +1,6 @@ use crate::{ - ActiveData, Event, EventFn, EventModule, IdLike, PainterData, PixelRegion, TextureHandle, - Widget, WidgetHandle, WidgetId, WidgetLike, util::Vec2, + ActiveData, Event, EventFn, EventLike, EventManager, IdLike, PainterData, PixelRegion, + TextureHandle, Widget, WidgetHandle, WidgetId, WidgetLike, util::Vec2, }; use image::DynamicImage; use std::{ @@ -24,11 +24,11 @@ impl Ui { } /// useful for debugging - pub fn set_label(&mut self, id: &WidgetHandle, label: String) { + pub fn set_label(&mut self, id: impl IdLike, label: String) { self.data.widgets.data_mut(id.id()).unwrap().label = label; } - pub fn label(&self, id: &WidgetHandle) -> &String { + pub fn label(&self, id: impl IdLike) -> &String { &self.data.widgets.data(id.id()).unwrap().label } @@ -63,16 +63,19 @@ impl Ui { self.data.textures.add(image) } - pub fn register_event( + pub fn register_event( &mut self, - id: &I, + id: impl IdLike, event: E, - f: impl EventFn, + f: impl for<'a> EventFn::Data<'a>>, ) { + self.data.events.register(id.id(), event, f); self.data - .modules - .get_mut::>() - .register(id.id(), event, f); + .widgets + .data_mut(id) + .unwrap() + .event_mgrs + .insert(EventManager::type_key::()); } pub fn resize(&mut self, size: impl Into) { @@ -81,10 +84,9 @@ impl Ui { } pub 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, active) in self.data.active.drain() { + let data = self.data.widgets.data(id).unwrap(); + self.data.events.undraw(data, &active); } // free before bc nothing should exist self.free(); @@ -116,9 +118,7 @@ impl Ui { /// free any resources that don't have references anymore fn free(&mut self) { for id in self.recv.try_iter() { - for m in self.data.modules.iter_mut() { - m.on_remove(id); - } + self.data.events.remove(id); self.data.widgets.delete(id); } self.data.textures.free(); diff --git a/core/src/widget/data.rs b/core/src/widget/data.rs new file mode 100644 index 0000000..68ef4f3 --- /dev/null +++ b/core/src/widget/data.rs @@ -0,0 +1,26 @@ +use std::any::TypeId; + +use crate::{Widget, util::HashSet}; + +pub struct WidgetData { + pub widget: Box, + pub label: String, + pub event_mgrs: HashSet, + /// dynamic borrow checking + pub borrowed: bool, +} + +impl WidgetData { + pub fn new(widget: W) -> Self { + let mut label = std::any::type_name::().to_string(); + if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) { + label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1; + } + Self { + widget: Box::new(widget), + label, + borrowed: false, + event_mgrs: Default::default(), + } + } +} diff --git a/core/src/widget/handle.rs b/core/src/widget/handle.rs index 9f425ee..6d61c13 100644 --- a/core/src/widget/handle.rs +++ b/core/src/widget/handle.rs @@ -94,6 +94,13 @@ pub trait IdLike { fn id(&self) -> WidgetId; } +impl IdLike for &WidgetHandle { + type Widget = W; + fn id(&self) -> WidgetId { + self.id + } +} + impl IdLike for WidgetHandle { type Widget = W; fn id(&self) -> WidgetId { @@ -108,6 +115,13 @@ impl IdLike for WidgetRef { } } +impl IdLike for WidgetId { + type Widget = dyn Widget; + fn id(&self) -> WidgetId { + *self + } +} + impl, U: ?Sized> CoerceUnsized> for WidgetHandle {} impl, U: ?Sized> CoerceUnsized> for WidgetRef {} diff --git a/core/src/widget/mod.rs b/core/src/widget/mod.rs index 7b4daf1..75f790b 100644 --- a/core/src/widget/mod.rs +++ b/core/src/widget/mod.rs @@ -1,11 +1,13 @@ use crate::{Len, Painter, SizeCtx, Ui}; use std::any::Any; +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/widget/widgets.rs b/core/src/widget/widgets.rs index 79366ea..8aaa8a2 100644 --- a/core/src/widget/widgets.rs +++ b/core/src/widget/widgets.rs @@ -1,5 +1,5 @@ use crate::{ - IdLike, Widget, WidgetId, + IdLike, Widget, WidgetData, WidgetId, util::{DynBorrower, HashSet, SlotVec}, }; @@ -9,13 +9,6 @@ pub struct Widgets { vec: SlotVec, } -pub struct WidgetData { - pub widget: Box, - pub label: String, - /// dynamic borrow checking - pub borrowed: bool, -} - impl Widgets { pub fn has_updates(&self) -> bool { !self.updates.is_empty() @@ -60,35 +53,23 @@ impl Widgets { } pub fn add(&mut self, widget: W) -> WidgetId { - let mut label = std::any::type_name::().to_string(); - if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) { - label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1; - } - self.insert_any(Box::new(widget), label) + self.vec.add(WidgetData::new(widget)) } - pub fn data(&self, id: WidgetId) -> Option<&WidgetData> { - self.vec.get(id) + pub fn data(&self, id: impl IdLike) -> Option<&WidgetData> { + self.vec.get(id.id()) } - pub fn label(&self, id: WidgetId) -> &String { - &self.data(id).unwrap().label + pub fn label(&self, id: impl IdLike) -> &String { + &self.data(id.id()).unwrap().label } - pub fn data_mut(&mut self, id: WidgetId) -> Option<&mut WidgetData> { - self.vec.get_mut(id) + pub fn data_mut(&mut self, id: impl IdLike) -> Option<&mut WidgetData> { + self.vec.get_mut(id.id()) } - pub fn insert_any(&mut self, widget: Box, label: String) -> WidgetId { - self.vec.add(WidgetData { - widget, - label, - borrowed: false, - }) - } - - pub fn delete(&mut self, id: WidgetId) { - self.vec.free(id); + pub fn delete(&mut self, id: impl IdLike) { + self.vec.free(id.id()); // not sure if there's any point in this // self.updates.remove(&id); } diff --git a/src/bin/test/main.rs b/src/bin/test/main.rs index 5d22bc4..0a63d4e 100644 --- a/src/bin/test/main.rs +++ b/src/bin/test/main.rs @@ -125,7 +125,8 @@ impl DefaultAppState for Client { add_text.h.width(rest(1)), Rect::new(Color::GREEN) .on(CursorSense::click(), move |ctx| { - ctx.ui.run_event(ctx.state, add_text.r, Submit, ()); + ctx.ui + .run_event::(ctx.state, add_text.r, &mut ()); }) .sized((40, 40)), ) diff --git a/src/default/attr.rs b/src/default/attr.rs index d8f6e8b..de94a92 100644 --- a/src/default/attr.rs +++ b/src/default/attr.rs @@ -1,21 +1,29 @@ -use crate::prelude::*; +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; - let region = ui.window_region(&id).unwrap(); + ui.register_event(container, CursorSense::click_or_drag(), move |ctx| { + let region = ctx.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 container_pos = ctx.ui.window_region(&container).unwrap().top_left; + let pos = ctx.data.pos + container_pos - id_pos; + let size = region.size(); + select( + ctx.ui, + id, + ctx.state, + pos, + size, + ctx.data.sense.is_dragging(), + ); }); } } @@ -26,18 +34,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); + ui.register_event(id, CursorSense::click_or_drag(), move |ctx| { + select( + ctx.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.edit(ui) - .select(data.cursor, data.size, data.sense.is_dragging(), recent); + id.edit(ui).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 ee9a59f..b33f09e 100644 --- a/src/default/event.rs +++ b/src/default/event.rs @@ -1,9 +1,9 @@ -use iris_core::DefaultEvent; +use iris_core::Event; #[derive(Eq, PartialEq, Hash, Clone)] pub struct Submit; -impl DefaultEvent for Submit {} +impl Event for Submit {} #[derive(Eq, PartialEq, Hash, Clone)] pub struct Edited; -impl DefaultEvent for Edited {} +impl Event for Edited {} diff --git a/src/default/mod.rs b/src/default/mod.rs index 6dc1202..7715313 100644 --- a/src/default/mod.rs +++ b/src/default/mod.rs @@ -11,12 +11,14 @@ mod attr; mod event; mod input; mod render; +mod sense; pub use app::*; pub use attr::*; pub use event::*; pub use input::*; pub use render::*; +pub use sense::*; pub type Proxy = EventLoopProxy; pub type DefaultApp = App>; @@ -131,13 +133,13 @@ impl AppState for DefaultState { ui_state.window.set_ime_allowed(false); } TextInputResult::Submit => { - ui.run_event(app_state, sel, Submit, ()); + ui.run_event::(app_state, sel, &mut ()); } TextInputResult::Paste => { if let Ok(t) = ui_state.clipboard.get_text() { text.insert(&t); } - ui.run_event(app_state, sel, Edited, ()); + ui.run_event::(app_state, sel, &mut ()); } TextInputResult::Copy(text) => { if let Err(err) = ui_state.clipboard.set_text(text) { @@ -145,7 +147,7 @@ impl AppState for DefaultState { } } TextInputResult::Used => { - ui.run_event(app_state, sel, Edited, ()); + ui.run_event::(app_state, sel, &mut ()); } TextInputResult::Unused => {} } diff --git a/src/widget/sense.rs b/src/default/sense.rs similarity index 52% rename from src/widget/sense.rs rename to src/default/sense.rs index 4b54c32..67bfc61 100644 --- a/src/widget/sense.rs +++ b/src/default/sense.rs @@ -1,5 +1,4 @@ use crate::prelude::*; -use iris_core::util::HashMap; use std::{ ops::{BitOr, Deref, DerefMut}, rc::Rc, @@ -23,8 +22,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) @@ -69,6 +82,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 { @@ -97,70 +120,24 @@ 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, + /// 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: &ActiveData) { - 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: &ActiveData) { - 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: &ActiveData) { - 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); } @@ -172,55 +149,39 @@ 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.layers); - let mut module = std::mem::take(ui.data.modules.get_mut::()); - - for i in layers.indices().rev() { - let Some(list) = module.active.get_mut(&i) else { - continue; - }; + let layers = std::mem::take(&mut self.data.layers); + let mut active = + std::mem::take(&mut self.data.events.get_type::().active); + for layer in layers.indices().rev() { 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(&layer).into_iter().flatten() { + let shape = self.data.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, + // this does not have any meaning; + // might wanna set up Event to have a prepare stage + sense: CursorSense::Hovering, + }; + self.run_event::(ctx, *id, &mut data); } if sensed { break; } } - - let ui_mod = ui.data.modules.get_mut::(); - std::mem::swap(ui_mod, &mut module); - ui_mod.merge(module); - ui.data.layers = layers; + self.data.events.get_type::().active = active; + self.data.layers = layers; } } @@ -288,66 +249,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>( - &self, - id: WidgetId, - event: E, - ) -> Option::Data>) + use<'a, E, Ctx>> { - let senses = event.into(); - if let Some(group) = self.map.get(&id) { - let fs: Vec<_> = group - .sensors - .iter() - .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 EventLike for CursorSense { + type Event = CursorSenses; + fn into_event(self) -> Self::Event { + self.into() } } @@ -387,12 +292,3 @@ impl BitOr for CursorSenses { self } } - -impl Default for CursorModule { - fn default() -> Self { - Self { - map: Default::default(), - active: Default::default(), - } - } -} diff --git a/src/event.rs b/src/event.rs index e03a4d5..bf168db 100644 --- a/src/event.rs +++ b/src/event.rs @@ -5,14 +5,14 @@ pub mod eventable { widget_trait! { pub trait Eventable; - fn on( + fn on( self, event: E, - f: impl WidgetEventFn, + f: impl for<'a> WidgetEventFn::Data<'a>, WL::Widget>, ) -> impl WidgetIdFn { move |ui| { let id = self.handles(ui); - ui.register_event(&id.r, event, move |ctx| { + ui.register_event(id.r, event.into_event(), move |ctx| { f(EventIdCtx { widget: id.r, state: ctx.state, @@ -35,21 +35,25 @@ macro_rules! event_ctx { #[allow(unused_imports)] use $crate::prelude::*; - pub trait EventableCtx, Tag, Ctx: 'static> { - fn on( + widget_trait! { + pub trait EventableCtx; + fn on( self, event: E, - f: impl WidgetEventFn, - ) -> impl WidgetIdFn; - } - - impl, Tag> EventableCtx for WL { - fn on( - self, - event: E, - f: impl WidgetEventFn<$ty, E::Data, WL::Widget>, + f: impl for<'a> WidgetEventFn<$ty, ::Data<'a>, WL::Widget>, ) -> impl WidgetIdFn { - eventable::Eventable::on(self, event, f) + move |ui| { + let id = self.handles(ui); + ui.register_event(id.r, event.into_event(), move |ctx| { + f(EventIdCtx { + widget: id.r, + state: ctx.state, + data: ctx.data, + ui: ctx.ui, + }); + }); + id.h + } } } } diff --git a/src/widget/mod.rs b/src/widget/mod.rs index 9b86230..016fbe8 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -3,7 +3,6 @@ mod mask; mod position; mod ptr; mod rect; -mod sense; mod text; mod trait_fns; @@ -12,6 +11,5 @@ pub use mask::*; pub use position::*; pub use ptr::*; pub use rect::*; -pub use sense::*; pub use text::*; pub use trait_fns::*;