add module system and move sensor into core with it

This commit is contained in:
2025-09-24 16:11:39 -04:00
parent 2adf7a43a1
commit 26c248dcba
13 changed files with 515 additions and 342 deletions

View File

@@ -1,64 +1,366 @@
use crate::prelude::*; use crate::prelude::*;
pub trait Sensable<W, Ctx, Tag> { use std::ops::{BitOr, Deref, DerefMut};
fn on(self, sense: impl Into<Senses>, f: impl SenseFn<Ctx>) -> impl WidgetIdFn<W, Ctx>;
fn id_on( use crate::{
self, layout::{UiModule, UiRegion, Vec2, WidgetId},
senses: impl Into<Senses>, util::{HashMap, Id},
f: impl FnMut(&WidgetId<W>, &mut Ctx, SenseCtx) + 'static, };
) -> impl WidgetIdFn<W, Ctx>
where pub trait CursorCtx {
W: Widget; fn cursor_state(&self) -> &CursorState;
fn edit_on(
self,
senses: impl Into<Senses>,
f: impl FnMut(&mut W, SenseCtx) + 'static,
) -> impl WidgetIdFn<W, Ctx>
where
W: Widget,
Ctx: UiCtx;
} }
impl<W: WidgetLike<Ctx, Tag>, Ctx, Tag> Sensable<W::Widget, Ctx, Tag> for W { pub enum Button {
fn on( Left,
self, Right,
senses: impl Into<Senses>, Middle,
f: impl SenseFn<Ctx>, }
) -> impl WidgetIdFn<W::Widget, Ctx> {
move |ui| { pub enum Sense {
let id = self.add(ui); PressStart(Button),
ui.add_sensor( Pressing(Button),
&id, PressEnd(Button),
Sensor { HoverStart,
senses: senses.into(), Hovering,
f: Box::new(f), HoverEnd,
}
pub struct Senses(Vec<Sense>);
impl Sense {
pub fn click() -> Self {
Self::PressStart(Button::Left)
}
pub fn unclick() -> Self {
Self::PressEnd(Button::Left)
}
}
#[derive(Default, Clone)]
pub struct CursorState {
pub pos: Vec2,
pub exists: bool,
pub buttons: CursorButtons,
}
#[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();
}
}
#[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 SenseShape = UiRegion;
pub struct SensorGroup<Ctx> {
pub hover: ActivationState,
pub sensors: Vec<Sensor<Ctx>>,
}
pub struct SenseData {
pub cursor: Vec2,
pub size: Vec2,
}
pub trait SenseFn<Ctx>: FnMut(&mut Ctx, SenseData) + 'static {}
impl<F: FnMut(&mut Ctx, SenseData) + 'static, Ctx> SenseFn<Ctx> for F {}
// pub fn run_sensors<Ctx: UiCtx>(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) {
// let mut layers = std::mem::take(&mut ctx.ui().layers);
// let mut map = std::mem::take(&mut ctx.ui().event_map);
// // temp solution, should probably cache somewhere
// let mut bruh = Vec::new();
// for (i, _) in layers.iter_mut() {
// bruh.push(i)
// }
// for i in bruh.into_iter().rev() {
// let layer = &layers[i];
// let mut ran = false;
// for (id, shape) in &layer.sensors {
// 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);
// if group.hover == ActivationState::Off {
// continue;
// }
//
// for sensor in &mut group.sensors {
// if should_run(&sensor.senses, &cursor.buttons, group.hover) {
// ran = true;
// let sctx = SenseCtx {
// cursor: cursor.pos - region.top_left,
// size: region.bot_right - region.top_left,
// };
// (sensor.f)(ctx, sctx);
// }
// }
// }
// if ran {
// break;
// }
// }
// let ui = ctx.ui();
// std::mem::swap(&mut ui.event_map, &mut map);
// // TODO: this removes existing sensors (if a new one is added to an id) lol
// // the proper code is easy but writing this comment is easier and I'm tired
// ui.event_map.extend(map);
// ui.layers = layers;
// }
pub struct SensorModule<Ctx> {
map: SensorMap<Ctx>,
active: HashMap<usize, HashMap<Id, SenseShape>>,
}
impl<Ctx: 'static> UiModule for SensorModule<Ctx> {
fn on_draw(&mut self, inst: &WidgetInstance) {
if self.map.contains_key(&inst.id) {
self.active
.entry(inst.layer)
.or_default()
.insert(inst.id.duplicate(), inst.region);
}
}
fn on_undraw(&mut self, inst: &WidgetInstance) {
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);
}
}
impl<Ctx> SensorModule<Ctx> {
pub fn set<W>(&mut self, id: WidgetId<W>, sensor: Sensor<Ctx>) {
// TODO: currently does not add to active if it exists
self.map.entry(id.key()).or_default().sensors.push(sensor);
}
}
pub fn run_sensors<Ctx: UiCtx + 'static>(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) {
let mut layers = std::mem::take(&mut ctx.ui().layers);
// TODO: temp, need to actually make reverse
let mut layer_idxs = Vec::new();
for (i, _) in layers.iter_mut() {
layer_idxs.push(i);
}
for l in &layer_idxs {
let m = ctx.ui().modules.get_mut::<SensorModule<Ctx>>();
let Some(list) = m.active.get_mut(l) else {
continue;
};
let list = list.clone();
let mut ran = false;
for (id, shape) in list.iter() {
let m = ctx.ui().modules.get_mut::<SensorModule<Ctx>>();
let mut group = m.map.remove(id).unwrap();
let region = shape.to_screen(window_size);
let in_shape = cursor.exists && region.contains(cursor.pos);
group.hover.update(in_shape);
if group.hover == ActivationState::Off {
// TODO: this does not merge if you happen to add a sensor to the widget itself
let m = ctx.ui().modules.get_mut::<SensorModule<Ctx>>();
m.map.insert(id.clone(), group);
continue;
}
for sensor in &mut group.sensors {
if should_run(&sensor.senses, &cursor.buttons, group.hover) {
ran = true;
let sctx = SenseData {
cursor: cursor.pos - region.top_left,
size: region.bot_right - region.top_left,
};
(sensor.f)(ctx, sctx);
}
}
// TODO: this does not merge if you happen to add a sensor to the widget itself
let m = ctx.ui().modules.get_mut::<SensorModule<Ctx>>();
m.map.insert(id.clone(), group);
}
if ran {
break;
}
}
ctx.ui().layers = layers;
}
pub fn should_run(senses: &Senses, cursor: &CursorButtons, hover: ActivationState) -> bool {
for sense in senses.iter() {
if match sense {
Sense::PressStart(button) => cursor.select(button).is_start(),
Sense::Pressing(button) => cursor.select(button).is_on(),
Sense::PressEnd(button) => cursor.select(button).is_end(),
Sense::HoverStart => hover.is_start(),
Sense::Hovering => hover.is_on(),
Sense::HoverEnd => hover.is_end(),
} {
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,
}, },
);
id
} }
} }
fn id_on(
self, pub fn end_frame(&mut self) {
senses: impl Into<Senses>, match self {
mut f: impl FnMut(&WidgetId<W::Widget>, &mut Ctx, SenseCtx) + 'static, Self::Start => *self = Self::On,
) -> impl WidgetIdFn<W::Widget, Ctx> Self::End => *self = Self::Off,
where _ => (),
W::Widget: Widget, }
{ }
self.with_id(move |ui, id| { }
let id2 = id.clone();
id.on(senses, move |ctx, pos| f(&id2, ctx, pos)).add(ui) impl<Ctx: 'static> Event<Ctx> for Senses {
}) type Module = SensorModule<Ctx>;
} type Data = SenseData;
fn edit_on( }
self,
senses: impl Into<Senses>, impl<Ctx: 'static> EventModule<Ctx, Senses> for SensorModule<Ctx> {
mut f: impl FnMut(&mut W::Widget, SenseCtx) + 'static, fn register(&mut self, id: Id, senses: Senses, f: impl EventFn<Ctx, SenseData>) {
) -> impl WidgetIdFn<W::Widget, Ctx> self.map.entry(id).or_default().sensors.push(Sensor {
where senses,
W::Widget: Widget, f: Box::new(f),
Ctx: UiCtx, });
{ }
self.id_on(senses, move |id, ctx, pos| f(&mut ctx.ui()[id], pos)) }
impl<Ctx: 'static> Event<Ctx> for Sense {
type Module = SensorModule<Ctx>;
type Data = SenseData;
}
impl<Ctx: 'static> EventModule<Ctx, Sense> for SensorModule<Ctx> {
fn register(&mut self, id: Id, sense: Sense, f: impl EventFn<Ctx, SenseData>) {
self.map.entry(id).or_default().sensors.push(Sensor {
senses: sense.into(),
f: Box::new(f),
});
}
}
impl<Ctx> Default for SensorGroup<Ctx> {
fn default() -> Self {
Self {
hover: Default::default(),
sensors: Default::default(),
}
}
}
impl Deref for Senses {
type Target = Vec<Sense>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Senses {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<Sense> for Senses {
fn from(val: Sense) -> Self {
Senses(vec![val])
}
}
impl BitOr for Sense {
type Output = Senses;
fn bitor(self, rhs: Self) -> Self::Output {
Senses(vec![self, rhs])
}
}
impl BitOr<Sense> for Senses {
type Output = Self;
fn bitor(mut self, rhs: Sense) -> Self::Output {
self.0.push(rhs);
self
}
}
impl<Ctx> Default for SensorModule<Ctx> {
fn default() -> Self {
Self {
map: Default::default(),
active: Default::default(),
}
} }
} }

