Files
iris/src/layout/sense.rs

144 lines
3.8 KiB
Rust

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<Ctx> {
pub senses: Senses,
pub f: Box<dyn SenseFn<Ctx>>,
}
pub type SensorMap<Ctx> = HashMap<Id, SensorGroup<Ctx>>;
pub type ActiveSensors = HashMap<Id, SenseShape>;
pub type SenseShape = UiRegion;
pub struct SensorGroup<Ctx> {
pub hover: ActivationState,
pub cursor: ActivationState,
pub sensors: Vec<Sensor<Ctx>>,
}
pub struct SenseCtx {
pub cursor: Vec2,
pub size: Vec2,
}
pub trait SenseFn<Ctx>: FnMut(&mut Ctx, SenseCtx) + 'static {}
impl<F: FnMut(&mut Ctx, SenseCtx) + 'static, Ctx> SenseFn<Ctx> for F {}
pub trait UiCtx {
fn ui(&mut self) -> &mut Ui<Self>
where
Self: Sized;
}
pub fn run_sensors<Ctx: UiCtx>(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<Ctx> Default for SensorGroup<Ctx> {
fn default() -> Self {
Self {
hover: Default::default(),
cursor: Default::default(),
sensors: Default::default(),
}
}
}