make run sensors sane and adjust on_edit to just use ui as ctx (so two run calls needed)

This commit is contained in:
2025-09-24 22:46:55 -04:00
parent 3463682d62
commit 443e13f094
4 changed files with 78 additions and 82 deletions

View File

@@ -3,7 +3,7 @@ use crate::prelude::*;
use std::ops::{BitOr, Deref, DerefMut}; use std::ops::{BitOr, Deref, DerefMut};
use crate::{ use crate::{
layout::{UiModule, UiRegion, Vec2, WidgetId}, layout::{UiModule, UiRegion, Vec2},
util::{HashMap, Id}, util::{HashMap, Id},
}; };
@@ -123,14 +123,23 @@ impl<Ctx: 'static> UiModule for SensorModule<Ctx> {
} }
impl<Ctx> SensorModule<Ctx> { impl<Ctx> SensorModule<Ctx> {
pub fn set<W>(&mut self, id: WidgetId<W>, sensor: Sensor<Ctx>) { pub fn merge(&mut self, other: Self) {
// TODO: currently does not add to active if it exists for (id, group) in other.map {
self.map.entry(id.key()).or_default().sensors.push(sensor); for sensor in group.sensors {
self.map
.entry(id.duplicate())
.or_default()
.sensors
.push(sensor);
}
}
} }
} }
pub fn run_sensors<Ctx: UiCtx + 'static>(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) { impl<Ctx: UiCtx + 'static> SensorModule<Ctx> {
pub fn run(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) {
let mut layers = std::mem::take(&mut ctx.ui().layers); let mut layers = std::mem::take(&mut ctx.ui().layers);
let mut module = std::mem::take(ctx.ui().modules.get_mut::<Self>());
// TODO: temp, need to actually make reverse // TODO: temp, need to actually make reverse
let mut layer_idxs = Vec::new(); let mut layer_idxs = Vec::new();
@@ -139,22 +148,16 @@ pub fn run_sensors<Ctx: UiCtx + 'static>(ctx: &mut Ctx, cursor: &CursorState, wi
} }
for l in &layer_idxs { for l in &layer_idxs {
let m = ctx.ui().modules.get_mut::<SensorModule<Ctx>>(); let Some(list) = module.active.get_mut(l) else {
let Some(list) = m.active.get_mut(l) else {
continue; continue;
}; };
let list = list.clone();
let mut ran = false; let mut ran = false;
for (id, shape) in list.iter() { for (id, shape) in list.iter() {
let m = ctx.ui().modules.get_mut::<SensorModule<Ctx>>(); let group = module.map.get_mut(id).unwrap();
let mut group = m.map.remove(id).unwrap();
let region = shape.to_screen(window_size); let region = shape.to_screen(window_size);
let in_shape = cursor.exists && region.contains(cursor.pos); let in_shape = cursor.exists && region.contains(cursor.pos);
group.hover.update(in_shape); group.hover.update(in_shape);
if group.hover == ActivationState::Off { 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::<SensorModule<Ctx>>();
m.map.insert(id.clone(), group);
continue; continue;
} }
@@ -168,17 +171,18 @@ pub fn run_sensors<Ctx: UiCtx + 'static>(ctx: &mut Ctx, cursor: &CursorState, wi
(sensor.f)(ctx, sctx); (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::<SensorModule<Ctx>>();
m.map.insert(id.clone(), group);
} }
if ran { if ran {
break; break;
} }
} }
let ui_mod = ctx.ui().modules.get_mut::<Self>();
std::mem::swap(ui_mod, &mut module);
ui_mod.merge(module);
ctx.ui().layers = layers; ctx.ui().layers = layers;
} }
}
pub fn should_run(senses: &Senses, cursor: &CursorButtons, hover: ActivationState) -> bool { pub fn should_run(senses: &Senses, cursor: &CursorButtons, hover: ActivationState) -> bool {
for sense in senses.iter() { for sense in senses.iter() {
@@ -260,6 +264,7 @@ impl<Ctx: 'static> Event<Ctx> for Sense {
impl<Ctx: 'static> EventModule<Ctx, Sense> for SensorModule<Ctx> { impl<Ctx: 'static> EventModule<Ctx, Sense> for SensorModule<Ctx> {
fn register(&mut self, id: Id, sense: Sense, f: impl EventFn<Ctx, SenseData>) { fn register(&mut self, id: Id, sense: Sense, f: impl EventFn<Ctx, SenseData>) {
// TODO: does not add to active if currently active
self.map.entry(id).or_default().sensors.push(Sensor { self.map.entry(id).or_default().sensors.push(Sensor {
senses: sense.into(), senses: sense.into(),
f: Box::new(f), f: Box::new(f),

View File

@@ -4,9 +4,13 @@ use crate::{
}; };
pub trait UiCtx { pub trait UiCtx {
fn ui(&mut self) -> &mut Ui fn ui(&mut self) -> &mut Ui;
where }
Self: Sized;
impl UiCtx for Ui {
fn ui(&mut self) -> &mut Ui {
self
}
} }
pub trait Event<Ctx>: Sized { pub trait Event<Ctx>: Sized {
@@ -21,48 +25,36 @@ pub trait EventModule<Ctx, E: Event<Ctx>> {
pub trait EventFn<Ctx, Data>: FnMut(&mut Ctx, Data) + 'static {} pub trait EventFn<Ctx, Data>: FnMut(&mut Ctx, Data) + 'static {}
impl<F: FnMut(&mut Ctx, Data) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {} impl<F: FnMut(&mut Ctx, Data) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {}
pub trait Eventable<W, Ctx, Tag> { pub trait Eventable<W, Tag> {
fn on<E: Event<Ctx>>( fn on<E: Event<Ctx>, Ctx>(
self, self,
event: E, event: E,
f: impl EventFn<Ctx, E::Data>, f: impl EventFn<Ctx, E::Data>,
) -> impl WidgetIdFn<W> + Eventable<W, Ctx, IdFnTag>; ) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>;
fn id_on<E: Event<Ctx>>( fn id_on<E: Event<Ctx>, Ctx>(
self, self,
event: E, event: E,
f: impl FnMut(&WidgetId<W>, &mut Ctx, E::Data) + 'static, f: impl FnMut(&WidgetId<W>, &mut Ctx, E::Data) + 'static,
) -> impl WidgetIdFn<W> + Eventable<W, Ctx, IdFnTag> ) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>
where where
W: Widget; W: Widget;
fn edit_on<E: Event<Ctx>>( fn edit_on<E: Event<Ui>>(
self, self,
event: E, event: E,
f: impl FnMut(&mut W, E::Data) + 'static, f: impl FnMut(&mut W, E::Data) + 'static,
) -> impl WidgetIdFn<W> + Eventable<W, Ctx, IdFnTag> ) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>
where where
W: Widget, W: Widget;
Ctx: UiCtx;
} }
pub trait Ctxable<W, Tag> { impl<W: WidgetLike<Tag>, Tag> Eventable<W::Widget, Tag> for W {
/// sets context which lets event functions work without needing to specify generics fn on<E: Event<Ctx>, Ctx>(
fn ctx<Ctx>(self) -> impl WidgetLike<Tag> + Eventable<W, Ctx, Tag>;
}
impl<W: WidgetLike<Tag>, Tag> Ctxable<W::Widget, Tag> for W {
fn ctx<Ctx>(self) -> impl WidgetLike<Tag> + Eventable<W::Widget, Ctx, Tag> {
self
}
}
impl<W: WidgetLike<Tag>, Ctx, Tag> Eventable<W::Widget, Ctx, Tag> for W {
fn on<E: Event<Ctx>>(
self, self,
event: E, event: E,
f: impl EventFn<Ctx, E::Data>, f: impl EventFn<Ctx, E::Data>,
) -> impl WidgetIdFn<W::Widget> + Eventable<W::Widget, Ctx, IdFnTag> { ) -> impl WidgetIdFn<W::Widget> {
move |ui| { move |ui| {
let id = self.add(ui); let id = self.add(ui);
ui.modules ui.modules
@@ -72,11 +64,11 @@ impl<W: WidgetLike<Tag>, Ctx, Tag> Eventable<W::Widget, Ctx, Tag> for W {
} }
} }
fn id_on<E: Event<Ctx>>( fn id_on<E: Event<Ctx>, Ctx>(
self, self,
event: E, event: E,
mut f: impl FnMut(&WidgetId<W::Widget>, &mut Ctx, E::Data) + 'static, mut f: impl FnMut(&WidgetId<W::Widget>, &mut Ctx, E::Data) + 'static,
) -> impl WidgetIdFn<W::Widget> + Eventable<W::Widget, Ctx, IdFnTag> ) -> impl WidgetIdFn<W::Widget>
where where
W::Widget: Widget, W::Widget: Widget,
{ {
@@ -86,15 +78,14 @@ impl<W: WidgetLike<Tag>, Ctx, Tag> Eventable<W::Widget, Ctx, Tag> for W {
}) })
} }
fn edit_on<E: Event<Ctx>>( fn edit_on<E: Event<Ui>>(
self, self,
event: E, event: E,
mut f: impl FnMut(&mut W::Widget, E::Data) + 'static, mut f: impl FnMut(&mut W::Widget, E::Data) + 'static,
) -> impl WidgetIdFn<W::Widget> + Eventable<W::Widget, Ctx, IdFnTag> ) -> impl WidgetIdFn<W::Widget>
where where
W::Widget: Widget, 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))
} }
} }

View File

@@ -155,8 +155,7 @@ impl Client {
let switch_button = |color, to, label| { let switch_button = |color, to, label| {
let rect = rect(color) let rect = rect(color)
.ctx::<Client>() .id_on(Sense::click(), move |id, ctx: &mut Client, _| {
.id_on(Sense::click(), move |id, ctx, _| {
ctx.ui[main].inner.set_static(to); ctx.ui[main].inner.set_static(to);
ctx.ui[id].color = color.darker(0.3); ctx.ui[id].color = color.darker(0.3);
}) })
@@ -212,7 +211,8 @@ impl Client {
} }
if input_changed { if input_changed {
let window_size = self.window_size(); 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 { match event {
WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::CloseRequested => event_loop.exit(),

View File

@@ -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; /// which should make it harder to misuse;
/// the idea is to generally try to guarantee all IDs /// the idea is to generally try to guarantee all IDs
/// point to something valid, although duplicate /// point to something valid, although duplicate
/// gets around this if needed /// gets around this if needed
#[derive(Eq, Hash, PartialEq, Debug, Clone)] #[derive(Eq, Hash, PartialEq, Debug)]
pub struct Id(u64); pub struct Id(u64);
#[derive(Default)] #[derive(Default)]