use crate::{ layout::{Ui, UiRegion, Vec2}, util::{HashMap, Id, bitflags}, }; bitflags!(Sense, Senses, senses { 1 << 0; PressStart, PRESS_START, 1 << 1; Pressing, PRESSING, 1 << 2; PressEnd, PRESS_END, 1 << 3; HoverStart, HOVER_START, 1 << 4; Hovering, HOVERING, 1 << 5; HoverEnd, HOVER_END, 1 << 6; NotHovering, NOT_HOVERING, }); 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 senses: Senses, pub f: Box>, } pub type SensorMap = HashMap>; pub type ActiveSensors = HashMap; pub type SenseShape = UiRegion; pub struct SensorGroup { pub hover: ActivationState, pub cursor: 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; } pub fn run_sensors(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) { let active = std::mem::take(&mut ctx.ui().active.sensors); let mut map = std::mem::take(&mut ctx.ui().sensor_map); for (id, shape) in active.iter() { 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.senses, group.cursor, group.hover) { let sctx = SenseCtx { cursor: cursor.pos - region.top_left, size: region.bot_right - region.top_left, }; (sensor.f)(ctx, sctx); } } } ctx.ui().sensor_map = map; ctx.ui().active.sensors = active; } pub fn should_run(senses: Senses, cursor: ActivationState, hover: ActivationState) -> bool { for sense in senses.iter() { if 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(), } { 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, }, } } } impl Default for SensorGroup { fn default() -> Self { Self { hover: Default::default(), cursor: Default::default(), sensors: Default::default(), } } }