85
src/layout/event.rs Normal file
View File

@@ -0,0 +1,85 @@
use crate::{
layout::{Ui, UiModule, Widget, WidgetId, WidgetIdFn, WidgetLike},
util::Id,
};
pub trait UiCtx {
fn ui(&mut self) -> &mut Ui<Self>
where
Self: Sized;
}
pub trait Event<Ctx>: Sized {
type Module: UiModule + EventModule<Ctx, Self> + Default;
type Data;
}
pub trait EventModule<Ctx, E: Event<Ctx>> {
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, E::Data>);
}
pub trait EventFn<Ctx, Data>: FnMut(&mut Ctx, Data) + 'static {}
impl<F: FnMut(&mut Ctx, Data) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {}
pub trait Eventable<W, Ctx, Tag> {
fn on<E: Event<Ctx>>(self, event: E, f: impl EventFn<Ctx, E::Data>) -> impl WidgetIdFn<W, Ctx>;
fn id_on<E: Event<Ctx>>(
self,
event: E,
f: impl FnMut(&WidgetId<W>, &mut Ctx, E::Data) + 'static,
) -> impl WidgetIdFn<W, Ctx>
where
W: Widget;
fn edit_on<E: Event<Ctx>>(
self,
event: E,
f: impl FnMut(&mut W, E::Data) + 'static,
) -> impl WidgetIdFn<W, Ctx>
where
W: Widget,
Ctx: UiCtx;
}
impl<W: WidgetLike<Ctx, Tag>, Ctx, Tag> Eventable<W::Widget, Ctx, Tag> for W {
fn on<E: Event<Ctx>>(
self,
event: E,
f: impl EventFn<Ctx, E::Data>,
) -> impl WidgetIdFn<W::Widget, Ctx> {
move |ui| {
let id = self.add(ui);
ui.modules
.get_mut::<E::Module>()
.register(id.id.duplicate(), event, f);
id
}
}
fn id_on<E: Event<Ctx>>(
self,
event: E,
mut f: impl FnMut(&WidgetId<W::Widget>, &mut Ctx, E::Data) + 'static,
) -> impl WidgetIdFn<W::Widget, Ctx>
where
W::Widget: Widget,
{
self.with_id(move |ui, id| {
let id2 = id.clone();
id.on(event, move |ctx, pos| f(&id2, ctx, pos)).add(ui)
})
}
fn edit_on<E: Event<Ctx>>(
self,
event: E,
mut f: impl FnMut(&mut W::Widget, E::Data) + 'static,
) -> impl WidgetIdFn<W::Widget, Ctx>
where
W::Widget: Widget,
Ctx: UiCtx,
{
self.id_on(event, move |id, ctx, pos| f(&mut ctx.ui()[id], pos))
}
}

