From 26c248dcba05a21f213be954f817e46669edb6d3 Mon Sep 17 00:00:00 2001 From: shadow cat Date: Wed, 24 Sep 2025 16:11:39 -0400 Subject: [PATCH] add module system and move sensor into core with it --- src/core/sense.rs | 416 ++++++++++++++++++++++++++++++++++++------ src/layout/event.rs | 85 +++++++++ src/layout/id.rs | 4 + src/layout/layer.rs | 6 +- src/layout/mod.rs | 6 +- src/layout/module.rs | 34 ++++ src/layout/painter.rs | 15 +- src/layout/sense.rs | 252 ------------------------- src/layout/ui.rs | 27 ++- src/layout/widgets.rs | 3 - src/lib.rs | 3 +- src/testing/input.rs | 2 +- src/util/id.rs | 4 +- 13 files changed, 515 insertions(+), 342 deletions(-) create mode 100644 src/layout/event.rs create mode 100644 src/layout/module.rs delete mode 100644 src/layout/sense.rs diff --git a/src/core/sense.rs b/src/core/sense.rs index 869a14d..0c22130 100644 --- a/src/core/sense.rs +++ b/src/core/sense.rs @@ -1,64 +1,366 @@ use crate::prelude::*; -pub trait Sensable { - fn on(self, sense: impl Into, f: impl SenseFn) -> impl WidgetIdFn; - fn id_on( - self, - senses: impl Into, - f: impl FnMut(&WidgetId, &mut Ctx, SenseCtx) + 'static, - ) -> impl WidgetIdFn - where - W: Widget; - fn edit_on( - self, - senses: impl Into, - f: impl FnMut(&mut W, SenseCtx) + 'static, - ) -> impl WidgetIdFn - where - W: Widget, - Ctx: UiCtx; +use std::ops::{BitOr, Deref, DerefMut}; + +use crate::{ + layout::{UiModule, UiRegion, Vec2, WidgetId}, + util::{HashMap, Id}, +}; + +pub trait CursorCtx { + fn cursor_state(&self) -> &CursorState; } -impl, Ctx, Tag> Sensable for W { - fn on( - self, - senses: impl Into, - f: impl SenseFn, - ) -> impl WidgetIdFn { - move |ui| { - let id = self.add(ui); - ui.add_sensor( - &id, - Sensor { - senses: senses.into(), - f: Box::new(f), - }, - ); - id - } +pub enum Button { + Left, + Right, + Middle, +} + +pub enum Sense { + PressStart(Button), + Pressing(Button), + PressEnd(Button), + HoverStart, + Hovering, + HoverEnd, +} + +pub struct Senses(Vec); + +impl Sense { + pub fn click() -> Self { + Self::PressStart(Button::Left) } - fn id_on( - self, - senses: impl Into, - mut f: impl FnMut(&WidgetId, &mut Ctx, SenseCtx) + 'static, - ) -> impl WidgetIdFn - where - W::Widget: Widget, - { - self.with_id(move |ui, id| { - let id2 = id.clone(); - id.on(senses, move |ctx, pos| f(&id2, ctx, pos)).add(ui) - }) - } - fn edit_on( - self, - senses: impl Into, - mut f: impl FnMut(&mut W::Widget, SenseCtx) + 'static, - ) -> impl WidgetIdFn - where - W::Widget: Widget, - Ctx: UiCtx, - { - self.id_on(senses, move |id, ctx, pos| f(&mut ctx.ui()[id], pos)) + pub fn unclick() -> Self { + Self::PressEnd(Button::Left) + } +} + +#[derive(Default, Clone)] +pub struct CursorState { + pub pos: Vec2, + pub exists: bool, + pub buttons: CursorButtons, +} + +#[derive(Default, Clone)] +pub struct CursorButtons { + pub left: ActivationState, + pub middle: ActivationState, + pub right: ActivationState, +} + +impl CursorButtons { + pub fn select(&self, button: &Button) -> &ActivationState { + match button { + Button::Left => &self.left, + Button::Right => &self.right, + Button::Middle => &self.middle, + } + } + + pub fn end_frame(&mut self) { + self.left.end_frame(); + self.middle.end_frame(); + self.right.end_frame(); + } +} + +#[derive(Debug, Clone, Copy, Default, PartialEq)] +pub enum ActivationState { + Start, + On, + End, + #[default] + Off, +} + +pub struct Sensor { + pub senses: Senses, + pub f: Box>, +} + +pub type SensorMap = HashMap>; +pub type SenseShape = UiRegion; +pub struct SensorGroup { + pub hover: ActivationState, + pub sensors: Vec>, +} + +pub struct SenseData { + pub cursor: Vec2, + pub size: Vec2, +} + +pub trait SenseFn: FnMut(&mut Ctx, SenseData) + 'static {} +impl SenseFn for F {} + +// pub fn run_sensors(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) { +// let mut layers = std::mem::take(&mut ctx.ui().layers); +// let mut map = std::mem::take(&mut ctx.ui().event_map); +// // temp solution, should probably cache somewhere +// let mut bruh = Vec::new(); +// for (i, _) in layers.iter_mut() { +// bruh.push(i) +// } +// for i in bruh.into_iter().rev() { +// let layer = &layers[i]; +// let mut ran = false; +// for (id, shape) in &layer.sensors { +// let group = &mut 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 = SenseCtx { +// cursor: cursor.pos - region.top_left, +// size: region.bot_right - region.top_left, +// }; +// (sensor.f)(ctx, sctx); +// } +// } +// } +// if ran { +// break; +// } +// } +// let ui = ctx.ui(); +// std::mem::swap(&mut ui.event_map, &mut map); +// // TODO: this removes existing sensors (if a new one is added to an id) lol +// // the proper code is easy but writing this comment is easier and I'm tired +// ui.event_map.extend(map); +// ui.layers = layers; +// } + +pub struct SensorModule { + map: SensorMap, + active: HashMap>, +} + +impl UiModule for SensorModule { + fn on_draw(&mut self, inst: &WidgetInstance) { + if self.map.contains_key(&inst.id) { + self.active + .entry(inst.layer) + .or_default() + .insert(inst.id.duplicate(), 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: &Id) { + self.map.remove(id); + } +} + +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 run_sensors(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) { + let mut layers = std::mem::take(&mut ctx.ui().layers); + + // 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); + 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); + } + } + // 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; + } + } + + ctx.ui().layers = layers; +} + +pub fn should_run(senses: &Senses, cursor: &CursorButtons, hover: ActivationState) -> bool { + for sense in senses.iter() { + if match sense { + Sense::PressStart(button) => cursor.select(button).is_start(), + Sense::Pressing(button) => cursor.select(button).is_on(), + Sense::PressEnd(button) => cursor.select(button).is_end(), + Sense::HoverStart => hover.is_start(), + Sense::Hovering => hover.is_on(), + Sense::HoverEnd => hover.is_end(), + } { + return true; + } + } + false +} + +impl ActivationState { + pub fn is_start(&self) -> bool { + *self == Self::Start + } + pub fn is_on(&self) -> bool { + *self == Self::Start || *self == Self::On + } + pub fn is_end(&self) -> bool { + *self == Self::End + } + pub fn is_off(&self) -> bool { + *self == Self::End || *self == Self::Off + } + pub fn update(&mut self, on: bool) { + *self = match *self { + Self::Start => match on { + true => Self::On, + false => Self::End, + }, + Self::On => match on { + true => Self::On, + false => Self::End, + }, + Self::End => match on { + true => Self::Start, + false => Self::Off, + }, + Self::Off => match on { + true => Self::Start, + false => Self::Off, + }, + } + } + + pub fn end_frame(&mut self) { + match self { + Self::Start => *self = Self::On, + Self::End => *self = Self::Off, + _ => (), + } + } +} + +impl Event for Senses { + type Module = SensorModule; + type Data = SenseData; +} + +impl EventModule for SensorModule { + fn register(&mut self, id: Id, senses: Senses, f: impl EventFn) { + self.map.entry(id).or_default().sensors.push(Sensor { + senses, + f: Box::new(f), + }); + } +} + +impl Event for Sense { + type Module = SensorModule; + type Data = SenseData; +} + +impl EventModule for SensorModule { + fn register(&mut self, id: Id, sense: Sense, f: impl EventFn) { + self.map.entry(id).or_default().sensors.push(Sensor { + senses: sense.into(), + f: Box::new(f), + }); + } +} + +impl Default for SensorGroup { + fn default() -> Self { + Self { + hover: Default::default(), + sensors: Default::default(), + } + } +} + +impl Deref for Senses { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Senses { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From for Senses { + fn from(val: Sense) -> Self { + Senses(vec![val]) + } +} + +impl BitOr for Sense { + type Output = Senses; + + fn bitor(self, rhs: Self) -> Self::Output { + Senses(vec![self, rhs]) + } +} + +impl BitOr for Senses { + type Output = Self; + + fn bitor(mut self, rhs: Sense) -> Self::Output { + self.0.push(rhs); + self + } +} + +impl Default for SensorModule { + fn default() -> Self { + Self { + map: Default::default(), + active: Default::default(), + } } } diff --git a/src/layout/event.rs b/src/layout/event.rs new file mode 100644 index 0000000..8202d30 --- /dev/null +++ b/src/layout/event.rs @@ -0,0 +1,85 @@ +use crate::{ + layout::{Ui, UiModule, Widget, WidgetId, WidgetIdFn, WidgetLike}, + util::Id, +}; + +pub trait UiCtx { + fn ui(&mut self) -> &mut Ui + where + Self: Sized; +} + +pub trait Event: Sized { + type Module: UiModule + EventModule + Default; + type Data; +} + +pub trait EventModule> { + fn register(&mut self, id: Id, event: E, f: impl EventFn); +} + +pub trait EventFn: FnMut(&mut Ctx, Data) + 'static {} +impl EventFn for F {} + +pub trait Eventable { + fn on>(self, event: E, f: impl EventFn) -> impl WidgetIdFn; + + fn id_on>( + self, + event: E, + f: impl FnMut(&WidgetId, &mut Ctx, E::Data) + 'static, + ) -> impl WidgetIdFn + where + W: Widget; + + fn edit_on>( + self, + event: E, + f: impl FnMut(&mut W, E::Data) + 'static, + ) -> impl WidgetIdFn + where + W: Widget, + Ctx: UiCtx; +} + +impl, Ctx, Tag> Eventable for W { + fn on>( + self, + event: E, + f: impl EventFn, + ) -> impl WidgetIdFn { + move |ui| { + let id = self.add(ui); + ui.modules + .get_mut::() + .register(id.id.duplicate(), event, f); + id + } + } + + fn id_on>( + self, + event: E, + mut f: impl FnMut(&WidgetId, &mut Ctx, E::Data) + 'static, + ) -> impl WidgetIdFn + where + W::Widget: Widget, + { + self.with_id(move |ui, id| { + let id2 = id.clone(); + id.on(event, move |ctx, pos| f(&id2, ctx, pos)).add(ui) + }) + } + + fn edit_on>( + self, + event: E, + mut f: impl FnMut(&mut W::Widget, E::Data) + 'static, + ) -> impl WidgetIdFn + where + W::Widget: Widget, + Ctx: UiCtx, + { + self.id_on(event, move |id, ctx, pos| f(&mut ctx.ui()[id], pos)) + } +} diff --git a/src/layout/id.rs b/src/layout/id.rs index 640f8eb..c04fba5 100644 --- a/src/layout/id.rs +++ b/src/layout/id.rs @@ -87,6 +87,10 @@ impl WidgetId { unsafe { std::mem::transmute(self) } } + pub fn key(&self) -> Id { + self.id.duplicate() + } + pub(super) fn cast_type(self) -> WidgetId { // safety: self is repr(C) and generic only used for phantom data unsafe { std::mem::transmute(self) } diff --git a/src/layout/layer.rs b/src/layout/layer.rs index 3a8a628..fe65477 100644 --- a/src/layout/layer.rs +++ b/src/layout/layer.rs @@ -1,9 +1,9 @@ use std::ops::{Index, IndexMut}; use crate::{ - layout::{SenseShape, UiRegion}, + layout::UiRegion, render::{Primitive, PrimitiveHandle, Primitives}, - util::{HashMap, Id}, + util::Id, }; struct LayerNode { @@ -26,10 +26,10 @@ pub struct Layers { vec: Vec, } +/// TODO: this can be replaced with Primitives itself atm #[derive(Default)] pub struct Layer { pub primitives: Primitives, - pub sensors: HashMap, } impl Layers { diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 360cbcc..16f63fb 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1,11 +1,12 @@ mod color; +mod event; mod id; mod layer; +mod module; mod num; mod orientation; mod painter; mod pos; -mod sense; mod text; mod texture; mod ui; @@ -14,13 +15,14 @@ mod widget; mod widgets; pub use color::*; +pub use event::*; pub use id::*; pub use layer::*; +pub use module::*; pub use num::*; pub use orientation::*; pub use painter::*; pub use pos::*; -pub use sense::*; pub use text::*; pub use texture::*; pub use ui::*; diff --git a/src/layout/module.rs b/src/layout/module.rs new file mode 100644 index 0000000..5e4feef --- /dev/null +++ b/src/layout/module.rs @@ -0,0 +1,34 @@ +use std::any::{Any, TypeId}; + +use crate::{ + layout::WidgetInstance, + util::{HashMap, Id}, +}; + +#[allow(unused_variables)] +pub trait UiModule: Any { + fn on_draw(&mut self, inst: &WidgetInstance) {} + fn on_undraw(&mut self, inst: &WidgetInstance) {} + fn on_remove(&mut self, id: &Id) {} +} + +#[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/src/layout/painter.rs b/src/layout/painter.rs index ca0ac07..215b44e 100644 --- a/src/layout/painter.rs +++ b/src/layout/painter.rs @@ -1,6 +1,6 @@ use crate::{ layout::{ - ActiveWidgets, Layers, TextAttrs, TextBuffer, TextData, TextOffset, TextureHandle, + ActiveWidgets, Layers, Modules, TextAttrs, TextBuffer, TextData, TextOffset, TextureHandle, Textures, UiRegion, UiVec2, Vec2, WidgetId, Widgets, }, render::{Primitive, PrimitiveHandle}, @@ -25,6 +25,7 @@ pub struct PainterCtx<'a> { pub textures: &'a mut Textures, pub text: &'a mut TextData, pub screen_size: Vec2, + pub modules: &'a mut Modules, drawing: HashSet, } @@ -44,6 +45,7 @@ impl<'a> PainterCtx<'a> { widgets: &'a Widgets, layers: &'a mut Layers, active: &'a mut ActiveWidgets, + modules: &'a mut Modules, textures: &'a mut Textures, text: &'a mut TextData, screen_size: Vec2, @@ -55,6 +57,7 @@ impl<'a> PainterCtx<'a> { textures, text, screen_size, + modules, drawing: HashSet::default(), } } @@ -165,10 +168,8 @@ impl<'a> PainterCtx<'a> { } } - if self.widgets.data(id).is_some_and(|w| w.sensor) { - self.layers[layer] - .sensors - .insert(id.duplicate(), instance.region); + for m in self.modules.iter_mut() { + m.on_draw(&instance); } self.active.insert(id.duplicate(), instance); } @@ -182,7 +183,9 @@ impl<'a> PainterCtx<'a> { } inst.textures.clear(); self.textures.free(); - self.layers[inst.layer].sensors.remove(id); + for m in self.modules.iter_mut() { + m.on_undraw(inst); + } } inst } diff --git a/src/layout/sense.rs b/src/layout/sense.rs deleted file mode 100644 index bed08e3..0000000 --- a/src/layout/sense.rs +++ /dev/null @@ -1,252 +0,0 @@ -use std::ops::{BitOr, Deref, DerefMut}; - -use crate::{ - layout::{Ui, UiRegion, Vec2}, - util::{HashMap, Id}, -}; - -pub trait CursorCtx { - fn cursor_state(&self) -> &CursorState; -} - -pub enum Button { - Left, - Right, - Middle, -} - -pub enum Sense { - PressStart(Button), - Pressing(Button), - PressEnd(Button), - HoverStart, - Hovering, - HoverEnd, -} - -pub struct Senses(Vec); - -impl Sense { - pub fn click() -> Self { - Self::PressStart(Button::Left) - } - pub fn unclick() -> Self { - Self::PressEnd(Button::Left) - } -} - -#[derive(Default, Clone)] -pub struct CursorState { - pub pos: Vec2, - pub exists: bool, - pub buttons: CursorButtons, -} - -#[derive(Default, Clone)] -pub struct CursorButtons { - pub left: ActivationState, - pub middle: ActivationState, - pub right: ActivationState, -} - -impl CursorButtons { - pub fn select(&self, button: &Button) -> &ActivationState { - match button { - Button::Left => &self.left, - Button::Right => &self.right, - Button::Middle => &self.middle, - } - } - - pub fn end_frame(&mut self) { - self.left.end_frame(); - self.middle.end_frame(); - self.right.end_frame(); - } -} - -#[derive(Debug, Clone, Copy, Default, PartialEq)] -pub enum ActivationState { - Start, - On, - End, - #[default] - Off, -} - -pub struct Sensor { - pub senses: Senses, - pub f: Box>, -} - -pub type SensorMap = HashMap>; -pub type SenseShape = UiRegion; -pub struct SensorGroup { - pub hover: ActivationState, - pub sensors: Vec>, -} - -pub struct SenseCtx { - pub cursor: Vec2, - pub size: Vec2, -} - -pub trait SenseFn: FnMut(&mut Ctx, SenseCtx) + 'static {} -impl SenseFn for F {} - -pub trait UiCtx { - fn ui(&mut self) -> &mut Ui - where - Self: Sized; -} - -/// TODO: this takes ui.sensor_map and then puts it back -/// it extends so you can add to it, but you can't actually index sensors with it -/// should something be done about this? -pub fn run_sensors(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) { - let mut layers = std::mem::take(&mut ctx.ui().layers); - let mut map = std::mem::take(&mut ctx.ui().event_map); - // temp solution, should probably cache somewhere - let mut bruh = Vec::new(); - for (i, _) in layers.iter_mut() { - bruh.push(i) - } - for i in bruh.into_iter().rev() { - let layer = &layers[i]; - let mut ran = false; - for (id, shape) in &layer.sensors { - let group = &mut 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 = SenseCtx { - cursor: cursor.pos - region.top_left, - size: region.bot_right - region.top_left, - }; - (sensor.f)(ctx, sctx); - } - } - } - if ran { - break; - } - } - let ui = ctx.ui(); - std::mem::swap(&mut ui.event_map, &mut map); - // TODO: this removes existing sensors (if a new one is added to an id) lol - // the proper code is easy but writing this comment is easier and I'm tired - ui.event_map.extend(map); - ui.layers = layers; -} - -pub fn should_run(senses: &Senses, cursor: &CursorButtons, hover: ActivationState) -> bool { - for sense in senses.iter() { - if match sense { - Sense::PressStart(button) => cursor.select(button).is_start(), - Sense::Pressing(button) => cursor.select(button).is_on(), - Sense::PressEnd(button) => cursor.select(button).is_end(), - Sense::HoverStart => hover.is_start(), - Sense::Hovering => hover.is_on(), - Sense::HoverEnd => hover.is_end(), - } { - return true; - } - } - false -} - -impl ActivationState { - pub fn is_start(&self) -> bool { - *self == Self::Start - } - pub fn is_on(&self) -> bool { - *self == Self::Start || *self == Self::On - } - pub fn is_end(&self) -> bool { - *self == Self::End - } - pub fn is_off(&self) -> bool { - *self == Self::End || *self == Self::Off - } - pub fn update(&mut self, on: bool) { - *self = match *self { - Self::Start => match on { - true => Self::On, - false => Self::End, - }, - Self::On => match on { - true => Self::On, - false => Self::End, - }, - Self::End => match on { - true => Self::Start, - false => Self::Off, - }, - Self::Off => match on { - true => Self::Start, - false => Self::Off, - }, - } - } - - pub fn end_frame(&mut self) { - match self { - Self::Start => *self = Self::On, - Self::End => *self = Self::Off, - _ => (), - } - } -} - -impl Default for SensorGroup { - fn default() -> Self { - Self { - hover: Default::default(), - sensors: Default::default(), - } - } -} - -impl Deref for Senses { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Senses { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl From for Senses { - fn from(val: Sense) -> Self { - Senses(vec![val]) - } -} - -impl BitOr for Sense { - type Output = Senses; - - fn bitor(self, rhs: Self) -> Self::Output { - Senses(vec![self, rhs]) - } -} - -impl BitOr for Senses { - type Output = Self; - - fn bitor(mut self, rhs: Sense) -> Self::Output { - self.0.push(rhs); - self - } -} diff --git a/src/layout/ui.rs b/src/layout/ui.rs index 70c3b8d..fdf181d 100644 --- a/src/layout/ui.rs +++ b/src/layout/ui.rs @@ -3,17 +3,19 @@ use image::DynamicImage; use crate::{ core::{TextEdit, TextEditCtx}, layout::{ - Layers, PainterCtx, Sensor, SensorMap, StaticWidgetId, TextData, TextureHandle, Textures, - Vec2, Widget, WidgetId, WidgetInstance, WidgetLike, Widgets, + Layers, Modules, PainterCtx, StaticWidgetId, TextData, TextureHandle, Textures, Vec2, + Widget, WidgetId, WidgetInstance, WidgetLike, Widgets, }, util::{HashMap, Id}, }; use std::{ any::{Any, TypeId}, + marker::PhantomData, ops::{Index, IndexMut}, sync::mpsc::{Receiver, Sender, channel}, }; +// TODO: remove generic pub struct Ui { root: Option, pub(super) widgets: Widgets, @@ -28,7 +30,8 @@ pub struct Ui { full_redraw: bool, pub(crate) active: ActiveWidgets, - pub(super) event_map: SensorMap, + pub modules: Modules, + _pd: PhantomData, } impl Ui { @@ -114,6 +117,7 @@ impl Ui { &self.widgets, &mut self.layers, &mut self.active, + &mut self.modules, &mut self.textures, &mut self.text, self.size, @@ -137,6 +141,7 @@ impl Ui { &self.widgets, &mut self.layers, &mut self.active, + &mut self.modules, &mut self.textures, &mut self.text, self.size, @@ -150,7 +155,9 @@ impl Ui { /// free any resources that don't have references anymore fn free(&mut self) { for id in self.recv.try_iter() { - self.event_map.remove(&id); + for m in self.modules.iter_mut() { + m.on_remove(&id); + } self.widgets.delete(id); } self.textures.free(); @@ -168,15 +175,6 @@ impl Ui { self.active.len() } - pub fn add_sensor(&mut self, id: &WidgetId, f: Sensor) { - self.event_map - .entry(id.id.duplicate()) - .or_default() - .sensors - .push(f); - self.widgets.data_mut(&id.id).unwrap().sensor = true; - } - pub fn text(&mut self, id: &WidgetId) -> TextEditCtx<'_> { self.updates.push(id.id.duplicate()); TextEditCtx { @@ -238,10 +236,11 @@ impl Default for Ui { text: TextData::default(), full_redraw: false, active: Default::default(), - event_map: Default::default(), send, recv, size: Vec2::ZERO, + modules: Modules::default(), + _pd: PhantomData, } } } diff --git a/src/layout/widgets.rs b/src/layout/widgets.rs index 4539ecd..c985376 100644 --- a/src/layout/widgets.rs +++ b/src/layout/widgets.rs @@ -14,8 +14,6 @@ pub struct WidgetData { pub label: String, /// dynamic borrow checking pub borrowed: bool, - /// whether this widget has any sensors - pub sensor: bool, } impl Widgets { @@ -85,7 +83,6 @@ impl Widgets { widget, label, borrowed: false, - sensor: false, }, ); } diff --git a/src/lib.rs b/src/lib.rs index d9852ed..ef028d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,9 @@ #![feature(macro_metavar_expr_concat)] #![feature(const_ops)] #![feature(const_trait_impl)] -#![feature(const_from)] +#![feature(const_convert)] #![feature(map_try_insert)] #![feature(trait_alias)] -#![feature(round_char_boundary)] #![feature(unboxed_closures)] #![feature(fn_traits)] diff --git a/src/testing/input.rs b/src/testing/input.rs index bd273cc..dd9014c 100644 --- a/src/testing/input.rs +++ b/src/testing/input.rs @@ -1,4 +1,4 @@ -use ui::layout::{CursorState, Vec2}; +use ui::{core::CursorState, layout::Vec2}; use winit::event::{MouseButton, WindowEvent}; use crate::testing::Client; diff --git a/src/util/id.rs b/src/util/id.rs index 79632e2..ed22029 100644 --- a/src/util/id.rs +++ b/src/util/id.rs @@ -1,9 +1,9 @@ -/// intentionally does not implement copy or clone +/// intentionally does not implement copy or ~~clone~~ HEEELP /// 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)] +#[derive(Eq, Hash, PartialEq, Debug, Clone)] pub struct Id(u64); #[derive(Default)]