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::{
Ui, UiModule, WidgetView,
util::{HashMap, Id},
};
use crate::{Ui, UiModule, WidgetId, WidgetView, util::HashMap};
use std::{hash::Hash, rc::Rc};
pub trait Event: Sized {
@@ -41,23 +38,23 @@ impl<E: DefaultEvent> Event for E {
}
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>(
&self,
id: &Id,
id: WidgetId,
event: E,
) -> 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> {
map: HashMap<E, EventFnMap<Ctx, <E as Event>::Data>>,
}
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() {
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: 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
.entry(event)
.or_default()
@@ -77,11 +74,11 @@ impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<
fn run<'a>(
&self,
id: &Id,
id: WidgetId,
event: E,
) -> Option<impl Fn(EventCtx<Ctx, E::Data>) + use<'a, E, Ctx>> {
if let Some(map) = self.map.get(&event)
&& let Some(fs) = map.get(id)
&& let Some(fs) = map.get(&id)
{
let fs = fs.clone();
Some(move |ctx: EventCtx<Ctx, <E as Event>::Data>| {
@@ -138,7 +135,7 @@ impl Ui {
.data
.modules
.get_mut::<E::Module<Ctx>>()
.run(&id.id(), event)
.run(id.id(), event)
{
f(EventCtx {
ui: self,

View File

@@ -1,15 +1,12 @@
use std::any::{Any, TypeId};
use crate::{
ActiveData,
util::{HashMap, Id},
};
use crate::{ActiveData, WidgetId, util::HashMap};
#[allow(unused_variables)]
pub trait UiModule: Any {
fn on_draw(&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) {}
}

View File

@@ -1,8 +1,8 @@
use crate::{
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},
util::{HashMap, HashSet, Id, TrackedArena, Vec2},
util::{HashMap, HashSet, TrackedArena, Vec2},
};
/// makes your surfaces look pretty
@@ -12,46 +12,46 @@ pub struct Painter<'a, 'c> {
mask: MaskIdx,
textures: Vec<TextureHandle>,
primitives: Vec<PrimitiveHandle>,
children: Vec<Id>,
children_width: HashMap<Id, (UiVec2, Len)>,
children_height: HashMap<Id, (UiVec2, Len)>,
children: Vec<WidgetId>,
children_width: HashMap<WidgetId, (UiVec2, Len)>,
children_height: HashMap<WidgetId, (UiVec2, Len)>,
pub layer: usize,
id: Id,
id: WidgetId,
}
/// context for a painter; lets you draw and redraw widgets
struct PainterCtx<'a> {
pub widgets: &'a Widgets,
pub active: &'a mut HashMap<Id, ActiveData>,
pub active: &'a mut HashMap<WidgetId, ActiveData>,
pub layers: &'a mut PrimitiveLayers,
pub textures: &'a mut Textures,
pub masks: &'a mut TrackedArena<Mask, u32>,
pub text: &'a mut TextData,
pub output_size: Vec2,
pub modules: &'a mut Modules,
pub cache_width: HashMap<Id, (UiVec2, Len)>,
pub cache_height: HashMap<Id, (UiVec2, Len)>,
pub needs_redraw: &'a mut HashSet<Id>,
draw_started: HashSet<Id>,
pub cache_width: HashMap<WidgetId, (UiVec2, Len)>,
pub cache_height: HashMap<WidgetId, (UiVec2, Len)>,
pub needs_redraw: &'a mut HashSet<WidgetId>,
draw_started: HashSet<WidgetId>,
}
/// stores information for children about the highest level parent that needed their size
/// so that they can redraw the parent if their size changes
#[derive(Clone, Copy, Debug, Default)]
pub struct ResizeRef {
x: Option<(Id, (UiVec2, Len))>,
y: Option<(Id, (UiVec2, Len))>,
x: Option<(WidgetId, (UiVec2, Len))>,
y: Option<(WidgetId, (UiVec2, Len))>,
}
/// important non rendering data for retained drawing
#[derive(Debug)]
pub struct ActiveData {
pub id: Id,
pub id: WidgetId,
pub region: UiRegion,
pub parent: Option<Id>,
pub parent: Option<WidgetId>,
pub textures: Vec<TextureHandle>,
pub primitives: Vec<PrimitiveHandle>,
pub children: Vec<Id>,
pub children: Vec<WidgetId>,
pub resize: ResizeRef,
pub mask: MaskIdx,
pub layer: usize,
@@ -61,13 +61,13 @@ pub struct ActiveData {
#[derive(Default)]
pub struct PainterData {
pub widgets: Widgets,
pub active: HashMap<Id, ActiveData>,
pub active: HashMap<WidgetId, ActiveData>,
pub layers: PrimitiveLayers,
pub textures: Textures,
pub text: TextData,
pub output_size: Vec2,
pub modules: Modules,
pub px_dependent: HashSet<Id>,
pub px_dependent: HashSet<WidgetId>,
pub masks: TrackedArena<Mask, u32>,
}
@@ -75,7 +75,7 @@ impl<'a> PainterCtx<'a> {
/// redraws a widget that's currently active (drawn)
/// can be called on something already drawn or removed,
/// will just return if so
pub fn redraw(&mut self, id: Id) {
pub fn redraw(&mut self, id: WidgetId) {
self.needs_redraw.remove(&id);
if self.draw_started.contains(&id) {
return;
@@ -168,11 +168,11 @@ impl<'a> PainterCtx<'a> {
fn draw_inner(
&mut self,
layer: usize,
id: Id,
id: WidgetId,
region: UiRegion,
parent: Option<Id>,
parent: Option<WidgetId>,
mask: MaskIdx,
old_children: Option<Vec<Id>>,
old_children: Option<Vec<WidgetId>>,
) {
// I have no idea if these checks work lol
// the idea is u can't redraw stuff u already drew,
@@ -272,7 +272,7 @@ impl<'a> PainterCtx<'a> {
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();
for h in &active.primitives {
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
fn remove(&mut self, id: Id) -> Option<ActiveData> {
fn remove(&mut self, id: WidgetId) -> Option<ActiveData> {
let mut inst = self.active.remove(&id);
if let Some(inst) = &mut inst {
for h in &inst.primitives {
@@ -309,7 +309,7 @@ impl<'a> PainterCtx<'a> {
inst
}
fn remove_rec(&mut self, id: Id) -> Option<ActiveData> {
fn remove_rec(&mut self, id: WidgetId) -> Option<ActiveData> {
let inst = self.remove(id);
if let Some(inst) = &inst {
for c in &inst.children {
@@ -321,7 +321,7 @@ impl<'a> PainterCtx<'a> {
}
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 {
widgets: &self.widgets,
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 ctx = self.ctx(&mut need_redraw);
ctx.draw_started.clear();
@@ -346,7 +346,7 @@ impl PainterData {
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);
while let Some(&id) = ctx.needs_redraw.iter().next() {
ctx.redraw(id);
@@ -475,10 +475,10 @@ impl<'a, 'c> Painter<'a, 'c> {
}
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
}
}
@@ -486,28 +486,28 @@ impl<'a, 'c> Painter<'a, 'c> {
pub struct SizeCtx<'a> {
pub text: &'a mut TextData,
pub textures: &'a mut Textures,
source: Id,
source: WidgetId,
widgets: &'a Widgets,
cache_width: &'a mut HashMap<Id, (UiVec2, Len)>,
cache_height: &'a mut HashMap<Id, (UiVec2, Len)>,
checked_width: &'a mut HashMap<Id, (UiVec2, Len)>,
checked_height: &'a mut HashMap<Id, (UiVec2, Len)>,
cache_width: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
cache_height: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
checked_width: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
checked_height: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
/// TODO: should this be pub? rn used for sized
pub outer: UiVec2,
output_size: Vec2,
id: Id,
id: WidgetId,
}
impl SizeCtx<'_> {
pub fn id(&self) -> &Id {
pub fn id(&self) -> &WidgetId {
&self.id
}
pub fn source(&self) -> &Id {
pub fn source(&self) -> &WidgetId {
&self.source
}
fn width_inner(&mut self, id: Id) -> Len {
fn width_inner(&mut self, id: WidgetId) -> Len {
// first check cache
// 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
@@ -532,7 +532,7 @@ impl SizeCtx<'_> {
}
// 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)
// && outer == self.outer
// {
@@ -584,7 +584,7 @@ impl SizeCtx<'_> {
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)
}
}

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
mod arena;
mod borrow;
mod change;
mod slot;
mod id;
mod math;
mod refcount;
@@ -9,6 +10,7 @@ mod vec2;
pub use arena::*;
pub use borrow::*;
pub use change::*;
pub use slot::*;
pub use id::*;
pub use math::*;
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::{
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.
/// It should always remain valid; it keeps a ref count and removes the widget from the UI if all
/// references are dropped.
@@ -14,14 +16,14 @@ use crate::{
///
/// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs?
pub struct WidgetHandle<W: ?Sized = dyn Widget> {
pub(super) id: Id,
pub(super) id: WidgetId,
counter: RefCounter,
send: Sender<Id>,
send: Sender<WidgetId>,
ty: *const W,
}
pub struct WidgetView<W: ?Sized = dyn Widget> {
pub(super) id: Id,
pub(super) id: WidgetId,
#[allow(unused)]
ty: *const W,
}
@@ -35,7 +37,7 @@ impl<W: Widget + ?Sized + Unsize<dyn Widget>> 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 {
id,
counter: RefCounter::new(),
@@ -49,7 +51,7 @@ impl<W: ?Sized> WidgetHandle<W> {
unsafe { std::mem::transmute(self) }
}
pub fn id(&self) -> Id {
pub fn id(&self) -> WidgetId {
self.id
}
@@ -69,7 +71,7 @@ impl<W: ?Sized> WidgetHandle<W> {
}
impl<W: ?Sized> WidgetView<W> {
pub fn id(&self) -> Id {
pub fn id(&self) -> WidgetId {
self.id
}
}
@@ -87,19 +89,19 @@ impl<W: ?Sized, F: FnOnce(&mut Ui) -> WidgetHandle<W>> WidgetIdFn<W> for F {}
pub trait IdLike {
type Widget: Widget + ?Sized + 'static;
fn id(&self) -> Id;
fn id(&self) -> WidgetId;
}
impl<W: Widget + ?Sized> IdLike for WidgetHandle<W> {
type Widget = W;
fn id(&self) -> Id {
fn id(&self) -> WidgetId {
self.id
}
}
impl<W: Widget + ?Sized> IdLike for WidgetView<W> {
type Widget = W;
fn id(&self) -> Id {
fn id(&self) -> WidgetId {
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.
/// Useful for defining trait functions on widgets that create a parent widget so that the children
/// don't need to be IDs yet

View File

@@ -1,13 +1,12 @@
use crate::{
IdLike, Widget,
util::{DynBorrower, HashMap, HashSet, Id, IdTracker},
IdLike, Widget, WidgetId,
util::{DynBorrower, HashSet, SlotVec},
};
#[derive(Default)]
pub struct Widgets {
pub updates: HashSet<Id>,
ids: IdTracker,
map: HashMap<Id, WidgetData>,
pub updates: HashSet<WidgetId>,
vec: SlotVec<WidgetData>,
}
pub struct WidgetData {
@@ -22,23 +21,23 @@ impl Widgets {
!self.updates.is_empty()
}
pub fn get_dyn(&self, id: Id) -> Option<&dyn Widget> {
Some(self.map.get(&id)?.widget.as_ref())
pub fn get_dyn(&self, id: WidgetId) -> Option<&dyn Widget> {
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);
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
/// 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
// done through the borrow variable
#[allow(mutable_transmutes)]
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 {
panic!("tried to mutably borrow the same widget twice");
@@ -46,60 +45,57 @@ impl Widgets {
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()
}
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()
}
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();
if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) {
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> {
self.map.get(id)
pub fn data(&self, id: WidgetId) -> Option<&WidgetData> {
self.vec.get(id)
}
pub fn label(&self, id: &Id) -> &String {
pub fn label(&self, id: WidgetId) -> &String {
&self.data(id).unwrap().label
}
pub fn data_mut(&mut self, id: &Id) -> Option<&mut WidgetData> {
self.map.get_mut(id)
pub fn data_mut(&mut self, id: WidgetId) -> Option<&mut WidgetData> {
self.vec.get_mut(id)
}
pub fn insert_any(&mut self, id: Id, widget: Box<dyn Widget>, label: String) {
self.map.insert(
id,
WidgetData {
widget,
label,
borrowed: false,
},
);
pub fn insert_any(&mut self, widget: Box<dyn Widget>, label: String) -> WidgetId {
self.vec.add(WidgetData {
widget,
label,
borrowed: false,
})
}
pub fn delete(&mut self, id: Id) {
self.map.remove(&id);
self.ids.free(id);
}
pub fn reserve(&mut self) -> Id {
self.ids.next()
pub fn delete(&mut self, id: WidgetId) {
self.vec.free(id);
// not sure if there's any point in this
// self.updates.remove(&id);
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
self.vec.len()
}
}