proper widgetid + slot vec instead of map

This commit is contained in:
2025-12-11 16:23:14 -05:00
parent 2dad409300
commit 966b6a2ac2
12 changed files with 606 additions and 152 deletions

View File

@@ -1,7 +1,4 @@
use crate::{ use crate::{Ui, UiModule, WidgetId, WidgetView, util::HashMap};
Ui, UiModule, WidgetView,
util::{HashMap, Id},
};
use std::{hash::Hash, rc::Rc}; use std::{hash::Hash, rc::Rc};
pub trait Event: Sized { pub trait Event: Sized {
@@ -41,23 +38,23 @@ impl<E: DefaultEvent> Event for E {
} }
pub trait EventModule<E: Event, Ctx>: UiModule + Default { pub trait EventModule<E: Event, Ctx>: UiModule + Default {
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, E::Data>); fn register(&mut self, id: WidgetId, event: E, f: impl EventFn<Ctx, E::Data>);
fn run<'a>( fn run<'a>(
&self, &self,
id: &Id, id: WidgetId,
event: E, event: E,
) -> Option<impl Fn(EventCtx<Ctx, E::Data>) + use<'a, Self, E, Ctx>>; ) -> Option<impl Fn(EventCtx<Ctx, E::Data>) + use<'a, Self, E, Ctx>>;
} }
type EventFnMap<Ctx, Data> = HashMap<Id, Vec<Rc<dyn EventFn<Ctx, Data>>>>; type EventFnMap<Ctx, Data> = HashMap<WidgetId, Vec<Rc<dyn EventFn<Ctx, Data>>>>;
pub struct DefaultEventModule<E: Event, Ctx> { pub struct DefaultEventModule<E: Event, Ctx> {
map: HashMap<E, EventFnMap<Ctx, <E as Event>::Data>>, map: HashMap<E, EventFnMap<Ctx, <E as Event>::Data>>,
} }
impl<E: Event + 'static, Ctx: 'static> UiModule for DefaultEventModule<E, Ctx> { impl<E: Event + 'static, Ctx: 'static> UiModule for DefaultEventModule<E, Ctx> {
fn on_remove(&mut self, id: &Id) { fn on_remove(&mut self, id: WidgetId) {
for map in self.map.values_mut() { for map in self.map.values_mut() {
map.remove(id); map.remove(&id);
} }
} }
} }
@@ -66,7 +63,7 @@ pub trait HashableEvent: Event + Hash + Eq + 'static {}
impl<E: Event + Hash + Eq + 'static> HashableEvent for E {} impl<E: Event + Hash + Eq + 'static> HashableEvent for E {}
impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<E, Ctx> { impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<E, Ctx> {
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, <E as Event>::Data>) { fn register(&mut self, id: WidgetId, event: E, f: impl EventFn<Ctx, <E as Event>::Data>) {
self.map self.map
.entry(event) .entry(event)
.or_default() .or_default()
@@ -77,11 +74,11 @@ impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<
fn run<'a>( fn run<'a>(
&self, &self,
id: &Id, id: WidgetId,
event: E, event: E,
) -> Option<impl Fn(EventCtx<Ctx, E::Data>) + use<'a, E, Ctx>> { ) -> Option<impl Fn(EventCtx<Ctx, E::Data>) + use<'a, E, Ctx>> {
if let Some(map) = self.map.get(&event) if let Some(map) = self.map.get(&event)
&& let Some(fs) = map.get(id) && let Some(fs) = map.get(&id)
{ {
let fs = fs.clone(); let fs = fs.clone();
Some(move |ctx: EventCtx<Ctx, <E as Event>::Data>| { Some(move |ctx: EventCtx<Ctx, <E as Event>::Data>| {
@@ -138,7 +135,7 @@ impl Ui {
.data .data
.modules .modules
.get_mut::<E::Module<Ctx>>() .get_mut::<E::Module<Ctx>>()
.run(&id.id(), event) .run(id.id(), event)
{ {
f(EventCtx { f(EventCtx {
ui: self, ui: self,

View File

@@ -1,15 +1,12 @@
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};
use crate::{ use crate::{ActiveData, WidgetId, util::HashMap};
ActiveData,
util::{HashMap, Id},
};
#[allow(unused_variables)] #[allow(unused_variables)]
pub trait UiModule: Any { pub trait UiModule: Any {
fn on_draw(&mut self, inst: &ActiveData) {} fn on_draw(&mut self, inst: &ActiveData) {}
fn on_undraw(&mut self, inst: &ActiveData) {} fn on_undraw(&mut self, inst: &ActiveData) {}
fn on_remove(&mut self, id: &Id) {} fn on_remove(&mut self, id: WidgetId) {}
fn on_move(&mut self, inst: &ActiveData) {} fn on_move(&mut self, inst: &ActiveData) {}
} }

View File

@@ -1,8 +1,8 @@
use crate::{ use crate::{
Axis, Len, Modules, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData, Axis, Len, Modules, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData,
TextureHandle, Textures, UiRegion, UiVec2, WidgetHandle, Widgets, TextureHandle, Textures, UiRegion, UiVec2, WidgetHandle, WidgetId, Widgets,
render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst}, render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst},
util::{HashMap, HashSet, Id, TrackedArena, Vec2}, util::{HashMap, HashSet, TrackedArena, Vec2},
}; };
/// makes your surfaces look pretty /// makes your surfaces look pretty
@@ -12,46 +12,46 @@ pub struct Painter<'a, 'c> {
mask: MaskIdx, mask: MaskIdx,
textures: Vec<TextureHandle>, textures: Vec<TextureHandle>,
primitives: Vec<PrimitiveHandle>, primitives: Vec<PrimitiveHandle>,
children: Vec<Id>, children: Vec<WidgetId>,
children_width: HashMap<Id, (UiVec2, Len)>, children_width: HashMap<WidgetId, (UiVec2, Len)>,
children_height: HashMap<Id, (UiVec2, Len)>, children_height: HashMap<WidgetId, (UiVec2, Len)>,
pub layer: usize, pub layer: usize,
id: Id, id: WidgetId,
} }
/// context for a painter; lets you draw and redraw widgets /// context for a painter; lets you draw and redraw widgets
struct PainterCtx<'a> { struct PainterCtx<'a> {
pub widgets: &'a Widgets, pub widgets: &'a Widgets,
pub active: &'a mut HashMap<Id, ActiveData>, pub active: &'a mut HashMap<WidgetId, ActiveData>,
pub layers: &'a mut PrimitiveLayers, pub layers: &'a mut PrimitiveLayers,
pub textures: &'a mut Textures, pub textures: &'a mut Textures,
pub masks: &'a mut TrackedArena<Mask, u32>, pub masks: &'a mut TrackedArena<Mask, u32>,
pub text: &'a mut TextData, pub text: &'a mut TextData,
pub output_size: Vec2, pub output_size: Vec2,
pub modules: &'a mut Modules, pub modules: &'a mut Modules,
pub cache_width: HashMap<Id, (UiVec2, Len)>, pub cache_width: HashMap<WidgetId, (UiVec2, Len)>,
pub cache_height: HashMap<Id, (UiVec2, Len)>, pub cache_height: HashMap<WidgetId, (UiVec2, Len)>,
pub needs_redraw: &'a mut HashSet<Id>, pub needs_redraw: &'a mut HashSet<WidgetId>,
draw_started: HashSet<Id>, draw_started: HashSet<WidgetId>,
} }
/// stores information for children about the highest level parent that needed their size /// stores information for children about the highest level parent that needed their size
/// so that they can redraw the parent if their size changes /// so that they can redraw the parent if their size changes
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct ResizeRef { pub struct ResizeRef {
x: Option<(Id, (UiVec2, Len))>, x: Option<(WidgetId, (UiVec2, Len))>,
y: Option<(Id, (UiVec2, Len))>, y: Option<(WidgetId, (UiVec2, Len))>,
} }
/// important non rendering data for retained drawing /// important non rendering data for retained drawing
#[derive(Debug)] #[derive(Debug)]
pub struct ActiveData { pub struct ActiveData {
pub id: Id, pub id: WidgetId,
pub region: UiRegion, pub region: UiRegion,
pub parent: Option<Id>, pub parent: Option<WidgetId>,
pub textures: Vec<TextureHandle>, pub textures: Vec<TextureHandle>,
pub primitives: Vec<PrimitiveHandle>, pub primitives: Vec<PrimitiveHandle>,
pub children: Vec<Id>, pub children: Vec<WidgetId>,
pub resize: ResizeRef, pub resize: ResizeRef,
pub mask: MaskIdx, pub mask: MaskIdx,
pub layer: usize, pub layer: usize,
@@ -61,13 +61,13 @@ pub struct ActiveData {
#[derive(Default)] #[derive(Default)]
pub struct PainterData { pub struct PainterData {
pub widgets: Widgets, pub widgets: Widgets,
pub active: HashMap<Id, ActiveData>, pub active: HashMap<WidgetId, ActiveData>,
pub layers: PrimitiveLayers, pub layers: PrimitiveLayers,
pub textures: Textures, pub textures: Textures,
pub text: TextData, pub text: TextData,
pub output_size: Vec2, pub output_size: Vec2,
pub modules: Modules, pub modules: Modules,
pub px_dependent: HashSet<Id>, pub px_dependent: HashSet<WidgetId>,
pub masks: TrackedArena<Mask, u32>, pub masks: TrackedArena<Mask, u32>,
} }
@@ -75,7 +75,7 @@ impl<'a> PainterCtx<'a> {
/// redraws a widget that's currently active (drawn) /// redraws a widget that's currently active (drawn)
/// can be called on something already drawn or removed, /// can be called on something already drawn or removed,
/// will just return if so /// will just return if so
pub fn redraw(&mut self, id: Id) { pub fn redraw(&mut self, id: WidgetId) {
self.needs_redraw.remove(&id); self.needs_redraw.remove(&id);
if self.draw_started.contains(&id) { if self.draw_started.contains(&id) {
return; return;
@@ -168,11 +168,11 @@ impl<'a> PainterCtx<'a> {
fn draw_inner( fn draw_inner(
&mut self, &mut self,
layer: usize, layer: usize,
id: Id, id: WidgetId,
region: UiRegion, region: UiRegion,
parent: Option<Id>, parent: Option<WidgetId>,
mask: MaskIdx, mask: MaskIdx,
old_children: Option<Vec<Id>>, old_children: Option<Vec<WidgetId>>,
) { ) {
// I have no idea if these checks work lol // I have no idea if these checks work lol
// the idea is u can't redraw stuff u already drew, // the idea is u can't redraw stuff u already drew,
@@ -272,7 +272,7 @@ impl<'a> PainterCtx<'a> {
self.active.insert(id, instance); self.active.insert(id, instance);
} }
fn mov(&mut self, id: Id, from: UiRegion, to: UiRegion) { fn mov(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) {
let active = self.active.get_mut(&id).unwrap(); let active = self.active.get_mut(&id).unwrap();
for h in &active.primitives { for h in &active.primitives {
let region = self.layers[h.layer].region_mut(h); let region = self.layers[h.layer].region_mut(h);
@@ -291,7 +291,7 @@ impl<'a> PainterCtx<'a> {
} }
/// NOTE: instance textures are cleared and self.textures freed /// NOTE: instance textures are cleared and self.textures freed
fn remove(&mut self, id: Id) -> Option<ActiveData> { fn remove(&mut self, id: WidgetId) -> Option<ActiveData> {
let mut inst = self.active.remove(&id); let mut inst = self.active.remove(&id);
if let Some(inst) = &mut inst { if let Some(inst) = &mut inst {
for h in &inst.primitives { for h in &inst.primitives {
@@ -309,7 +309,7 @@ impl<'a> PainterCtx<'a> {
inst inst
} }
fn remove_rec(&mut self, id: Id) -> Option<ActiveData> { fn remove_rec(&mut self, id: WidgetId) -> Option<ActiveData> {
let inst = self.remove(id); let inst = self.remove(id);
if let Some(inst) = &inst { if let Some(inst) = &inst {
for c in &inst.children { for c in &inst.children {
@@ -321,7 +321,7 @@ impl<'a> PainterCtx<'a> {
} }
impl PainterData { impl PainterData {
fn ctx<'a>(&'a mut self, needs_redraw: &'a mut HashSet<Id>) -> PainterCtx<'a> { fn ctx<'a>(&'a mut self, needs_redraw: &'a mut HashSet<WidgetId>) -> PainterCtx<'a> {
PainterCtx { PainterCtx {
widgets: &self.widgets, widgets: &self.widgets,
active: &mut self.active, active: &mut self.active,
@@ -338,7 +338,7 @@ impl PainterData {
} }
} }
pub fn draw(&mut self, id: Id) { pub fn draw(&mut self, id: WidgetId) {
let mut need_redraw = HashSet::default(); let mut need_redraw = HashSet::default();
let mut ctx = self.ctx(&mut need_redraw); let mut ctx = self.ctx(&mut need_redraw);
ctx.draw_started.clear(); ctx.draw_started.clear();
@@ -346,7 +346,7 @@ impl PainterData {
ctx.draw_inner(0, id, UiRegion::FULL, None, MaskIdx::NONE, None); ctx.draw_inner(0, id, UiRegion::FULL, None, MaskIdx::NONE, None);
} }
pub fn redraw(&mut self, ids: &mut HashSet<Id>) { pub fn redraw(&mut self, ids: &mut HashSet<WidgetId>) {
let mut ctx = self.ctx(ids); let mut ctx = self.ctx(ids);
while let Some(&id) = ctx.needs_redraw.iter().next() { while let Some(&id) = ctx.needs_redraw.iter().next() {
ctx.redraw(id); ctx.redraw(id);
@@ -475,10 +475,10 @@ impl<'a, 'c> Painter<'a, 'c> {
} }
pub fn label(&self) -> &str { pub fn label(&self) -> &str {
&self.ctx.widgets.data(&self.id).unwrap().label &self.ctx.widgets.data(self.id).unwrap().label
} }
pub fn id(&self) -> &Id { pub fn id(&self) -> &WidgetId {
&self.id &self.id
} }
} }
@@ -486,28 +486,28 @@ impl<'a, 'c> Painter<'a, 'c> {
pub struct SizeCtx<'a> { pub struct SizeCtx<'a> {
pub text: &'a mut TextData, pub text: &'a mut TextData,
pub textures: &'a mut Textures, pub textures: &'a mut Textures,
source: Id, source: WidgetId,
widgets: &'a Widgets, widgets: &'a Widgets,
cache_width: &'a mut HashMap<Id, (UiVec2, Len)>, cache_width: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
cache_height: &'a mut HashMap<Id, (UiVec2, Len)>, cache_height: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
checked_width: &'a mut HashMap<Id, (UiVec2, Len)>, checked_width: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
checked_height: &'a mut HashMap<Id, (UiVec2, Len)>, checked_height: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
/// TODO: should this be pub? rn used for sized /// TODO: should this be pub? rn used for sized
pub outer: UiVec2, pub outer: UiVec2,
output_size: Vec2, output_size: Vec2,
id: Id, id: WidgetId,
} }
impl SizeCtx<'_> { impl SizeCtx<'_> {
pub fn id(&self) -> &Id { pub fn id(&self) -> &WidgetId {
&self.id &self.id
} }
pub fn source(&self) -> &Id { pub fn source(&self) -> &WidgetId {
&self.source &self.source
} }
fn width_inner(&mut self, id: Id) -> Len { fn width_inner(&mut self, id: WidgetId) -> Len {
// first check cache // first check cache
// TODO: is this needed? broken rn bc does not store children during upper size check, // TODO: is this needed? broken rn bc does not store children during upper size check,
// so if something actually using check_* hits cache it fails to add them // so if something actually using check_* hits cache it fails to add them
@@ -532,7 +532,7 @@ impl SizeCtx<'_> {
} }
// TODO: should be refactored to share code w width_inner // TODO: should be refactored to share code w width_inner
fn height_inner(&mut self, id: Id) -> Len { fn height_inner(&mut self, id: WidgetId) -> Len {
// if let Some(&(outer, len)) = self.cache_height.get(&id) // if let Some(&(outer, len)) = self.cache_height.get(&id)
// && outer == self.outer // && outer == self.outer
// { // {
@@ -584,7 +584,7 @@ impl SizeCtx<'_> {
self.text.draw(buffer, attrs, self.textures) self.text.draw(buffer, attrs, self.textures)
} }
pub fn label(&self, id: &Id) -> &String { pub fn label(&self, id: WidgetId) -> &String {
self.widgets.label(id) self.widgets.label(id)
} }
} }

View File

@@ -1,19 +1,18 @@
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use crate::{ use crate::{
Color, UiRegion, Color, UiRegion, WidgetId,
render::{ render::{
ArrBuf, ArrBuf,
data::{MaskIdx, PrimitiveInstance}, data::{MaskIdx, PrimitiveInstance},
}, },
util::Id,
}; };
use bytemuck::Pod; use bytemuck::Pod;
use wgpu::*; use wgpu::*;
pub struct Primitives { pub struct Primitives {
instances: Vec<PrimitiveInstance>, instances: Vec<PrimitiveInstance>,
assoc: Vec<Id>, assoc: Vec<WidgetId>,
data: PrimitiveData, data: PrimitiveData,
free: Vec<usize>, free: Vec<usize>,
pub updated: bool, pub updated: bool,
@@ -99,7 +98,7 @@ macro_rules! primitives {
} }
pub struct PrimitiveInst<P> { pub struct PrimitiveInst<P> {
pub id: Id, pub id: WidgetId,
pub primitive: P, pub primitive: P,
pub region: UiRegion, pub region: UiRegion,
pub mask_idx: MaskIdx, pub mask_idx: MaskIdx,
@@ -175,7 +174,7 @@ impl Primitives {
} }
pub struct PrimitiveChange { pub struct PrimitiveChange {
pub id: Id, pub id: WidgetId,
pub old: usize, pub old: usize,
pub new: usize, pub new: usize,
} }

View File

@@ -1,11 +1,9 @@
use crate::{ use crate::{
ActiveData, Event, EventFn, EventModule, IdLike, PainterData, PixelRegion, TextureHandle, ActiveData, Event, EventFn, EventModule, IdLike, PainterData, PixelRegion, TextureHandle,
Widget, WidgetHandle, WidgetLike, Widget, WidgetHandle, WidgetId, WidgetLike, util::Vec2,
util::{Id, Vec2},
}; };
use image::DynamicImage; use image::DynamicImage;
use std::{ use std::{
any::Any,
ops::{Index, IndexMut}, ops::{Index, IndexMut},
sync::mpsc::{Receiver, Sender, channel}, sync::mpsc::{Receiver, Sender, channel},
}; };
@@ -14,8 +12,8 @@ pub struct Ui {
// TODO: make this at least pub(super) // TODO: make this at least pub(super)
pub data: PainterData, pub data: PainterData,
root: Option<WidgetHandle>, root: Option<WidgetHandle>,
recv: Receiver<Id>, recv: Receiver<WidgetId>,
pub(super) send: Sender<Id>, pub(super) send: Sender<WidgetId>,
full_redraw: bool, full_redraw: bool,
resized: bool, resized: bool,
} }
@@ -27,17 +25,15 @@ impl Ui {
/// useful for debugging /// useful for debugging
pub fn set_label<W: ?Sized>(&mut self, id: &WidgetHandle<W>, label: String) { pub fn set_label<W: ?Sized>(&mut self, id: &WidgetHandle<W>, label: String) {
self.data.widgets.data_mut(&id.id()).unwrap().label = label; self.data.widgets.data_mut(id.id()).unwrap().label = label;
} }
pub fn label<W: ?Sized>(&self, id: &WidgetHandle<W>) -> &String { pub fn label<W: ?Sized>(&self, id: &WidgetHandle<W>) -> &String {
&self.data.widgets.data(&id.id()).unwrap().label &self.data.widgets.data(id.id()).unwrap().label
} }
pub fn add_widget<W: Widget>(&mut self, w: W) -> WidgetHandle<W> { pub fn add_widget<W: Widget>(&mut self, w: W) -> WidgetHandle<W> {
let id = self.new_id(); WidgetHandle::new(self.data.widgets.add(w), self.send.clone())
self.data.widgets.insert(id.id(), w);
id
} }
pub fn set_root<Tag>(&mut self, w: impl WidgetLike<Tag>) { pub fn set_root<Tag>(&mut self, w: impl WidgetLike<Tag>) {
@@ -63,10 +59,6 @@ impl Ui {
self.data.widgets.get_mut(id) self.data.widgets.get_mut(id)
} }
fn new_id<W: Widget>(&mut self) -> WidgetHandle<W> {
WidgetHandle::new(self.data.widgets.reserve(), self.send.clone())
}
pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle { pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle {
self.data.textures.add(image) self.data.textures.add(image)
} }
@@ -125,7 +117,7 @@ impl Ui {
fn free(&mut self) { fn free(&mut self) {
for id in self.recv.try_iter() { for id in self.recv.try_iter() {
for m in self.data.modules.iter_mut() { for m in self.data.modules.iter_mut() {
m.on_remove(&id); m.on_remove(id);
} }
self.data.widgets.delete(id); self.data.widgets.delete(id);
} }
@@ -162,9 +154,9 @@ impl Ui {
} }
pub fn debug(&self, label: &str) -> impl Iterator<Item = &ActiveData> { pub fn debug(&self, label: &str) -> impl Iterator<Item = &ActiveData> {
self.data.active.iter().filter_map(move |(id, inst)| { self.data.active.iter().filter_map(move |(&id, inst)| {
let l = &self.data.widgets.label(id); let l = self.data.widgets.label(id);
if *l == label { Some(inst) } else { None } if l == label { Some(inst) } else { None }
}) })
} }
} }
@@ -189,16 +181,6 @@ where
} }
} }
impl dyn Widget {
pub fn as_any(&self) -> &dyn Any {
self
}
pub fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl Default for Ui { impl Default for Ui {
fn default() -> Self { fn default() -> Self {
let (send, recv) = channel(); let (send, recv) = channel();

View File

@@ -1,6 +1,7 @@
mod arena; mod arena;
mod borrow; mod borrow;
mod change; mod change;
mod slot;
mod id; mod id;
mod math; mod math;
mod refcount; mod refcount;
@@ -9,6 +10,7 @@ mod vec2;
pub use arena::*; pub use arena::*;
pub use borrow::*; pub use borrow::*;
pub use change::*; pub use change::*;
pub use slot::*;
pub use id::*; pub use id::*;
pub use math::*; pub use math::*;
pub use refcount::*; pub use refcount::*;

69
core/src/util/slot.rs Normal file
View File

@@ -0,0 +1,69 @@
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SlotId {
idx: u32,
genr: u32,
}
pub struct SlotVec<T> {
data: Vec<(u32, Option<T>)>,
free: Vec<u32>,
}
impl<T> SlotVec<T> {
pub fn new() -> Self {
Self {
data: Default::default(),
free: Default::default(),
}
}
pub fn add(&mut self, x: T) -> SlotId {
if let Some(idx) = self.free.pop() {
let (genr, data) = &mut self.data[idx as usize];
*data = Some(x);
SlotId { idx, genr: *genr }
} else {
let idx = self.data.len() as u32;
let genr = 0;
self.data.push((genr, Some(x)));
SlotId { idx, genr }
}
}
pub fn free(&mut self, id: SlotId) {
let (genr, data) = &mut self.data[id.idx as usize];
*genr += 1;
*data = None;
self.free.push(id.idx);
}
pub fn get(&self, id: SlotId) -> Option<&T> {
let slot = &self.data[id.idx as usize];
if slot.0 != id.genr {
return None;
}
slot.1.as_ref()
}
pub fn get_mut(&mut self, id: SlotId) -> Option<&mut T> {
let slot = &mut self.data[id.idx as usize];
if slot.0 != id.genr {
return None;
}
slot.1.as_mut()
}
pub fn len(&self) -> usize {
self.data.len() - self.free.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<T> Default for SlotVec<T> {
fn default() -> Self {
Self::new()
}
}

View File

@@ -2,9 +2,11 @@ use std::{marker::Unsize, mem::MaybeUninit, ops::CoerceUnsized, sync::mpsc::Send
use crate::{ use crate::{
Ui, Widget, Ui, Widget,
util::{Id, RefCounter}, util::{RefCounter, SlotId},
}; };
pub type WidgetId = SlotId;
/// An identifier for a widget that can index a UI to get the associated widget. /// An identifier for a widget that can index a UI to get the associated widget.
/// It should always remain valid; it keeps a ref count and removes the widget from the UI if all /// It should always remain valid; it keeps a ref count and removes the widget from the UI if all
/// references are dropped. /// references are dropped.
@@ -14,14 +16,14 @@ use crate::{
/// ///
/// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs? /// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs?
pub struct WidgetHandle<W: ?Sized = dyn Widget> { pub struct WidgetHandle<W: ?Sized = dyn Widget> {
pub(super) id: Id, pub(super) id: WidgetId,
counter: RefCounter, counter: RefCounter,
send: Sender<Id>, send: Sender<WidgetId>,
ty: *const W, ty: *const W,
} }
pub struct WidgetView<W: ?Sized = dyn Widget> { pub struct WidgetView<W: ?Sized = dyn Widget> {
pub(super) id: Id, pub(super) id: WidgetId,
#[allow(unused)] #[allow(unused)]
ty: *const W, ty: *const W,
} }
@@ -35,7 +37,7 @@ impl<W: Widget + ?Sized + Unsize<dyn Widget>> WidgetHandle<W> {
} }
impl<W: ?Sized> WidgetHandle<W> { impl<W: ?Sized> WidgetHandle<W> {
pub(crate) fn new(id: Id, send: Sender<Id>) -> Self { pub(crate) fn new(id: WidgetId, send: Sender<WidgetId>) -> Self {
Self { Self {
id, id,
counter: RefCounter::new(), counter: RefCounter::new(),
@@ -49,7 +51,7 @@ impl<W: ?Sized> WidgetHandle<W> {
unsafe { std::mem::transmute(self) } unsafe { std::mem::transmute(self) }
} }
pub fn id(&self) -> Id { pub fn id(&self) -> WidgetId {
self.id self.id
} }
@@ -69,7 +71,7 @@ impl<W: ?Sized> WidgetHandle<W> {
} }
impl<W: ?Sized> WidgetView<W> { impl<W: ?Sized> WidgetView<W> {
pub fn id(&self) -> Id { pub fn id(&self) -> WidgetId {
self.id self.id
} }
} }
@@ -87,19 +89,19 @@ impl<W: ?Sized, F: FnOnce(&mut Ui) -> WidgetHandle<W>> WidgetIdFn<W> for F {}
pub trait IdLike { pub trait IdLike {
type Widget: Widget + ?Sized + 'static; type Widget: Widget + ?Sized + 'static;
fn id(&self) -> Id; fn id(&self) -> WidgetId;
} }
impl<W: Widget + ?Sized> IdLike for WidgetHandle<W> { impl<W: Widget + ?Sized> IdLike for WidgetHandle<W> {
type Widget = W; type Widget = W;
fn id(&self) -> Id { fn id(&self) -> WidgetId {
self.id self.id
} }
} }
impl<W: Widget + ?Sized> IdLike for WidgetView<W> { impl<W: Widget + ?Sized> IdLike for WidgetView<W> {
type Widget = W; type Widget = W;
fn id(&self) -> Id { fn id(&self) -> WidgetId {
self.id self.id
} }
} }

View File

@@ -27,6 +27,16 @@ impl Widget for () {
} }
} }
impl dyn Widget {
pub fn as_any(&self) -> &dyn Any {
self
}
pub fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
/// A function that returns a widget given a UI. /// A function that returns a widget given a UI.
/// Useful for defining trait functions on widgets that create a parent widget so that the children /// Useful for defining trait functions on widgets that create a parent widget so that the children
/// don't need to be IDs yet /// don't need to be IDs yet

View File

@@ -1,13 +1,12 @@
use crate::{ use crate::{
IdLike, Widget, IdLike, Widget, WidgetId,
util::{DynBorrower, HashMap, HashSet, Id, IdTracker}, util::{DynBorrower, HashSet, SlotVec},
}; };
#[derive(Default)] #[derive(Default)]
pub struct Widgets { pub struct Widgets {
pub updates: HashSet<Id>, pub updates: HashSet<WidgetId>,
ids: IdTracker, vec: SlotVec<WidgetData>,
map: HashMap<Id, WidgetData>,
} }
pub struct WidgetData { pub struct WidgetData {
@@ -22,23 +21,23 @@ impl Widgets {
!self.updates.is_empty() !self.updates.is_empty()
} }
pub fn get_dyn(&self, id: Id) -> Option<&dyn Widget> { pub fn get_dyn(&self, id: WidgetId) -> Option<&dyn Widget> {
Some(self.map.get(&id)?.widget.as_ref()) Some(self.vec.get(id)?.widget.as_ref())
} }
pub fn get_dyn_mut(&mut self, id: Id) -> Option<&mut dyn Widget> { pub fn get_dyn_mut(&mut self, id: WidgetId) -> Option<&mut dyn Widget> {
self.updates.insert(id); self.updates.insert(id);
Some(self.map.get_mut(&id)?.widget.as_mut()) Some(self.vec.get_mut(id)?.widget.as_mut())
} }
/// get_dyn but dynamic borrow checking of widgets /// get_dyn but dynamic borrow checking of widgets
/// lets you do recursive (tree) operations, like the painter does /// lets you do recursive (tree) operations, like the painter does
pub fn get_dyn_dynamic(&self, id: Id) -> WidgetWrapper<'_> { pub fn get_dyn_dynamic(&self, id: WidgetId) -> WidgetWrapper<'_> {
// SAFETY: must guarantee no other mutable references to this widget exist // SAFETY: must guarantee no other mutable references to this widget exist
// done through the borrow variable // done through the borrow variable
#[allow(mutable_transmutes)] #[allow(mutable_transmutes)]
let data = unsafe { let data = unsafe {
std::mem::transmute::<&WidgetData, &mut WidgetData>(self.map.get(&id).unwrap()) std::mem::transmute::<&WidgetData, &mut WidgetData>(self.vec.get(id).unwrap())
}; };
if data.borrowed { if data.borrowed {
panic!("tried to mutably borrow the same widget twice"); panic!("tried to mutably borrow the same widget twice");
@@ -46,60 +45,57 @@ impl Widgets {
WidgetWrapper::new(data.widget.as_mut(), &mut data.borrowed) WidgetWrapper::new(data.widget.as_mut(), &mut data.borrowed)
} }
pub fn get<I: IdLike>(&self, id: &I) -> Option<&I::Widget> where I::Widget: Sized { pub fn get<I: IdLike>(&self, id: &I) -> Option<&I::Widget>
where
I::Widget: Sized,
{
self.get_dyn(id.id())?.as_any().downcast_ref() self.get_dyn(id.id())?.as_any().downcast_ref()
} }
pub fn get_mut<I: IdLike>(&mut self, id: &I) -> Option<&mut I::Widget> where I::Widget: Sized { pub fn get_mut<I: IdLike>(&mut self, id: &I) -> Option<&mut I::Widget>
where
I::Widget: Sized,
{
self.get_dyn_mut(id.id())?.as_any_mut().downcast_mut() self.get_dyn_mut(id.id())?.as_any_mut().downcast_mut()
} }
pub fn insert<W: Widget>(&mut self, id: Id, widget: W) { pub fn add<W: Widget>(&mut self, widget: W) -> WidgetId {
let mut label = std::any::type_name::<W>().to_string(); let mut label = std::any::type_name::<W>().to_string();
if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) { if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) {
label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1; label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1;
} }
self.insert_any(id, Box::new(widget), label); self.insert_any(Box::new(widget), label)
} }
pub fn data(&self, id: &Id) -> Option<&WidgetData> { pub fn data(&self, id: WidgetId) -> Option<&WidgetData> {
self.map.get(id) self.vec.get(id)
} }
pub fn label(&self, id: &Id) -> &String { pub fn label(&self, id: WidgetId) -> &String {
&self.data(id).unwrap().label &self.data(id).unwrap().label
} }
pub fn data_mut(&mut self, id: &Id) -> Option<&mut WidgetData> { pub fn data_mut(&mut self, id: WidgetId) -> Option<&mut WidgetData> {
self.map.get_mut(id) self.vec.get_mut(id)
} }
pub fn insert_any(&mut self, id: Id, widget: Box<dyn Widget>, label: String) { pub fn insert_any(&mut self, widget: Box<dyn Widget>, label: String) -> WidgetId {
self.map.insert( self.vec.add(WidgetData {
id,
WidgetData {
widget, widget,
label, label,
borrowed: false, borrowed: false,
}, })
);
} }
pub fn delete(&mut self, id: Id) { pub fn delete(&mut self, id: WidgetId) {
self.map.remove(&id); self.vec.free(id);
self.ids.free(id); // not sure if there's any point in this
} // self.updates.remove(&id);
pub fn reserve(&mut self) -> Id {
self.ids.next()
} }
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.map.len() self.vec.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
} }
} }

400
fm:w Normal file
View File

@@ -0,0 +1,400 @@
use crate::prelude::*;
use iris_core::util::{HashMap};
use std::{
ops::{BitOr, Deref, DerefMut},
rc::Rc,
};
#[derive(Clone, Copy, PartialEq)]
pub enum Button {
Left,
Right,
Middle,
}
#[derive(Clone, Copy, PartialEq)]
pub enum CursorSense {
PressStart(Button),
Pressing(Button),
PressEnd(Button),
HoverStart,
Hovering,
HoverEnd,
Scroll,
}
pub struct CursorSenses(Vec<CursorSense>);
impl CursorSense {
pub fn click() -> Self {
Self::PressStart(Button::Left)
}
pub fn click_or_drag() -> CursorSenses {
Self::click() | Self::Pressing(Button::Left)
}
pub fn unclick() -> Self {
Self::PressEnd(Button::Left)
}
pub fn is_dragging(&self) -> bool {
matches!(self, CursorSense::Pressing(Button::Left))
}
}
#[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: &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();
}
}
impl CursorState {
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<Ctx, Data> {
pub senses: CursorSenses,
pub f: Rc<dyn EventFn<Ctx, Data>>,
}
pub type SensorMap<Ctx, Data> = HashMap<Id, SensorGroup<Ctx, Data>>;
pub type SenseShape = UiRegion;
pub struct SensorGroup<Ctx, Data> {
pub hover: ActivationState,
pub sensors: Vec<Sensor<Ctx, Data>>,
}
#[derive(Clone)]
pub struct CursorData {
pub cursor: Vec2,
pub size: Vec2,
pub scroll_delta: Vec2,
/// the (first) sense that triggered this event
/// the senses are checked in order
pub sense: CursorSense,
}
pub struct CursorModule<Ctx> {
map: SensorMap<Ctx, CursorData>,
active: HashMap<usize, HashMap<Id, SenseShape>>,
}
impl<Ctx: 'static> UiModule for CursorModule<Ctx> {
fn on_draw(&mut self, inst: &ActiveData) {
if self.map.contains_key(&inst.id) {
self.active
.entry(inst.layer)
.or_default()
.insert(inst.id, inst.region);
}
}
fn on_undraw(&mut self, inst: &ActiveData) {
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);
for layer in self.active.values_mut() {
layer.remove(id);
}
}
fn on_move(&mut self, inst: &ActiveData) {
if let Some(map) = self.active.get_mut(&inst.layer)
&& let Some(region) = map.get_mut(&inst.id)
{
*region = inst.region;
}
}
}
impl<Ctx> CursorModule<Ctx> {
pub fn merge(&mut self, other: Self) {
for (id, group) in other.map {
for sensor in group.sensors {
self.map.entry(id).or_default().sensors.push(sensor);
}
}
}
}
pub trait SensorUi {
fn run_sensors<Ctx: 'static>(&mut self, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2);
}
impl SensorUi for Ui {
fn run_sensors<Ctx: 'static>(
&mut self,
ctx: &mut Ctx,
cursor: &CursorState,
window_size: Vec2,
) {
CursorModule::<Ctx>::run(self, ctx, cursor, window_size);
}
}
impl<Ctx: 'static> CursorModule<Ctx> {
pub fn run(ui: &mut Ui, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) {
let layers = std::mem::take(&mut ui.data.layers);
let mut module = std::mem::take(ui.data.modules.get_mut::<Self>());
for i in layers.indices().rev() {
let Some(list) = module.active.get_mut(&i) else {
continue;
};
let mut sensed = false;
for (id, shape) in list.iter() {
let group = module.map.get_mut(id).unwrap();
let region = shape.to_px(window_size);
let in_shape = cursor.exists && region.contains(cursor.pos);
group.hover.update(in_shape);
if group.hover == ActivationState::Off {
continue;
}
sensed = true;
for sensor in &mut group.sensors {
if let Some(sense) = should_run(&sensor.senses, cursor, group.hover) {
let data = CursorData {
cursor: cursor.pos - region.top_left,
size: region.bot_right - region.top_left,
scroll_delta: cursor.scroll_delta,
sense,
};
(sensor.f)(EventCtx {
ui,
state: ctx,
data,
});
}
}
}
if sensed {
break;
}
}
let ui_mod = ui.data.modules.get_mut::<Self>();
std::mem::swap(ui_mod, &mut module);
ui_mod.merge(module);
ui.data.layers = layers;
}
}
pub fn should_run(
senses: &CursorSenses,
cursor: &CursorState,
hover: ActivationState,
) -> Option<CursorSense> {
for sense in senses.iter() {
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 Event for CursorSenses {
type Module<Ctx: 'static> = CursorModule<Ctx>;
type Data = CursorData;
}
impl Event for CursorSense {
type Module<Ctx: 'static> = CursorModule<Ctx>;
type Data = CursorData;
}
impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: 'static>
EventModule<E, Ctx> for CursorModule<Ctx>
{
fn register(&mut self, id: Id, senses: E, f: impl EventFn<Ctx, <E as Event>::Data>) {
// TODO: does not add to active if currently active
self.map.entry(id).or_default().sensors.push(Sensor {
senses: senses.into(),
f: Rc::new(f),
});
}
fn run<'a>(
&self,
id: &Id,
event: E,
) -> Option<impl Fn(EventCtx<Ctx, <E as Event>::Data>) + use<'a, E, Ctx>> {
let senses = event.into();
if let Some(group) = self.map.get(id) {
let fs: Vec<_> = group
.sensors
.iter()
.filter_map(|sensor| {
if sensor.senses.iter().any(|s| senses.contains(s)) {
Some(sensor.f.clone())
} else {
None
}
})
.collect();
Some(move |ctx: EventCtx<Ctx, CursorData>| {
for f in &fs {
f(EventCtx {
state: ctx.state,
ui: ctx.ui,
data: ctx.data.clone(),
});
}
})
} else {
None
}
}
}
impl<Ctx, Data> Default for SensorGroup<Ctx, Data> {
fn default() -> Self {
Self {
hover: Default::default(),
sensors: Default::default(),
}
}
}
impl Deref for CursorSenses {
type Target = Vec<CursorSense>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for CursorSenses {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<CursorSense> 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<CursorSense> for CursorSenses {
type Output = Self;
fn bitor(mut self, rhs: CursorSense) -> Self::Output {
self.0.push(rhs);
self
}
}
impl<Ctx> Default for CursorModule<Ctx> {
fn default() -> Self {
Self {
map: Default::default(),
active: Default::default(),
}
}
}

View File

@@ -1,5 +1,5 @@
use crate::prelude::*; use crate::prelude::*;
use iris_core::util::{HashMap, Id}; use iris_core::util::HashMap;
use std::{ use std::{
ops::{BitOr, Deref, DerefMut}, ops::{BitOr, Deref, DerefMut},
rc::Rc, rc::Rc,
@@ -97,7 +97,7 @@ pub struct Sensor<Ctx, Data> {
pub f: Rc<dyn EventFn<Ctx, Data>>, pub f: Rc<dyn EventFn<Ctx, Data>>,
} }
pub type SensorMap<Ctx, Data> = HashMap<Id, SensorGroup<Ctx, Data>>; pub type SensorMap<Ctx, Data> = HashMap<WidgetId, SensorGroup<Ctx, Data>>;
pub type SenseShape = UiRegion; pub type SenseShape = UiRegion;
pub struct SensorGroup<Ctx, Data> { pub struct SensorGroup<Ctx, Data> {
pub hover: ActivationState, pub hover: ActivationState,
@@ -116,7 +116,7 @@ pub struct CursorData {
pub struct CursorModule<Ctx> { pub struct CursorModule<Ctx> {
map: SensorMap<Ctx, CursorData>, map: SensorMap<Ctx, CursorData>,
active: HashMap<usize, HashMap<Id, SenseShape>>, active: HashMap<usize, HashMap<WidgetId, SenseShape>>,
} }
impl<Ctx: 'static> UiModule for CursorModule<Ctx> { impl<Ctx: 'static> UiModule for CursorModule<Ctx> {
@@ -135,10 +135,10 @@ impl<Ctx: 'static> UiModule for CursorModule<Ctx> {
} }
} }
fn on_remove(&mut self, id: &Id) { fn on_remove(&mut self, id: WidgetId) {
self.map.remove(id); self.map.remove(&id);
for layer in self.active.values_mut() { for layer in self.active.values_mut() {
layer.remove(id); layer.remove(&id);
} }
} }
@@ -301,7 +301,7 @@ impl Event for CursorSense {
impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: 'static> impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: 'static>
EventModule<E, Ctx> for CursorModule<Ctx> EventModule<E, Ctx> for CursorModule<Ctx>
{ {
fn register(&mut self, id: Id, senses: E, f: impl EventFn<Ctx, <E as Event>::Data>) { fn register(&mut self, id: WidgetId, senses: E, f: impl EventFn<Ctx, <E as Event>::Data>) {
// TODO: does not add to active if currently active // TODO: does not add to active if currently active
self.map.entry(id).or_default().sensors.push(Sensor { self.map.entry(id).or_default().sensors.push(Sensor {
senses: senses.into(), senses: senses.into(),
@@ -311,11 +311,11 @@ impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: '
fn run<'a>( fn run<'a>(
&self, &self,
id: &Id, id: WidgetId,
event: E, event: E,
) -> Option<impl Fn(EventCtx<Ctx, <E as Event>::Data>) + use<'a, E, Ctx>> { ) -> Option<impl Fn(EventCtx<Ctx, <E as Event>::Data>) + use<'a, E, Ctx>> {
let senses = event.into(); let senses = event.into();
if let Some(group) = self.map.get(id) { if let Some(group) = self.map.get(&id) {
let fs: Vec<_> = group let fs: Vec<_> = group
.sensors .sensors
.iter() .iter()