actually sane sensor handling

This commit is contained in:
2025-08-16 00:56:00 -04:00
parent f4aef3a983
commit 11188f2951
9 changed files with 306 additions and 117 deletions

View File

@@ -1,5 +1,5 @@
use crate::{
ActiveSensor, ActiveSensors, SenseTrigger, SensorMap, UiRegion, WidgetId, Widgets,
ActiveSensors, SensorMap, UiRegion, WidgetId, Widgets,
primitive::{PrimitiveData, PrimitiveInstance, Primitives},
};
@@ -7,7 +7,7 @@ pub struct Painter<'a, Ctx: 'static> {
nodes: &'a Widgets<Ctx>,
ctx: &'a mut Ctx,
sensors_map: &'a mut SensorMap<Ctx>,
active_sensors: &'a mut ActiveSensors<Ctx>,
active_sensors: &'a mut ActiveSensors,
primitives: Primitives,
pub region: UiRegion,
}
@@ -17,7 +17,7 @@ impl<'a, Ctx> Painter<'a, Ctx> {
nodes: &'a Widgets<Ctx>,
ctx: &'a mut Ctx,
sensors_map: &'a mut SensorMap<Ctx>,
active_sensors: &'a mut ActiveSensors<Ctx>,
active_sensors: &'a mut ActiveSensors,
) -> Self {
Self {
nodes,
@@ -44,19 +44,8 @@ impl<'a, Ctx> Painter<'a, Ctx> {
where
Ctx: 'static,
{
if let Some(sensors) = self.sensors_map.get(&id.id) {
self.active_sensors.push(
sensors
.iter()
.map(|sensor| ActiveSensor {
trigger: SenseTrigger {
shape: self.region,
sense: sensor.sense,
},
f: sensor.f.box_clone(),
})
.collect(),
);
if self.sensors_map.get(&id.id).is_some() {
self.active_sensors.push((self.region, id.id.duplicate()));
}
self.nodes.get_dyn(id).draw(self);
}

View File

@@ -174,9 +174,7 @@ impl UiRegion {
}
pub fn shifted(mut self, offset: impl Into<Vec2>) -> Self {
println!("before {:?}", self);
self.shift(offset);
println!("after {:?}", self);
self
}

View File

@@ -1,11 +1,29 @@
use crate::{HashMap, Ui, UiRegion, util::Id};
use crate::{HashMap, Ui, UiRegion, Vec2, WidgetId, util::Id};
#[derive(Clone, Copy, PartialEq)]
pub enum Sense {
Press,
Held,
Hover,
NoHover,
PressStart,
Pressing,
PressEnd,
HoverStart,
Hovering,
HoverEnd,
NotHovering,
}
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> {
@@ -13,20 +31,8 @@ pub struct Sensor<Ctx> {
pub f: Box<dyn SenseFn<Ctx>>,
}
pub struct ActiveSensor<Ctx> {
pub trigger: SenseTrigger,
pub f: Box<dyn SenseFn<Ctx>>,
}
impl<Ctx: 'static> Clone for ActiveSensor<Ctx> {
fn clone(&self) -> Self {
Self {
trigger: self.trigger.clone(),
f: self.f.box_clone(),
}
}
}
pub type SensorMap<Ctx> = HashMap<Id, Vec<Sensor<Ctx>>>;
pub type ActiveSensors<Ctx> = Vec<Vec<ActiveSensor<Ctx>>>;
pub type SensorMap<Ctx> = HashMap<Id, SensorGroup<Ctx>>;
pub type ActiveSensors = Vec<(SenseShape, Id)>;
pub trait SenseFn_<Ctx> = FnMut(&mut Ui<Ctx>, &mut Ctx) + 'static;
pub type SenseShape = UiRegion;
#[derive(Clone)]
@@ -34,6 +40,11 @@ pub struct SenseTrigger {
pub shape: SenseShape,
pub sense: Sense,
}
pub struct SensorGroup<Ctx> {
pub hover: ActivationState,
pub cursor: ActivationState,
pub sensors: Vec<Sensor<Ctx>>,
}
pub trait SenseFn<Ctx>: SenseFn_<Ctx> {
fn box_clone(&self) -> Box<dyn SenseFn<Ctx>>;
}
@@ -42,3 +53,110 @@ impl<F: SenseFn_<Ctx> + Clone, Ctx> SenseFn<Ctx> for F {
Box::new(self.clone())
}
}
impl<Ctx> Ui<Ctx> {
pub fn add_sensor<W>(&mut self, id: &WidgetId<W>, f: Sensor<Ctx>) {
self.sensor_map
.entry(id.id.duplicate())
.or_default()
.sensors
.push(f);
}
pub fn run_sensors(&mut self, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2)
where
Ctx: 'static,
{
let mut active = std::mem::take(&mut self.active_sensors);
let mut map = std::mem::take(&mut self.sensor_map);
for (shape, id) in active.iter_mut().rev() {
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.sense, group.cursor, group.hover) {
(sensor.f.box_clone())(self, ctx);
}
}
}
self.sensor_map = map;
self.active_sensors = active;
}
}
pub fn should_run(sense: Sense, cursor: ActivationState, hover: ActivationState) -> bool {
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(),
}
}
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(),
}
}
}
impl<Ctx: 'static> Clone for SensorGroup<Ctx> {
fn clone(&self) -> Self {
Self {
hover: self.hover,
cursor: self.cursor,
sensors: self.sensors.clone(),
}
}
}
impl<Ctx: 'static> Clone for Sensor<Ctx> {
fn clone(&self) -> Self {
Self {
sense: self.sense,
f: self.f.box_clone(),
}
}
}

View File

@@ -1,5 +1,5 @@
use crate::{
ActiveSensors, HashMap, Painter, SenseCtx, Sensor, SensorMap, Widget, WidgetId, WidgetLike,
ActiveSensors, HashMap, Painter, SensorMap, Widget, WidgetId, WidgetLike,
primitive::Primitives,
util::{IDTracker, Id},
};
@@ -13,8 +13,8 @@ pub struct Ui<Ctx> {
base: Option<WidgetId>,
widgets: Widgets<Ctx>,
updates: Vec<WidgetId>,
active_sensors: ActiveSensors<Ctx>,
sensor_map: SensorMap<Ctx>,
pub(super) active_sensors: ActiveSensors,
pub(super) sensor_map: SensorMap<Ctx>,
primitives: Primitives,
full_redraw: bool,
}
@@ -85,26 +85,6 @@ impl<Ctx> Ui<Ctx> {
self.primitives = painter.finish();
}
pub fn add_sensor<W>(&mut self, id: &WidgetId<W>, f: Sensor<Ctx>) {
self.sensor_map
.entry(id.id.duplicate())
.or_default()
.push(f);
}
pub fn run_sensors(&mut self, ctx: &mut Ctx)
where
Ctx: SenseCtx + 'static,
{
for sensors in self.active_sensors.clone().iter().rev() {
for sensor in sensors {
if ctx.active(&sensor.trigger) {
(sensor.f.box_clone())(self, ctx);
}
}
}
}
pub fn update(&mut self, ctx: &mut Ctx) -> Option<&Primitives>
where
Ctx: 'static,