use crate::{HashMap, Ui, UiRegion, Vec2, WidgetId, util::Id}; #[derive(Clone, Copy, PartialEq)] pub enum Sense { PressStart, Pressing, PressEnd, HoverStart, Hovering, HoverEnd, NotHovering, } pub struct CursorState { pub pos: Vec2, pub exists: bool, pub pressed: bool, } #[derive(Debug, Clone, Copy, Default, PartialEq)] pub enum ActivationState { Start, On, End, #[default] Off, } pub struct Sensor { pub sense: Sense, pub f: Box>, } pub type SensorMap = HashMap>; pub type ActiveSensors = Vec<(SenseShape, Id)>; pub type SenseShape = UiRegion; #[derive(Clone)] pub struct SenseTrigger { pub shape: SenseShape, pub sense: Sense, } pub struct SensorGroup { pub hover: ActivationState, pub cursor: ActivationState, pub sensors: Vec>, } pub struct SenseCtx<'a, Ctx> { pub ui: &'a mut Ui, pub app: &'a mut Ctx, } pub trait SenseFn: FnMut(SenseCtx) + 'static { fn box_clone(&self) -> Box>; } impl) + 'static + Clone, Ctx> SenseFn for F { fn box_clone(&self) -> Box> { Box::new(self.clone()) } } impl Ui { pub fn add_sensor(&mut self, id: &WidgetId, f: Sensor) { self.sensor_map .entry(id.id.duplicate()) .or_default() .sensors .push(f); } pub fn run_sensors(&mut self, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) where Ctx: 'static, { let mut active = std::mem::take(&mut self.active_sensors); let mut map = std::mem::take(&mut self.sensor_map); for (shape, id) in active.iter_mut().rev() { 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); group.cursor.update(cursor.pressed && in_shape); for sensor in &mut group.sensors { if should_run(sensor.sense, group.cursor, group.hover) { (sensor.f.box_clone())(SenseCtx { ui: self, app: ctx, }); } } } self.sensor_map = map; self.active_sensors = active; } } pub fn should_run(sense: Sense, cursor: ActivationState, hover: ActivationState) -> bool { match sense { Sense::PressStart => cursor.is_start(), Sense::Pressing => cursor.is_on(), Sense::PressEnd => cursor.is_end(), Sense::HoverStart => hover.is_start(), Sense::Hovering => hover.is_on(), Sense::HoverEnd => hover.is_end(), Sense::NotHovering => hover.is_off(), } } 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, }, } } } impl Default for SensorGroup { fn default() -> Self { Self { hover: Default::default(), cursor: Default::default(), sensors: Default::default(), } } } impl Clone for SensorGroup { fn clone(&self) -> Self { Self { hover: self.hover, cursor: self.cursor, sensors: self.sensors.clone(), } } } impl Clone for Sensor { fn clone(&self) -> Self { Self { sense: self.sense, f: self.f.box_clone(), } } }