From 443e13f094e0e35ad53c6469f77c59bd0daea0cb Mon Sep 17 00:00:00 2001 From: shadow cat Date: Wed, 24 Sep 2025 22:46:55 -0400 Subject: [PATCH] make run sensors sane and adjust on_edit to just use ui as ctx (so two run calls needed) --- src/core/sense.rs | 95 ++++++++++++++++++++++++--------------------- src/layout/event.rs | 55 +++++++++++--------------- src/testing/mod.rs | 6 +-- src/util/id.rs | 4 +- 4 files changed, 78 insertions(+), 82 deletions(-) diff --git a/src/core/sense.rs b/src/core/sense.rs index 8e44bde..4400fec 100644 --- a/src/core/sense.rs +++ b/src/core/sense.rs @@ -3,7 +3,7 @@ use crate::prelude::*; use std::ops::{BitOr, Deref, DerefMut}; use crate::{ - layout::{UiModule, UiRegion, Vec2, WidgetId}, + layout::{UiModule, UiRegion, Vec2}, util::{HashMap, Id}, }; @@ -123,61 +123,65 @@ impl UiModule for SensorModule { } impl SensorModule { - pub fn set(&mut self, id: WidgetId, sensor: Sensor) { - // TODO: currently does not add to active if it exists - self.map.entry(id.key()).or_default().sensors.push(sensor); + pub fn merge(&mut self, other: Self) { + for (id, group) in other.map { + for sensor in group.sensors { + self.map + .entry(id.duplicate()) + .or_default() + .sensors + .push(sensor); + } + } } } -pub fn run_sensors(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) { - let mut layers = std::mem::take(&mut ctx.ui().layers); +impl SensorModule { + pub fn run(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) { + let mut layers = std::mem::take(&mut ctx.ui().layers); + let mut module = std::mem::take(ctx.ui().modules.get_mut::()); - // TODO: temp, need to actually make reverse - let mut layer_idxs = Vec::new(); - for (i, _) in layers.iter_mut() { - layer_idxs.push(i); - } + // TODO: temp, need to actually make reverse + let mut layer_idxs = Vec::new(); + for (i, _) in layers.iter_mut() { + layer_idxs.push(i); + } - for l in &layer_idxs { - let m = ctx.ui().modules.get_mut::>(); - let Some(list) = m.active.get_mut(l) else { - continue; - }; - let list = list.clone(); - let mut ran = false; - for (id, shape) in list.iter() { - let m = ctx.ui().modules.get_mut::>(); - let mut group = m.map.remove(id).unwrap(); - let region = shape.to_screen(window_size); - let in_shape = cursor.exists && region.contains(cursor.pos); - group.hover.update(in_shape); - if group.hover == ActivationState::Off { - // TODO: this does not merge if you happen to add a sensor to the widget itself - let m = ctx.ui().modules.get_mut::>(); - m.map.insert(id.clone(), group); + for l in &layer_idxs { + let Some(list) = module.active.get_mut(l) else { continue; - } + }; + let mut ran = false; + for (id, shape) in list.iter() { + let group = module.map.get_mut(id).unwrap(); + let region = shape.to_screen(window_size); + let in_shape = cursor.exists && region.contains(cursor.pos); + group.hover.update(in_shape); + if group.hover == ActivationState::Off { + continue; + } - for sensor in &mut group.sensors { - if should_run(&sensor.senses, &cursor.buttons, group.hover) { - ran = true; - let sctx = SenseData { - cursor: cursor.pos - region.top_left, - size: region.bot_right - region.top_left, - }; - (sensor.f)(ctx, sctx); + for sensor in &mut group.sensors { + if should_run(&sensor.senses, &cursor.buttons, group.hover) { + ran = true; + let sctx = SenseData { + cursor: cursor.pos - region.top_left, + size: region.bot_right - region.top_left, + }; + (sensor.f)(ctx, sctx); + } } } - // TODO: this does not merge if you happen to add a sensor to the widget itself - let m = ctx.ui().modules.get_mut::>(); - m.map.insert(id.clone(), group); + if ran { + break; + } } - if ran { - break; - } - } - ctx.ui().layers = layers; + let ui_mod = ctx.ui().modules.get_mut::(); + std::mem::swap(ui_mod, &mut module); + ui_mod.merge(module); + ctx.ui().layers = layers; + } } pub fn should_run(senses: &Senses, cursor: &CursorButtons, hover: ActivationState) -> bool { @@ -260,6 +264,7 @@ impl Event for Sense { impl EventModule for SensorModule { fn register(&mut self, id: Id, sense: Sense, f: impl EventFn) { + // TODO: does not add to active if currently active self.map.entry(id).or_default().sensors.push(Sensor { senses: sense.into(), f: Box::new(f), diff --git a/src/layout/event.rs b/src/layout/event.rs index 81fa5a5..706c85b 100644 --- a/src/layout/event.rs +++ b/src/layout/event.rs @@ -4,9 +4,13 @@ use crate::{ }; pub trait UiCtx { - fn ui(&mut self) -> &mut Ui - where - Self: Sized; + fn ui(&mut self) -> &mut Ui; +} + +impl UiCtx for Ui { + fn ui(&mut self) -> &mut Ui { + self + } } pub trait Event: Sized { @@ -21,48 +25,36 @@ pub trait EventModule> { pub trait EventFn: FnMut(&mut Ctx, Data) + 'static {} impl EventFn for F {} -pub trait Eventable { - fn on>( +pub trait Eventable { + fn on, Ctx>( self, event: E, f: impl EventFn, - ) -> impl WidgetIdFn + Eventable; + ) -> impl WidgetIdFn + Eventable; - fn id_on>( + fn id_on, Ctx>( self, event: E, f: impl FnMut(&WidgetId, &mut Ctx, E::Data) + 'static, - ) -> impl WidgetIdFn + Eventable + ) -> impl WidgetIdFn + Eventable where W: Widget; - fn edit_on>( + fn edit_on>( self, event: E, f: impl FnMut(&mut W, E::Data) + 'static, - ) -> impl WidgetIdFn + Eventable + ) -> impl WidgetIdFn + Eventable where - W: Widget, - Ctx: UiCtx; + W: Widget; } -pub trait Ctxable { - /// sets context which lets event functions work without needing to specify generics - fn ctx(self) -> impl WidgetLike + Eventable; -} - -impl, Tag> Ctxable for W { - fn ctx(self) -> impl WidgetLike + Eventable { - self - } -} - -impl, Ctx, Tag> Eventable for W { - fn on>( +impl, Tag> Eventable for W { + fn on, Ctx>( self, event: E, f: impl EventFn, - ) -> impl WidgetIdFn + Eventable { + ) -> impl WidgetIdFn { move |ui| { let id = self.add(ui); ui.modules @@ -72,11 +64,11 @@ impl, Ctx, Tag> Eventable for W { } } - fn id_on>( + fn id_on, Ctx>( self, event: E, mut f: impl FnMut(&WidgetId, &mut Ctx, E::Data) + 'static, - ) -> impl WidgetIdFn + Eventable + ) -> impl WidgetIdFn where W::Widget: Widget, { @@ -86,15 +78,14 @@ impl, Ctx, Tag> Eventable for W { }) } - fn edit_on>( + fn edit_on>( self, event: E, mut f: impl FnMut(&mut W::Widget, E::Data) + 'static, - ) -> impl WidgetIdFn + Eventable + ) -> impl WidgetIdFn where W::Widget: Widget, - Ctx: UiCtx, { - self.id_on(event, move |id, ctx, pos| f(&mut ctx.ui()[id], pos)) + self.id_on(event, move |id, ui, pos| f(&mut ui[id], pos)) } } diff --git a/src/testing/mod.rs b/src/testing/mod.rs index 1cb814b..35762b8 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -155,8 +155,7 @@ impl Client { let switch_button = |color, to, label| { let rect = rect(color) - .ctx::() - .id_on(Sense::click(), move |id, ctx, _| { + .id_on(Sense::click(), move |id, ctx: &mut Client, _| { ctx.ui[main].inner.set_static(to); ctx.ui[id].color = color.darker(0.3); }) @@ -212,7 +211,8 @@ impl Client { } if input_changed { let window_size = self.window_size(); - run_sensors(self, &cursor_state, window_size); + SensorModule::run(&mut self.ui, &cursor_state, window_size); + SensorModule::run(self, &cursor_state, window_size); } match event { WindowEvent::CloseRequested => event_loop.exit(), diff --git a/src/util/id.rs b/src/util/id.rs index ed22029..79632e2 100644 --- a/src/util/id.rs +++ b/src/util/id.rs @@ -1,9 +1,9 @@ -/// intentionally does not implement copy or ~~clone~~ HEEELP +/// intentionally does not implement copy or clone /// which should make it harder to misuse; /// the idea is to generally try to guarantee all IDs /// point to something valid, although duplicate /// gets around this if needed -#[derive(Eq, Hash, PartialEq, Debug, Clone)] +#[derive(Eq, Hash, PartialEq, Debug)] pub struct Id(u64); #[derive(Default)]