View File

@@ -87,6 +87,10 @@ impl<W> WidgetId<W> {
unsafe { std::mem::transmute(self) } unsafe { std::mem::transmute(self) }
} }
pub fn key(&self) -> Id {
self.id.duplicate()
}
pub(super) fn cast_type<W2>(self) -> WidgetId<W2> { pub(super) fn cast_type<W2>(self) -> WidgetId<W2> {
// safety: self is repr(C) and generic only used for phantom data // safety: self is repr(C) and generic only used for phantom data
unsafe { std::mem::transmute(self) } unsafe { std::mem::transmute(self) }

View File

@@ -1,9 +1,9 @@
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
use crate::{ use crate::{
layout::{SenseShape, UiRegion}, layout::UiRegion,
render::{Primitive, PrimitiveHandle, Primitives}, render::{Primitive, PrimitiveHandle, Primitives},
util::{HashMap, Id}, util::Id,
}; };
struct LayerNode { struct LayerNode {
@@ -26,10 +26,10 @@ pub struct Layers {
vec: Vec<LayerNode>, vec: Vec<LayerNode>,
} }
/// TODO: this can be replaced with Primitives itself atm
#[derive(Default)] #[derive(Default)]
pub struct Layer { pub struct Layer {
pub primitives: Primitives, pub primitives: Primitives,
pub sensors: HashMap<Id, SenseShape>,
} }
impl Layers { impl Layers {

View File

@@ -1,11 +1,12 @@
mod color; mod color;
mod event;
mod id; mod id;
mod layer; mod layer;
mod module;
mod num; mod num;
mod orientation; mod orientation;
mod painter; mod painter;
mod pos; mod pos;
mod sense;
mod text; mod text;
mod texture; mod texture;
mod ui; mod ui;
@@ -14,13 +15,14 @@ mod widget;
mod widgets; mod widgets;
pub use color::*; pub use color::*;
pub use event::*;
pub use id::*; pub use id::*;
pub use layer::*; pub use layer::*;
pub use module::*;
pub use num::*; pub use num::*;
pub use orientation::*; pub use orientation::*;
pub use painter::*; pub use painter::*;
pub use pos::*; pub use pos::*;
pub use sense::*;
pub use text::*; pub use text::*;
pub use texture::*; pub use texture::*;
pub use ui::*; pub use ui::*;

34
src/layout/module.rs Normal file
View File

@@ -0,0 +1,34 @@
use std::any::{Any, TypeId};
use crate::{
layout::WidgetInstance,
util::{HashMap, Id},
};
#[allow(unused_variables)]
pub trait UiModule: Any {
fn on_draw(&mut self, inst: &WidgetInstance) {}
fn on_undraw(&mut self, inst: &WidgetInstance) {}
fn on_remove(&mut self, id: &Id) {}
}
#[derive(Default)]
pub struct Modules {
map: HashMap<TypeId, Box<dyn UiModule>>,
}
impl Modules {
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (dyn UiModule + 'static)> {
self.map.values_mut().map(|m| m.as_mut())
}
pub fn get_mut<M: UiModule + Default>(&mut self) -> &mut M {
let rf = self
.map
.entry(TypeId::of::<M>())
.or_insert_with(|| Box::new(M::default()))
.as_mut();
let any: &mut dyn Any = &mut *rf;
any.downcast_mut().unwrap()
}
}

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
layout::{ layout::{
ActiveWidgets, Layers, TextAttrs, TextBuffer, TextData, TextOffset, TextureHandle, ActiveWidgets, Layers, Modules, TextAttrs, TextBuffer, TextData, TextOffset, TextureHandle,
Textures, UiRegion, UiVec2, Vec2, WidgetId, Widgets, Textures, UiRegion, UiVec2, Vec2, WidgetId, Widgets,
}, },
render::{Primitive, PrimitiveHandle}, render::{Primitive, PrimitiveHandle},
@@ -25,6 +25,7 @@ pub struct PainterCtx<'a> {
pub textures: &'a mut Textures, pub textures: &'a mut Textures,
pub text: &'a mut TextData, pub text: &'a mut TextData,
pub screen_size: Vec2, pub screen_size: Vec2,
pub modules: &'a mut Modules,
drawing: HashSet<Id>, drawing: HashSet<Id>,
} }
@@ -44,6 +45,7 @@ impl<'a> PainterCtx<'a> {
widgets: &'a Widgets, widgets: &'a Widgets,
layers: &'a mut Layers, layers: &'a mut Layers,
active: &'a mut ActiveWidgets, active: &'a mut ActiveWidgets,
modules: &'a mut Modules,
textures: &'a mut Textures, textures: &'a mut Textures,
text: &'a mut TextData, text: &'a mut TextData,
screen_size: Vec2, screen_size: Vec2,
@@ -55,6 +57,7 @@ impl<'a> PainterCtx<'a> {
textures, textures,
text, text,
screen_size, screen_size,
modules,
drawing: HashSet::default(), drawing: HashSet::default(),
} }
} }
@@ -165,10 +168,8 @@ impl<'a> PainterCtx<'a> {
} }
} }
if self.widgets.data(id).is_some_and(|w| w.sensor) { for m in self.modules.iter_mut() {
self.layers[layer] m.on_draw(&instance);
.sensors
.insert(id.duplicate(), instance.region);
} }
self.active.insert(id.duplicate(), instance); self.active.insert(id.duplicate(), instance);
} }
@@ -182,7 +183,9 @@ impl<'a> PainterCtx<'a> {
} }
inst.textures.clear(); inst.textures.clear();
self.textures.free(); self.textures.free();
self.layers[inst.layer].sensors.remove(id); for m in self.modules.iter_mut() {
m.on_undraw(inst);
}
} }
inst inst
} }

