153 lines
4.0 KiB
Rust
153 lines
4.0 KiB
Rust
use crate::{
|
|
layout::{Ui, UiRegion, Vec2, WidgetId},
|
|
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<dyn SenseFn>,
|
|
}
|
|
|
|
pub type SensorMap = HashMap<Id, SensorGroup>;
|
|
pub type ActiveSensors = HashMap<Id, SenseShape>;
|
|
pub type SenseShape = UiRegion;
|
|
#[derive(Default)]
|
|
pub struct SensorGroup {
|
|
pub hover: ActivationState,
|
|
pub cursor: ActivationState,
|
|
pub sensors: Vec<Sensor>,
|
|
}
|
|
pub trait SenseFn: FnMut(&mut Ui) + 'static {
|
|
fn box_clone(&self) -> Box<dyn SenseFn>;
|
|
}
|
|
impl<F: FnMut(&mut Ui) + 'static + Clone> SenseFn for F {
|
|
fn box_clone(&self) -> Box<dyn SenseFn> {
|
|
Box::new(self.clone())
|
|
}
|
|
}
|
|
|
|
impl Ui {
|
|
pub fn add_sensor<W>(&mut self, id: &WidgetId<W>, f: Sensor) {
|
|
self.sensor_map
|
|
.entry(id.id.duplicate())
|
|
.or_default()
|
|
.sensors
|
|
.push(f);
|
|
}
|
|
|
|
pub fn run_sensors(&mut self, cursor: &CursorState, window_size: Vec2) {
|
|
let active = std::mem::take(&mut self.active.sensors);
|
|
let mut map = std::mem::take(&mut self.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) {
|
|
(sensor.f.box_clone())(self);
|
|
}
|
|
}
|
|
}
|
|
self.sensor_map = map;
|
|
self.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 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 {
|
|
senses: self.senses,
|
|
f: self.f.box_clone(),
|
|
}
|
|
}
|
|
}
|