proper widgetid + slot vec instead of map
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
69
core/src/util/slot.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user