View File

@@ -1,252 +0,0 @@
use std::ops::{BitOr, Deref, DerefMut};
use crate::{
layout::{Ui, UiRegion, Vec2},
util::{HashMap, Id},
};
pub trait CursorCtx {
fn cursor_state(&self) -> &CursorState;
}
pub enum Button {
Left,
Right,
Middle,
}
pub enum Sense {
PressStart(Button),
Pressing(Button),
PressEnd(Button),
HoverStart,
Hovering,
HoverEnd,
}
pub struct Senses(Vec<Sense>);
impl Sense {
pub fn click() -> Self {
Self::PressStart(Button::Left)
}
pub fn unclick() -> Self {
Self::PressEnd(Button::Left)
}
}
#[derive(Default, Clone)]
pub struct CursorState {
pub pos: Vec2,
pub exists: bool,
pub buttons: CursorButtons,
}
#[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();
}
}
#[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 SenseShape = UiRegion;
pub struct SensorGroup<Ctx> {
pub hover: 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;
}
/// TODO: this takes ui.sensor_map and then puts it back
/// it extends so you can add to it, but you can't actually index sensors with it
/// should something be done about this?
pub fn run_sensors<Ctx: UiCtx>(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) {
let mut layers = std::mem::take(&mut ctx.ui().layers);
let mut map = std::mem::take(&mut ctx.ui().event_map);
// temp solution, should probably cache somewhere
let mut bruh = Vec::new();
for (i, _) in layers.iter_mut() {
bruh.push(i)
}
for i in bruh.into_iter().rev() {
let layer = &layers[i];
let mut ran = false;
for (id, shape) in &layer.sensors {
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);
if group.hover == ActivationState::Off {
continue;
}
for sensor in &mut group.sensors {
if should_run(&sensor.senses, &cursor.buttons, group.hover) {
ran = true;
let sctx = SenseCtx {
cursor: cursor.pos - region.top_left,
size: region.bot_right - region.top_left,
};
(sensor.f)(ctx, sctx);
}
}
}
if ran {
break;
}
}
let ui = ctx.ui();
std::mem::swap(&mut ui.event_map, &mut map);
// TODO: this removes existing sensors (if a new one is added to an id) lol
// the proper code is easy but writing this comment is easier and I'm tired
ui.event_map.extend(map);
ui.layers = layers;
}
pub fn should_run(senses: &Senses, cursor: &CursorButtons, hover: ActivationState) -> bool {
for sense in senses.iter() {
if match sense {
Sense::PressStart(button) => cursor.select(button).is_start(),
Sense::Pressing(button) => cursor.select(button).is_on(),
Sense::PressEnd(button) => cursor.select(button).is_end(),
Sense::HoverStart => hover.is_start(),
Sense::Hovering => hover.is_on(),
Sense::HoverEnd => hover.is_end(),
} {
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,
},
}
}
pub fn end_frame(&mut self) {
match self {
Self::Start => *self = Self::On,
Self::End => *self = Self::Off,
_ => (),
}
}
}
impl<Ctx> Default for SensorGroup<Ctx> {
fn default() -> Self {
Self {
hover: Default::default(),
sensors: Default::default(),
}
}
}
impl Deref for Senses {
type Target = Vec<Sense>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Senses {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<Sense> for Senses {
fn from(val: Sense) -> Self {
Senses(vec![val])
}
}
impl BitOr for Sense {
type Output = Senses;
fn bitor(self, rhs: Self) -> Self::Output {
Senses(vec![self, rhs])
}
}
impl BitOr<Sense> for Senses {
type Output = Self;
fn bitor(mut self, rhs: Sense) -> Self::Output {
self.0.push(rhs);
self
}
}

View File

@@ -3,17 +3,19 @@ use image::DynamicImage;
use crate::{ use crate::{
core::{TextEdit, TextEditCtx}, core::{TextEdit, TextEditCtx},
layout::{ layout::{
Layers, PainterCtx, Sensor, SensorMap, StaticWidgetId, TextData, TextureHandle, Textures, Layers, Modules, PainterCtx, StaticWidgetId, TextData, TextureHandle, Textures, Vec2,
Vec2, Widget, WidgetId, WidgetInstance, WidgetLike, Widgets, Widget, WidgetId, WidgetInstance, WidgetLike, Widgets,
}, },
util::{HashMap, Id}, util::{HashMap, Id},
}; };
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
marker::PhantomData,
ops::{Index, IndexMut}, ops::{Index, IndexMut},
sync::mpsc::{Receiver, Sender, channel}, sync::mpsc::{Receiver, Sender, channel},
}; };
// TODO: remove generic
pub struct Ui<Ctx> { pub struct Ui<Ctx> {
root: Option<WidgetId>, root: Option<WidgetId>,
pub(super) widgets: Widgets, pub(super) widgets: Widgets,
@@ -28,7 +30,8 @@ pub struct Ui<Ctx> {
full_redraw: bool, full_redraw: bool,
pub(crate) active: ActiveWidgets, pub(crate) active: ActiveWidgets,
pub(super) event_map: SensorMap<Ctx>, pub modules: Modules,
_pd: PhantomData<Ctx>,
} }
impl<Ctx> Ui<Ctx> { impl<Ctx> Ui<Ctx> {
@@ -114,6 +117,7 @@ impl<Ctx> Ui<Ctx> {
&self.widgets, &self.widgets,
&mut self.layers, &mut self.layers,
&mut self.active, &mut self.active,
&mut self.modules,
&mut self.textures, &mut self.textures,
&mut self.text, &mut self.text,
self.size, self.size,
@@ -137,6 +141,7 @@ impl<Ctx> Ui<Ctx> {
&self.widgets, &self.widgets,
&mut self.layers, &mut self.layers,
&mut self.active, &mut self.active,
&mut self.modules,
&mut self.textures, &mut self.textures,
&mut self.text, &mut self.text,
self.size, self.size,
@@ -150,7 +155,9 @@ impl<Ctx> Ui<Ctx> {
/// free any resources that don't have references anymore /// free any resources that don't have references anymore
fn free(&mut self) { fn free(&mut self) {
for id in self.recv.try_iter() { for id in self.recv.try_iter() {
self.event_map.remove(&id); for m in self.modules.iter_mut() {
m.on_remove(&id);
}
self.widgets.delete(id); self.widgets.delete(id);
} }
self.textures.free(); self.textures.free();
@@ -168,15 +175,6 @@ impl<Ctx> Ui<Ctx> {
self.active.len() self.active.len()
} }
pub fn add_sensor<W>(&mut self, id: &WidgetId<W>, f: Sensor<Ctx>) {
self.event_map
.entry(id.id.duplicate())
.or_default()
.sensors
.push(f);
self.widgets.data_mut(&id.id).unwrap().sensor = true;
}
pub fn text(&mut self, id: &WidgetId<TextEdit>) -> TextEditCtx<'_> { pub fn text(&mut self, id: &WidgetId<TextEdit>) -> TextEditCtx<'_> {
self.updates.push(id.id.duplicate()); self.updates.push(id.id.duplicate());
TextEditCtx { TextEditCtx {
@@ -238,10 +236,11 @@ impl<Ctx> Default for Ui<Ctx> {
text: TextData::default(), text: TextData::default(),
full_redraw: false, full_redraw: false,
active: Default::default(), active: Default::default(),
event_map: Default::default(),
send, send,
recv, recv,
size: Vec2::ZERO, size: Vec2::ZERO,
modules: Modules::default(),
_pd: PhantomData,
} }
} }
} }

View File

@@ -14,8 +14,6 @@ pub struct WidgetData {
pub label: String, pub label: String,
/// dynamic borrow checking /// dynamic borrow checking
pub borrowed: bool, pub borrowed: bool,
/// whether this widget has any sensors
pub sensor: bool,
} }
impl Widgets { impl Widgets {
@@ -85,7 +83,6 @@ impl Widgets {
widget, widget,
label, label,
borrowed: false, borrowed: false,
sensor: false,
}, },
); );
} }

View File

@@ -1,10 +1,9 @@
#![feature(macro_metavar_expr_concat)] #![feature(macro_metavar_expr_concat)]
#![feature(const_ops)] #![feature(const_ops)]
#![feature(const_trait_impl)] #![feature(const_trait_impl)]
#![feature(const_from)] #![feature(const_convert)]
#![feature(map_try_insert)] #![feature(map_try_insert)]
#![feature(trait_alias)] #![feature(trait_alias)]
#![feature(round_char_boundary)]
#![feature(unboxed_closures)] #![feature(unboxed_closures)]
#![feature(fn_traits)] #![feature(fn_traits)]

View File

@@ -1,4 +1,4 @@
use ui::layout::{CursorState, Vec2}; use ui::{core::CursorState, layout::Vec2};
use winit::event::{MouseButton, WindowEvent}; use winit::event::{MouseButton, WindowEvent};
use crate::testing::Client; use crate::testing::Client;

View File

@@ -1,9 +1,9 @@
/// intentionally does not implement copy or clone /// intentionally does not implement copy or ~~clone~~ HEEELP
/// which should make it harder to misuse; /// which should make it harder to misuse;
/// the idea is to generally try to guarantee all IDs /// the idea is to generally try to guarantee all IDs
/// point to something valid, although duplicate /// point to something valid, although duplicate
/// gets around this if needed /// gets around this if needed
#[derive(Eq, Hash, PartialEq, Debug)] #[derive(Eq, Hash, PartialEq, Debug, Clone)]
pub struct Id(u64); pub struct Id(u64);
#[derive(Default)] #[derive(Default)]