use crate::prelude::*; use iris_core::util::{HashMap}; use std::{ ops::{BitOr, Deref, DerefMut}, rc::Rc, }; #[derive(Clone, Copy, PartialEq)] pub enum Button { Left, Right, Middle, } #[derive(Clone, Copy, PartialEq)] pub enum CursorSense { PressStart(Button), Pressing(Button), PressEnd(Button), HoverStart, Hovering, HoverEnd, Scroll, } pub struct CursorSenses(Vec); impl CursorSense { pub fn click() -> Self { Self::PressStart(Button::Left) } pub fn click_or_drag() -> CursorSenses { Self::click() | Self::Pressing(Button::Left) } pub fn unclick() -> Self { Self::PressEnd(Button::Left) } pub fn is_dragging(&self) -> bool { matches!(self, CursorSense::Pressing(Button::Left)) } } #[derive(Default, Clone)] pub struct CursorState { pub pos: Vec2, pub exists: bool, pub buttons: CursorButtons, pub scroll_delta: Vec2, } #[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(); } } impl CursorState { pub fn end_frame(&mut self) { self.buttons.end_frame(); self.scroll_delta = Vec2::ZERO; } } #[derive(Debug, Clone, Copy, Default, PartialEq)] pub enum ActivationState { Start, On, End, #[default] Off, } /// this and other similar stuff has a generic /// because I kind of want to make CursorModule generic /// or basically have some way to have custom senses /// that depend on active widget positions /// but I'm not sure how or if worth it pub struct Sensor { pub senses: CursorSenses, pub f: Rc>, } pub type SensorMap = HashMap>; pub type SenseShape = UiRegion; pub struct SensorGroup { pub hover: ActivationState, pub sensors: Vec>, } #[derive(Clone)] pub struct CursorData { pub cursor: Vec2, pub size: Vec2, pub scroll_delta: Vec2, /// the (first) sense that triggered this event /// the senses are checked in order pub sense: CursorSense, } pub struct CursorModule { map: SensorMap, active: HashMap>, } impl UiModule for CursorModule { fn on_draw(&mut self, inst: &ActiveData) { if self.map.contains_key(&inst.id) { self.active .entry(inst.layer) .or_default() .insert(inst.id, inst.region); } } fn on_undraw(&mut self, inst: &ActiveData) { 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); for layer in self.active.values_mut() { layer.remove(id); } } fn on_move(&mut self, inst: &ActiveData) { if let Some(map) = self.active.get_mut(&inst.layer) && let Some(region) = map.get_mut(&inst.id) { *region = inst.region; } } } impl CursorModule { pub fn merge(&mut self, other: Self) { for (id, group) in other.map { for sensor in group.sensors { self.map.entry(id).or_default().sensors.push(sensor); } } } } pub trait SensorUi { fn run_sensors(&mut self, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2); } impl SensorUi for Ui { fn run_sensors( &mut self, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2, ) { CursorModule::::run(self, ctx, cursor, window_size); } } impl CursorModule { pub fn run(ui: &mut Ui, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) { let layers = std::mem::take(&mut ui.data.layers); let mut module = std::mem::take(ui.data.modules.get_mut::()); for i in layers.indices().rev() { let Some(list) = module.active.get_mut(&i) else { continue; }; let mut sensed = false; for (id, shape) in list.iter() { let group = module.map.get_mut(id).unwrap(); let region = shape.to_px(window_size); let in_shape = cursor.exists && region.contains(cursor.pos); group.hover.update(in_shape); if group.hover == ActivationState::Off { continue; } sensed = true; for sensor in &mut group.sensors { if let Some(sense) = should_run(&sensor.senses, cursor, group.hover) { let data = CursorData { cursor: cursor.pos - region.top_left, size: region.bot_right - region.top_left, scroll_delta: cursor.scroll_delta, sense, }; (sensor.f)(EventCtx { ui, state: ctx, data, }); } } } if sensed { break; } } let ui_mod = ui.data.modules.get_mut::(); std::mem::swap(ui_mod, &mut module); ui_mod.merge(module); ui.data.layers = layers; } } pub fn should_run( senses: &CursorSenses, cursor: &CursorState, hover: ActivationState, ) -> Option { for sense in senses.iter() { if match sense { CursorSense::PressStart(button) => cursor.buttons.select(button).is_start(), CursorSense::Pressing(button) => cursor.buttons.select(button).is_on(), CursorSense::PressEnd(button) => cursor.buttons.select(button).is_end(), CursorSense::HoverStart => hover.is_start(), CursorSense::Hovering => hover.is_on(), CursorSense::HoverEnd => hover.is_end(), CursorSense::Scroll => cursor.scroll_delta != Vec2::ZERO, } { return Some(*sense); } } None } 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 CursorSenses { type Module = CursorModule; type Data = CursorData; } impl Event for CursorSense { type Module = CursorModule; type Data = CursorData; } impl::Data> + Into, Ctx: 'static> EventModule for CursorModule { fn register(&mut self, id: Id, senses: E, f: impl EventFn::Data>) { // TODO: does not add to active if currently active self.map.entry(id).or_default().sensors.push(Sensor { senses: senses.into(), f: Rc::new(f), }); } fn run<'a>( &self, id: &Id, event: E, ) -> Option::Data>) + use<'a, E, Ctx>> { let senses = event.into(); if let Some(group) = self.map.get(id) { let fs: Vec<_> = group .sensors .iter() .filter_map(|sensor| { if sensor.senses.iter().any(|s| senses.contains(s)) { Some(sensor.f.clone()) } else { None } }) .collect(); Some(move |ctx: EventCtx| { for f in &fs { f(EventCtx { state: ctx.state, ui: ctx.ui, data: ctx.data.clone(), }); } }) } else { None } } } impl Default for SensorGroup { fn default() -> Self { Self { hover: Default::default(), sensors: Default::default(), } } } impl Deref for CursorSenses { type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for CursorSenses { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl From for CursorSenses { fn from(val: CursorSense) -> Self { CursorSenses(vec![val]) } } impl BitOr for CursorSense { type Output = CursorSenses; fn bitor(self, rhs: Self) -> Self::Output { CursorSenses(vec![self, rhs]) } } impl BitOr for CursorSenses { type Output = Self; fn bitor(mut self, rhs: CursorSense) -> Self::Output { self.0.push(rhs); self } } impl Default for CursorModule { fn default() -> Self { Self { map: Default::default(), active: Default::default(), } } }