use crate::prelude::*; use std::{ ops::{BitOr, Deref, DerefMut}, rc::Rc, }; #[derive(Debug, Clone, Copy, PartialEq)] pub enum CursorButton { Left, Right, Middle, } #[derive(Debug, Clone, Copy, PartialEq)] pub enum CursorSense { PressStart(CursorButton), Pressing(CursorButton), PressEnd(CursorButton), HoverStart, Hovering, HoverEnd, Scroll, } #[derive(Clone)] pub struct CursorSenses(Vec); impl Event for CursorSenses { type Data<'a> = CursorData<'a>; type Global = Hovered; fn should_run<'a>(&self, data: &Self::Data<'a>) -> Option> { if let Some(sense) = should_run(self, &data.cursor, data.hover) { let mut data = data.clone(); data.sense = sense; Some(data) } else { None } } /// A press or a scroll is used up by whatever answered it, so it stops /// there. Hovering is not: a cursor resting somewhere goes on resting. fn consumes(&self, data: &Self::Data<'_>) -> bool { !data.sense.position_only() } } /// Who the cursor was inside, before and after an input. The difference is /// whose hover has ended -- including a widget a higher layer has covered, /// which the walk stops before reaching. /// /// Two buffers that swap rather than one rebuilt, so an input allocates /// nothing once they have grown. #[derive(Default)] pub struct Hovered { was: Vec, now: Vec, } impl CursorSense { pub fn click() -> Self { Self::PressStart(CursorButton::Left) } pub fn click_or_drag() -> CursorSenses { Self::click() | Self::Pressing(CursorButton::Left) } pub fn unclick() -> Self { Self::PressEnd(CursorButton::Left) } pub fn is_dragging(&self) -> bool { matches!(self, CursorSense::Pressing(CursorButton::Left)) } /// False if the sense is a button or a scroll, true if it is only about /// where the cursor is. fn position_only(&self) -> bool { matches!(self, Self::HoverStart | Self::Hovering | Self::HoverEnd) } } #[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: &CursorButton) -> &ActivationState { match button { CursorButton::Left => &self.left, CursorButton::Right => &self.right, CursorButton::Middle => &self.middle, } } pub fn end_frame(&mut self) { self.left.end_frame(); self.middle.end_frame(); self.right.end_frame(); } pub fn iter(&self) -> impl Iterator { [ CursorButton::Left, CursorButton::Middle, CursorButton::Right, ] .into_iter() .map(|b| (b, self.select(&b))) } } impl CursorState { /// True if the cursor is only reporting where it is: no button and no /// scroll this frame. pub fn position_only(&self) -> bool { self.scroll_delta == Vec2::ZERO && self.buttons.iter().all(|(_, state)| state.is_off()) } 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 SenseShape = UiRegion; #[derive(Clone)] pub struct CursorData<'a> { /// where this widget was hit pub pos: Vec2, pub size: Vec2, pub scroll_delta: Vec2, pub hover: ActivationState, pub cursor: CursorState, /// the first sense that triggered this pub sense: CursorSense, pub render: &'a UiRenderState, } pub trait SensorUi { fn run_sensors( &self, rsc: &mut Rsc, state: &mut Rsc::State, cursor: CursorState, ); } impl SensorUi for UiRenderState { fn run_sensors( &self, rsc: &mut Rsc, state: &mut Rsc::State, cursor: CursorState, ) { // in order to remove this take, need to store active list in UiRenderState somehow // this would probably be done through a generic parameter that adds yet another rsc / // state like thing, but local to render state, and is passed to UiRsc events so you can // update it there? let active = std::mem::take(&mut rsc.events_mut().get_type::().active); let mut hovered = std::mem::take(&mut rsc.events_mut().get_type::().global); hovered.now.clear(); let position_only = cursor.position_only(); let region_of = |id| self.window_region(&id); for layer in self.layers.indices().rev() { let mut consumed = false; for id in active.get(&layer).into_flat_iter().map(|(id, _)| *id) { let Some(region) = region_of(id) else { continue; }; if !cursor.exists || !region.contains(PxVec2::from_f32(cursor.pos)) { continue; } hovered.now.push(id); let hover = match hovered.was.contains(&id) { true => ActivationState::On, false => ActivationState::Start, }; // A press or a scroll stops where something answered it, so a // button over a list does not swallow the list's scrolling. consumed |= deliver(self, rsc, state, id, hover, &cursor, region); // A cursor doing neither stops at whatever it is over, so // hovering does not reach through. consumed |= position_only; } // Applied after the layer, never during it: senses on one layer do // not block each other. if consumed { break; } } // Whatever the cursor was inside and is not now, whether it left or a // layer above took the input before the walk reached it. A widget that // stopped being drawn has no region to report and is simply dropped. for &id in &hovered.was { if !hovered.now.contains(&id) && let Some(region) = region_of(id) { deliver(self, rsc, state, id, ActivationState::End, &cursor, region); } } std::mem::swap(&mut hovered.was, &mut hovered.now); let senses = rsc.events_mut().get_type::(); senses.active = active; senses.global = hovered; } } /// Runs one widget's cursor senses, and says whether they used up the input. fn deliver( render: &UiRenderState, rsc: &mut Rsc, state: &mut Rsc::State, id: WidgetId, hover: ActivationState, cursor: &CursorState, region: PixelRegion, ) -> bool { let data = CursorData { pos: cursor.pos - region.top_left.to_f32(), size: region.size().to_f32(), scroll_delta: cursor.scroll_delta, hover, cursor: cursor.clone(), // this does not have any meaning; // might wanna set up Event to have a prepare stage sense: CursorSense::Hovering, render, }; rsc.run_event::(id, data, state) } pub fn should_run( senses: &CursorSenses, cursor: &CursorState, hover: ActivationState, ) -> Option { for sense in senses.iter() { // A widget the cursor is no longer inside senses only its position: // the press that ended its hover landed on something else. if !hover.is_on() && !sense.position_only() { continue; } 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 EventLike for CursorSense { type Event = CursorSenses; fn into_event(self) -> Self::Event { self.into() } } 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 } }