From 6156c66a20de233170064d5dbaa25b5b45968a49 Mon Sep 17 00:00:00 2001 From: shadow cat Date: Wed, 10 Dec 2025 01:42:39 -0500 Subject: [PATCH] initial global widget store --- core/src/layout/attr.rs | 4 +- core/src/layout/event.rs | 70 ++++---- core/src/layout/module.rs | 6 +- core/src/layout/painter.rs | 125 +++++++------- core/src/layout/ui.rs | 27 +-- core/src/layout/view.rs | 8 +- core/src/layout/widget/handle.rs | 278 +++++++++++++++++++++--------- core/src/layout/widget/like.rs | 8 +- core/src/layout/widget/mod.rs | 16 +- core/src/layout/widget/widgets.rs | 124 +++++++++---- core/src/lib.rs | 2 + core/src/render/primitive.rs | 12 +- core/src/util/handle.rs | 39 ++++- src/bin/test/main.rs | 49 ++++-- src/default/attr.rs | 34 ++-- src/default/mod.rs | 3 +- src/event.rs | 8 +- src/widget/mask.rs | 2 +- src/widget/position/align.rs | 2 +- src/widget/position/layer.rs | 2 +- src/widget/position/max_size.rs | 2 +- src/widget/position/offset.rs | 2 +- src/widget/position/pad.rs | 2 +- src/widget/position/scroll.rs | 4 +- src/widget/position/sized.rs | 2 +- src/widget/position/span.rs | 6 +- src/widget/position/stack.rs | 4 +- src/widget/ptr.rs | 2 +- src/widget/sense.rs | 39 ++--- src/widget/text/mod.rs | 4 +- src/widget/trait_fns.rs | 2 +- 31 files changed, 536 insertions(+), 352 deletions(-) diff --git a/core/src/layout/attr.rs b/core/src/layout/attr.rs index 5f9095d..6f1f4ab 100644 --- a/core/src/layout/attr.rs +++ b/core/src/layout/attr.rs @@ -2,7 +2,7 @@ use crate::layout::{Ui, WidgetIdFn, WidgetLike, WidgetRef}; pub trait WidgetAttr { type Input; - fn run(ui: &mut Ui, id: &WidgetRef, input: Self::Input); + fn run(ui: &mut Ui, id: WidgetRef, input: Self::Input); } pub trait Attrable { @@ -13,7 +13,7 @@ impl, Tag> Attrable for WL { fn attr>(self, input: A::Input) -> impl WidgetIdFn { |ui| { let id = self.add(ui); - A::run(ui, &id, input); + A::run(ui, id.weak(), input); id } } diff --git a/core/src/layout/event.rs b/core/src/layout/event.rs index 509e34f..a060e5f 100644 --- a/core/src/layout/event.rs +++ b/core/src/layout/event.rs @@ -1,8 +1,8 @@ -use std::{hash::Hash, rc::Rc}; +use std::hash::Hash; use crate::{ - layout::{IdFnTag, Ui, UiModule, WidgetIdFn, WidgetLike, WidgetRef}, - util::{HashMap, Id}, + layout::{Ui, UiModule, WidgetId, WidgetRef}, + util::HashMap, }; pub trait Event: Sized { @@ -18,26 +18,25 @@ pub struct EventCtx<'a, Ctx, Data> { pub type ECtx<'a, Ctx, Data, W> = EventIdCtx<'a, Ctx, Data, W>; pub struct EventIdCtx<'a, Ctx, Data, W: ?Sized> { - pub widget: &'a WidgetRef, + pub widget: WidgetRef, pub ui: &'a mut Ui, pub state: &'a mut Ctx, pub data: Data, } -pub trait EventFn: Fn(EventCtx) + 'static {} -impl) + 'static, Ctx, Data> EventFn for F {} +pub trait EventFn: FnMut(EventCtx) + 'static {} +impl) + 'static, Ctx, Data> EventFn for F {} -pub trait WidgetEventFn: Fn(EventIdCtx) + 'static {} -impl) + 'static, Ctx, Data, W: ?Sized> WidgetEventFn +pub trait WidgetEventFn: FnMut(EventIdCtx) + 'static {} +impl) + 'static, Ctx, Data, W: ?Sized> WidgetEventFn for F { } - impl Ui { pub fn register_event( &mut self, - id: &WidgetRef, + id: WidgetRef, event: E, f: impl EventFn, ) { @@ -49,17 +48,16 @@ impl Ui { pub fn register_widget_event( &mut self, - id: &WidgetRef, + id: WidgetRef, event: E, - f: impl WidgetEventFn, + mut f: impl WidgetEventFn, ) { - let id_ = id.weak(); self.data .modules .get_mut::>() .register(id.id(), event, move |ctx| { - f(EventIdCtx { - widget: &id_.expect_strong(), + (&mut f)(EventIdCtx { + widget: id, ui: ctx.ui, state: ctx.state, data: ctx.data, @@ -78,21 +76,17 @@ impl Event for E { } pub trait EventModule: UiModule + Default { - fn register(&mut self, id: Id, event: E, f: impl EventFn); - fn run<'a>( - &self, - id: &Id, - event: E, - ) -> Option) + use<'a, Self, E, Ctx>>; + fn register(&mut self, id: WidgetId, event: E, f: impl EventFn); + fn run(&mut self, id: WidgetId, event: E) -> Option)>; } -type EventFnMap = HashMap>>>; +type EventFnMap = HashMap>>>; pub struct DefaultEventModule { map: HashMap::Data>>, } impl UiModule for DefaultEventModule { - fn on_remove(&mut self, id: &Id) { + fn on_remove(&mut self, id: &WidgetId) { for map in self.map.values_mut() { map.remove(id); } @@ -103,26 +97,21 @@ pub trait HashableEvent: Event + Hash + Eq + 'static {} impl HashableEvent for E {} impl EventModule for DefaultEventModule { - fn register(&mut self, id: Id, event: E, f: impl EventFn::Data>) { + fn register(&mut self, id: WidgetId, event: E, f: impl EventFn::Data>) { self.map .entry(event) .or_default() .entry(id) .or_default() - .push(Rc::new(f)); + .push(Box::new(f)); } - fn run<'a>( - &self, - id: &Id, - event: E, - ) -> Option) + use<'a, E, Ctx>> { - if let Some(map) = self.map.get(&event) - && let Some(fs) = map.get(id) + fn run(&mut self, id: WidgetId, event: E) -> Option)> { + if let Some(map) = self.map.get_mut(&event) + && let Some(fs) = map.get_mut(&id) { - let fs = fs.clone(); Some(move |ctx: EventCtx::Data>| { - for f in &fs { + for f in fs { f(EventCtx { ui: ctx.ui, state: ctx.state, @@ -137,12 +126,12 @@ impl EventModule for DefaultEventModule< } impl DefaultEventModule { - pub fn run_all(&self, event: E, ctx: EventCtx) + pub fn run_all(&mut self, event: E, ctx: EventCtx) where E::Data: Clone, { - if let Some(map) = self.map.get(&event) { - for fs in map.values() { + if let Some(map) = self.map.get_mut(&event) { + for fs in map.values_mut() { for f in fs { f(EventCtx { ui: ctx.ui, @@ -167,7 +156,7 @@ impl Ui { pub fn run_event( &mut self, ctx: &mut Ctx, - id: &WidgetRef, + id: WidgetRef, event: E, data: E::Data, ) { @@ -175,10 +164,11 @@ impl Ui { .data .modules .get_mut::>() - .run(&id.id(), event) + .run(id.id(), event) { + eprintln!("FIXME"); f(EventCtx { - ui: self, + ui: &mut Ui::new(), state: ctx, data, }); diff --git a/core/src/layout/module.rs b/core/src/layout/module.rs index 4ea672c..18fbc4b 100644 --- a/core/src/layout/module.rs +++ b/core/src/layout/module.rs @@ -1,15 +1,15 @@ use std::any::{Any, TypeId}; use crate::{ - layout::WidgetInstance, - util::{HashMap, Id}, + layout::{WidgetId, WidgetInstance}, + util::HashMap, }; #[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) {} + fn on_remove(&mut self, id: &WidgetId) {} fn on_move(&mut self, inst: &WidgetInstance) {} } diff --git a/core/src/layout/painter.rs b/core/src/layout/painter.rs index 2bc56c9..2777612 100644 --- a/core/src/layout/painter.rs +++ b/core/src/layout/painter.rs @@ -1,84 +1,85 @@ -use std::{cell::Ref, marker::Unsize, sync::mpsc::Sender}; +use std::{marker::Unsize, sync::mpsc::Sender}; use crate::{ layout::{ Axis, Len, Modules, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData, - TextureHandle, Textures, UiRegion, UiVec2, Vec2, Widget, WidgetRef, WidgetUpdate, Widgets, + TextureHandle, Textures, UiRegion, UiVec2, Vec2, Widget, WidgetHandle, WidgetId, + WidgetUpdate, }, render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst}, - util::{HashMap, HashSet, Id, TrackedArena}, + util::{HashMap, HashSet, RefMap, TrackedArena}, }; /// makes your surfaces look pretty pub struct Painter<'a, 'c> { ctx: &'a mut PainterCtx<'c>, - widget: WidgetRef, - id: Id, + widget: WidgetHandle, + id: WidgetId, region: UiRegion, mask: MaskIdx, textures: Vec, primitives: Vec, - children: Vec, - children_width: HashMap, - children_height: HashMap, + children: Vec, + children_width: HashMap, + children_height: HashMap, pub layer: usize, } /// context for a painter; lets you draw and redraw widgets struct PainterCtx<'a> { - pub widgets: &'a Widgets, - pub active: &'a mut HashMap, + pub active: &'a mut HashMap, pub layers: &'a mut PrimitiveLayers, pub textures: &'a mut Textures, pub masks: &'a mut TrackedArena, pub text: &'a mut TextData, pub output_size: Vec2, pub modules: &'a mut Modules, - pub cache_width: HashMap, - pub cache_height: HashMap, - pub needs_redraw: HashSet, - draw_started: HashSet, + send: &'a Sender, + pub cache_width: HashMap, + pub cache_height: HashMap, + pub needs_redraw: HashSet, + draw_started: HashSet, } /// 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 WidgetInstance { - pub id: Id, + pub id: WidgetId, pub region: UiRegion, - pub parent: Option, + pub parent: Option, pub textures: Vec, pub primitives: Vec, - pub children: Vec, + pub children: Vec, pub resize: ResizeRef, pub mask: MaskIdx, pub layer: usize, } /// data to be stored in Ui to create PainterCtxs easily +/// TODO: actually use this LMAO pub struct PainterData { - pub widgets: Widgets, - pub active: HashMap, + pub active: HashMap, pub layers: PrimitiveLayers, pub textures: Textures, pub text: TextData, pub output_size: Vec2, pub modules: Modules, - pub px_dependent: HashSet, + pub px_dependent: HashSet, pub masks: TrackedArena, + send: Sender, } impl PainterData { pub fn new(send: Sender) -> Self { Self { - widgets: Widgets::new(send), active: Default::default(), layers: Default::default(), textures: Default::default(), @@ -87,6 +88,7 @@ impl PainterData { modules: Default::default(), px_dependent: Default::default(), masks: Default::default(), + send, } } } @@ -95,7 +97,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, widget: &WidgetRef) { + pub fn redraw>(&mut self, widget: &WidgetHandle) { let id = widget.id(); self.needs_redraw.remove(&id); if self.draw_started.contains(&id) { @@ -131,9 +133,11 @@ impl<'a> PainterCtx<'a> { id, } .width(widget); - if new_desired != *old_desired { + if new_desired != *old_desired + && let Some(w) = rid.strong() + { // unsure if I need to walk down the tree here - self.redraw(&self.widgets.get(*rid)); + self.redraw(&w); *old_desired = new_desired; if self.draw_started.contains(&id) { ret = true; @@ -156,8 +160,10 @@ impl<'a> PainterCtx<'a> { id, } .height(widget); - if new_desired != *old_desired { - self.redraw(&self.widgets.get(*rid)); + if new_desired != *old_desired + && let Some(w) = rid.strong() + { + self.redraw(&w); *old_desired = new_desired; if self.draw_started.contains(&id) { ret = true; @@ -187,13 +193,14 @@ impl<'a> PainterCtx<'a> { fn draw_inner>( &mut self, layer: usize, - widget: &WidgetRef, + widget: &WidgetHandle, region: UiRegion, - parent: Option, + parent: Option, mask: MaskIdx, - old_children: Option>, + old_children: Option>, ) { let id = widget.id(); + widget.data_mut().send = Some(self.send.clone()); // I have no idea if these checks work lol // the idea is u can't redraw stuff u already drew, // and if parent is different then there's another copy with a different parent @@ -293,7 +300,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); @@ -312,7 +319,7 @@ impl<'a> PainterCtx<'a> { } /// NOTE: instance textures are cleared and self.textures freed - fn remove(&mut self, id: Id) -> Option { + fn remove(&mut self, id: WidgetId) -> Option { let mut inst = self.active.remove(&id); if let Some(inst) = &mut inst { for h in &inst.primitives { @@ -330,7 +337,7 @@ impl<'a> PainterCtx<'a> { inst } - fn remove_rec(&mut self, id: Id) -> Option { + fn remove_rec(&mut self, id: WidgetId) -> Option { let inst = self.remove(id); if let Some(inst) = &inst { for c in &inst.children { @@ -342,9 +349,8 @@ impl<'a> PainterCtx<'a> { } impl PainterData { - fn ctx(&mut self, needs_redraw: HashSet) -> PainterCtx<'_> { + fn ctx(&mut self, needs_redraw: HashSet) -> PainterCtx<'_> { PainterCtx { - widgets: &self.widgets, active: &mut self.active, layers: &mut self.layers, textures: &mut self.textures, @@ -352,6 +358,7 @@ impl PainterData { output_size: self.output_size, modules: &mut self.modules, masks: &mut self.masks, + send: &self.send, cache_width: Default::default(), cache_height: Default::default(), draw_started: Default::default(), @@ -359,17 +366,19 @@ impl PainterData { } } - pub fn draw>(&mut self, id: &WidgetRef) { + pub fn draw>(&mut self, id: &WidgetHandle) { let mut ctx = self.ctx(Default::default()); ctx.draw_started.clear(); ctx.layers.clear(); ctx.draw_inner(0, id, UiRegion::FULL, None, MaskIdx::NONE, None); } - pub fn redraw(&mut self, ids: HashSet) { + pub fn redraw(&mut self, ids: HashSet) { let mut ctx = self.ctx(ids); while let Some(&id) = ctx.needs_redraw.iter().next() { - ctx.redraw(&ctx.widgets.get(id)); + if let Some(w) = id.strong() { + ctx.redraw(&w); + } } } } @@ -407,7 +416,7 @@ impl<'a, 'c> Painter<'a, 'c> { } /// Draws a widget within this widget's region. - pub fn widget>(&mut self, id: &WidgetRef) { + pub fn widget>(&mut self, id: &WidgetHandle) { self.widget_at(id, self.region); } @@ -415,7 +424,7 @@ impl<'a, 'c> Painter<'a, 'c> { /// Useful for drawing child widgets in select areas. pub fn widget_within>( &mut self, - id: &WidgetRef, + id: &WidgetHandle, region: UiRegion, ) { self.widget_at(id, region.within(&self.region)); @@ -423,7 +432,7 @@ impl<'a, 'c> Painter<'a, 'c> { fn widget_at>( &mut self, - id: &WidgetRef, + id: &WidgetHandle, region: UiRegion, ) { self.children.push(id.id()); @@ -458,11 +467,11 @@ impl<'a, 'c> Painter<'a, 'c> { self.region } - pub fn size(&mut self, id: &WidgetRef) -> Size { + pub fn size(&mut self, id: &WidgetHandle) -> Size { self.size_ctx().size(id) } - pub fn len_axis(&mut self, id: &WidgetRef, axis: Axis) -> Len { + pub fn len_axis(&mut self, id: &WidgetHandle, axis: Axis) -> Len { match axis { Axis::X => self.size_ctx().width(id), Axis::Y => self.size_ctx().height(id), @@ -504,11 +513,11 @@ impl<'a, 'c> Painter<'a, 'c> { self.layer = self.ctx.layers.next(self.layer); } - pub fn label(&self) -> Ref<'_, String> { + pub fn label(&self) -> RefMap<'_, String> { self.widget.get_label() } - pub fn id(&self) -> Id { + pub fn id(&self) -> WidgetId { self.id } } @@ -516,27 +525,27 @@ impl<'a, 'c> Painter<'a, 'c> { pub struct SizeCtx<'a> { pub text: &'a mut TextData, pub textures: &'a mut Textures, - source: Id, - cache_width: &'a mut HashMap, - cache_height: &'a mut HashMap, - checked_width: &'a mut HashMap, - checked_height: &'a mut HashMap, + source: WidgetId, + cache_width: &'a mut HashMap, + cache_height: &'a mut HashMap, + checked_width: &'a mut HashMap, + checked_height: &'a mut HashMap, /// 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 } - pub fn width(&mut self, widget: &WidgetRef) -> Len { + pub fn width(&mut self, widget: &WidgetHandle) -> 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 @@ -562,7 +571,7 @@ impl SizeCtx<'_> { } // TODO: should be refactored to share code w width_inner - pub fn height(&mut self, widget: &WidgetRef) -> Len { + pub fn height(&mut self, widget: &WidgetHandle) -> Len { // if let Some(&(outer, len)) = self.cache_height.get(&id) // && outer == self.outer // { @@ -581,14 +590,14 @@ impl SizeCtx<'_> { len } - pub fn len_axis(&mut self, id: &WidgetRef, axis: Axis) -> Len { + pub fn len_axis(&mut self, id: &WidgetHandle, axis: Axis) -> Len { match axis { Axis::X => self.width(id), Axis::Y => self.height(id), } } - pub fn size(&mut self, id: &WidgetRef) -> Size { + pub fn size(&mut self, id: &WidgetHandle) -> Size { Size { x: self.width(id), y: self.height(id), diff --git a/core/src/layout/ui.rs b/core/src/layout/ui.rs index e2c3219..67d89bc 100644 --- a/core/src/layout/ui.rs +++ b/core/src/layout/ui.rs @@ -2,32 +2,24 @@ use image::DynamicImage; use crate::{ layout::{ - IdLike, PainterData, PixelRegion, TextureHandle, Vec2, Widget, WidgetInstance, WidgetLike, - WidgetRef, WidgetUpdate, + IdLike, PainterData, PixelRegion, TextureHandle, Vec2, Widget, WidgetHandle, WidgetId, + WidgetInstance, WidgetLike, WidgetUpdate, }, - util::{HashSet, Id}, + util::HashSet, }; use std::sync::mpsc::{Receiver, channel}; pub struct Ui { pub(crate) data: PainterData, - root: Option, - updates: HashSet, - free: Vec, + root: Option, + updates: HashSet, + free: Vec, recv: Receiver, full_redraw: bool, resized: bool, } impl Ui { - pub fn add(&mut self, w: impl WidgetLike) -> WidgetRef { - w.add(self) - } - - pub fn add_widget(&mut self, w: W) -> WidgetRef { - self.data.widgets.insert(w) - } - pub fn set_root(&mut self, w: impl WidgetLike) { self.root = Some(w.add(self)); self.full_redraw = true; @@ -97,7 +89,6 @@ impl Ui { for m in self.data.modules.iter_mut() { m.on_remove(&id); } - self.data.widgets.delete(id); } self.data.textures.free(); } @@ -106,10 +97,6 @@ impl Ui { self.full_redraw || !self.updates.is_empty() } - pub fn num_widgets(&self) -> usize { - self.data.widgets.len() - } - pub fn active_widgets(&self) -> usize { self.data.active.len() } @@ -133,7 +120,7 @@ impl Ui { pub fn debug(&self, label: &str) -> impl Iterator { self.data.active.iter().filter_map(move |(id, inst)| { - let widget = &self.data.widgets.get(*id); + let widget = id.strong().unwrap(); if widget.get_label().as_str() == label { Some(inst) } else { diff --git a/core/src/layout/view.rs b/core/src/layout/view.rs index 74c706a..4e09052 100644 --- a/core/src/layout/view.rs +++ b/core/src/layout/view.rs @@ -1,9 +1,9 @@ -use crate::layout::{Widget, WidgetLike, WidgetRef}; +use crate::layout::{Widget, WidgetHandle, WidgetLike}; use std::marker::Unsize; pub trait WidgetView { type Widget: Widget + ?Sized + Unsize = dyn Widget; - fn view(&self) -> &WidgetRef; + fn view(self) -> WidgetHandle; } pub struct ViewTag; @@ -11,7 +11,7 @@ pub struct ViewTag; impl WidgetLike for WV { type Widget = WV::Widget; - fn add(self, _ui: &mut super::Ui) -> WidgetRef { - self.view().clone() + fn add(self, _ui: &mut super::Ui) -> WidgetHandle { + self.view() } } diff --git a/core/src/layout/widget/handle.rs b/core/src/layout/widget/handle.rs index 067669e..21ed679 100644 --- a/core/src/layout/widget/handle.rs +++ b/core/src/layout/widget/handle.rs @@ -1,62 +1,53 @@ use std::{ - cell::{Ref, RefMut}, marker::Unsize, - ops::CoerceUnsized, + mem::MaybeUninit, + ops::{CoerceUnsized, Deref, DerefMut}, sync::mpsc::Sender, }; use crate::{ - layout::{IdFnTag, IdTag, Ui, Widget, WidgetLike}, - util::{Handle, Id, WeakHandle}, + layout::{IdFnTag, IdTag, Ui, WIDGETS, Widget, WidgetLike}, + util::{Handle, Ref, RefMap, RefMapMut, RefMut, WeakHandle}, }; -/// An handle for a widget in a UI. -/// -/// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs? -pub struct WidgetRef(Handle>); -pub struct WeakWidgetRef(WeakHandle>); +pub(super) type SlotTy = u32; -struct Inner { - id: Id, - send: Sender, - label: String, - widget: W, +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct WidgetId { + pub(super) i: SlotTy, + pub(super) genr: SlotTy, +} + +/// A strong handle to a widget. Cannot be cloned, only one may exist +/// Use .weak() to obtain weak references to this +pub struct WidgetHandle(WidgetId, Handle>); +/// A weak handle to a widget. Implements Copy so you can easily pass into closures +pub struct WidgetRef(WidgetId, *const W); + +pub(crate) struct WidgetData { + pub id: WidgetId, + pub send: Option>, + pub label: String, + pub widget: W, } pub enum WidgetUpdate { - Drop(Id), - Mutate(Id), + Drop(WidgetId), + Mutate(WidgetId), } -impl PartialEq for WidgetRef { - fn eq(&self, other: &Self) -> bool { - self.id() == other.id() - } -} - -impl std::fmt::Debug for WidgetRef { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.id().fmt(f) - } -} - -impl Clone for WidgetRef { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl WidgetRef { - pub(super) fn new(id: Id, widget: W, send: Sender) -> Self { +impl WidgetHandle { + pub(super) fn new(id: WidgetId, widget: W) -> Self { let mut label = std::any::type_name::().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( - Inner { - widget, + id, + WidgetData { id, - send, + widget, + send: None, label, } .into(), @@ -64,100 +55,225 @@ impl WidgetRef { } } -impl> WidgetRef { - pub fn any(self) -> WidgetRef { - WidgetRef(self.0) +impl> WidgetHandle { + pub(super) fn weak_inner(&self) -> WeakHandle> { + self.1.weak() } - pub fn as_any(&self) -> WidgetRef { - WidgetRef(self.0.clone()) + + pub fn any(self) -> WidgetHandle { + self + } + + /// DO NOT CALL OTHER THAN TEMP IN PAINTER + pub(crate) fn as_any(&self) -> WidgetHandle { + WidgetHandle(self.0, self.1.clone()) } } -impl WidgetRef { - pub fn id(&self) -> Id { - self.0.get().id +impl WidgetHandle { + pub fn id(&self) -> WidgetId { + self.0 } - pub fn get(&self) -> Ref<'_, W> { - Ref::map(self.0.get(), |i| &i.widget) + pub fn get(&self) -> RefMap<'_, W> { + Ref::map(self.1.get(), |i| &mut i.widget) } - pub fn get_mut(&self) -> RefMut<'_, W> { - let inner = self.0.get_mut(); - let _ = inner.send.send(WidgetUpdate::Mutate(inner.id)); + pub fn get_mut(&self) -> RefMapMut<'_, W> { + let inner = self.1.get_mut(); + if let Some(send) = &inner.send { + let _ = send.send(WidgetUpdate::Mutate(self.0)); + } RefMut::map(inner, |i| &mut i.widget) } - pub fn get_mut_quiet(&self) -> RefMut<'_, W> { - RefMut::map(self.0.get_mut(), |i| &mut i.widget) + pub(crate) fn data_mut(&self) -> RefMut<'_, WidgetData> { + self.1.get_mut() } - pub fn get_label(&self) -> Ref<'_, String> { - Ref::map(self.0.get(), |i| &i.label) + pub fn get_mut_quiet(&self) -> RefMapMut<'_, W> { + RefMut::map(self.1.get_mut(), |i| &mut i.widget) + } + + pub fn get_label(&self) -> RefMap<'_, String> { + Ref::map(self.1.get(), |i| &mut i.label) } pub fn set_label(&self, label: impl Into) { - self.0.get_mut().label = label.into(); + self.1.get_mut().label = label.into(); } + /// TODO: THIS SHOULD ALWAYS BE 1, remove this probably (weak count might be nice though) pub fn refs(&self) -> usize { - self.0.refs() + self.1.refs() } - pub fn weak(&self) -> WeakWidgetRef { - WeakWidgetRef(self.0.weak()) + pub fn weak(&self) -> WidgetRef { + WidgetRef(self.0, null_ptr()) } } -impl WeakWidgetRef { - /// should guarantee that widget is still valid to prevent indexing failures - pub(crate) fn expect_strong(&self) -> WidgetRef { - WidgetRef(self.0.strong().expect("widget should not be dropped")) +impl WidgetRef { + fn handle(&self) -> Handle> { + WIDGETS.get_type(self.0).unwrap() + } + + pub fn get<'a>(&self) -> WRef<'a, W> { + let handle = self.handle(); + WRef { + val: unsafe { + std::mem::transmute::, RefMap<'_, W>>(Ref::map(handle.get(), |i| { + &mut i.widget + })) + }, + _handle: handle, + } + } + + pub fn get_mut<'a>(&self) -> WRefMut<'a, W> { + let handle = self.handle(); + if let Some(send) = &handle.get().send { + let _ = send.send(WidgetUpdate::Mutate(self.0)); + } + WRefMut { + val: unsafe { + std::mem::transmute::, RefMapMut<'_, W>>(RefMut::map( + handle.get_mut(), + |i| &mut i.widget, + )) + }, + _handle: handle, + } } } -impl> WeakWidgetRef { - pub fn any(self) -> WeakWidgetRef { - WeakWidgetRef(self.0.clone()) +pub struct WRef<'a, W> { + _handle: Handle>, + val: RefMap<'a, W>, +} + +pub struct WRefMut<'a, W> { + _handle: Handle>, + val: RefMapMut<'a, W>, +} + +impl> WidgetRef { + pub fn any(self) -> WidgetRef { + WidgetRef(self.0, null_ptr()) } } -impl Drop for Inner { +fn null_ptr() -> *const T { + unsafe { MaybeUninit::zeroed().assume_init() } +} + +impl WidgetRef { + pub fn id(&self) -> WidgetId { + self.0 + } +} + +impl WidgetId { + /// THIS SHOULD ONLY BE CALLED FOR TEMP STUFF DURING PAINTING + pub(crate) fn strong(self) -> Option { + Some(WidgetHandle(self, WIDGETS.get(self)?)) + } +} + +impl Drop for WidgetData { fn drop(&mut self) { - let _ = self.send.send(WidgetUpdate::Drop(self.id)); + if let Some(send) = &self.send { + WIDGETS.remove(self.id); + let _ = send.send(WidgetUpdate::Drop(self.id)); + } } } -pub trait WidgetIdFn: FnOnce(&mut Ui) -> WidgetRef {} -impl WidgetRef> WidgetIdFn for F {} +pub trait WidgetIdFn: FnOnce(&mut Ui) -> WidgetHandle {} +impl WidgetHandle> WidgetIdFn for F {} -pub trait WidgetRet: FnOnce(&mut Ui) -> WidgetRef {} -impl WidgetRef> WidgetRet for F {} +pub trait WidgetRet: FnOnce(&mut Ui) -> WidgetHandle {} +impl WidgetHandle> WidgetRet for F {} -impl + 'static> WidgetLike for WidgetRef { +impl + 'static> WidgetLike for WidgetHandle { type Widget = W; - fn add(self, _: &mut Ui) -> WidgetRef { + fn add(self, _: &mut Ui) -> WidgetHandle { self } } -impl + 'static, F: FnOnce(&mut Ui) -> WidgetRef> +impl + 'static, F: FnOnce(&mut Ui) -> WidgetHandle> WidgetLike for F { type Widget = W; - fn add(self, ui: &mut Ui) -> WidgetRef { + fn add(self, ui: &mut Ui) -> WidgetHandle { self(ui) } } pub trait IdLike { - fn id(&self) -> Id; + fn id(&self) -> WidgetId; } -impl IdLike for WidgetRef { - fn id(&self) -> Id { +impl IdLike for WidgetHandle { + fn id(&self) -> WidgetId { self.id() } } -impl, U: ?Sized> CoerceUnsized> for WidgetRef {} +impl IdLike for WidgetRef { + fn id(&self) -> WidgetId { + self.id() + } +} + +impl, U: ?Sized> CoerceUnsized> for WidgetHandle {} +impl, U: ?Sized> CoerceUnsized> for WidgetRef {} + +impl PartialEq for WidgetHandle { + fn eq(&self, other: &Self) -> bool { + self.id() == other.id() + } +} + +impl std::fmt::Debug for WidgetHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.id().fmt(f) + } +} + +impl Clone for WidgetRef { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for WidgetRef {} + +impl PartialEq for WidgetRef { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} + +impl Deref for WRef<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.val + } +} + +impl Deref for WRefMut<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.val + } +} + +impl DerefMut for WRefMut<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.val + } +} diff --git a/core/src/layout/widget/like.rs b/core/src/layout/widget/like.rs index 076ae9e..5c05a33 100644 --- a/core/src/layout/widget/like.rs +++ b/core/src/layout/widget/like.rs @@ -4,11 +4,11 @@ use std::marker::Unsize; pub trait WidgetLike { type Widget: Widget + ?Sized + Unsize + 'static; - fn add(self, ui: &mut Ui) -> WidgetRef; + fn add(self, ui: &mut Ui) -> WidgetHandle; fn with_id( self, - f: impl FnOnce(&mut Ui, WidgetRef) -> WidgetRef, + f: impl FnOnce(&mut Ui, WidgetHandle) -> WidgetHandle, ) -> impl WidgetIdFn where Self: Sized, @@ -28,11 +28,11 @@ pub trait WidgetLike { } pub struct WidgetArr { - pub arr: [WidgetRef; LEN], + pub arr: [WidgetHandle; LEN], } impl WidgetArr { - pub fn new(arr: [WidgetRef; LEN]) -> Self { + pub fn new(arr: [WidgetHandle; LEN]) -> Self { Self { arr } } } diff --git a/core/src/layout/widget/mod.rs b/core/src/layout/widget/mod.rs index 20b2239..6b959ac 100644 --- a/core/src/layout/widget/mod.rs +++ b/core/src/layout/widget/mod.rs @@ -10,7 +10,7 @@ pub use widgets::*; use crate::layout::{Len, Painter, SizeCtx, Ui}; -pub trait Widget: 'static { +pub trait Widget: 'static + Send + Sync { fn draw(&mut self, painter: &mut Painter); fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len; fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len; @@ -34,30 +34,30 @@ impl W> WidgetFn for F {} impl W> WidgetLike for F { type Widget = W; - fn add(self, ui: &mut Ui) -> WidgetRef { + fn add(self, ui: &mut Ui) -> WidgetHandle { self(ui).add(ui) } } impl WidgetLike for W { type Widget = W; - fn add(self, ui: &mut Ui) -> WidgetRef { - ui.add_widget(self) + fn add(self, _: &mut Ui) -> WidgetHandle { + WIDGETS.insert(self) } } pub trait WidgetOption { - fn get(self, ui: &mut Ui) -> Option; + fn get(self, ui: &mut Ui) -> Option; } impl WidgetOption for () { - fn get(self, _: &mut Ui) -> Option { + fn get(self, _: &mut Ui) -> Option { None } } -impl Option> WidgetOption for F { - fn get(self, ui: &mut Ui) -> Option { +impl Option> WidgetOption for F { + fn get(self, ui: &mut Ui) -> Option { self(ui) } } diff --git a/core/src/layout/widget/widgets.rs b/core/src/layout/widget/widgets.rs index feec35f..aa1d11d 100644 --- a/core/src/layout/widget/widgets.rs +++ b/core/src/layout/widget/widgets.rs @@ -1,50 +1,108 @@ -use std::sync::mpsc::Sender; - -use crate::{ - layout::{WeakWidgetRef, Widget, WidgetRef, WidgetUpdate}, - util::{HashMap, Id, IdTracker}, +use std::{ + any::Any, + sync::{Mutex, MutexGuard}, }; -pub struct Widgets { - ids: IdTracker, - map: HashMap, - send: Sender, +use crate::{ + layout::{ + Widget, WidgetData, WidgetHandle, + widget::{SlotTy, WidgetId}, + }, + util::{Handle, WeakHandle}, +}; + +pub(crate) static WIDGETS: Widgets = Widgets::new(); +pub(crate) struct Widgets(Mutex); + +pub fn total_widgets() -> usize { + WIDGETS.len() +} + +pub struct WidgetSlot { + genr: SlotTy, + data: WeakHandle>, +} + +struct Inner { + cur_id: SlotTy, + vec: Vec, + free: Vec, } impl Widgets { - pub fn new(send: Sender) -> Self { - Self { - ids: IdTracker::default(), - map: HashMap::default(), - send, + pub const fn new() -> Self { + Self(Mutex::new(Inner { + cur_id: 0, + vec: Vec::new(), + free: Vec::new(), + })) + } + + fn expect(&self) -> MutexGuard<'_, Inner> { + self.0.lock().unwrap() + } + + pub fn get_type(&self, id: WidgetId) -> Option>> { + let slot = &self.expect().vec[id.i as usize]; + if slot.genr != id.genr { + None + } else { + Some( + unsafe { slot.data.clone().downcast() } + .unwrap() + .clone() + .strong()?, + ) } } - pub fn get(&self, id: Id) -> WidgetRef { - self.map.get(&id).unwrap().expect_strong() + pub fn get(&self, id: WidgetId) -> Option>> { + let slot = &self.expect().vec[id.i as usize]; + if slot.genr != id.genr { + None + } else { + Some(slot.data.clone().strong()?) + } } - pub fn insert(&mut self, widget: W) -> WidgetRef { - let id = self.ids.next(); - let rf = WidgetRef::new(id, widget, self.send.clone()); - self.map.insert(id, rf.weak().any()); - rf + pub fn insert(&self, widget: W) -> WidgetHandle { + let mut s = self.expect(); + let id = s + .free + .pop() + .map(|i| { + assert!(s.vec[i as usize].data.dropped()); + WidgetId { + i, + genr: s.vec[i as usize].genr, + } + }) + .unwrap_or_else(|| { + let i = s.cur_id; + s.cur_id += 1; + WidgetId { i, genr: 0 } + }); + let handle = WidgetHandle::new(id, widget); + let slot = WidgetSlot { + genr: id.genr, + data: handle.weak_inner(), + }; + if id.i == s.vec.len() as SlotTy { + s.vec.push(slot); + } else { + s.vec[id.i as usize] = slot; + } + handle } - 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 remove(&self, id: WidgetId) { + let mut s = self.expect(); + s.vec[id.i as usize].genr += 1; + s.free.push(id.i); } pub fn len(&self) -> usize { - self.map.len() - } - - pub fn is_empty(&self) -> bool { - self.map.is_empty() + let s = self.expect(); + s.vec.len() - s.free.len() } } diff --git a/core/src/lib.rs b/core/src/lib.rs index f2ad881..65684f1 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -11,6 +11,8 @@ #![feature(associated_type_defaults)] #![feature(unsize)] #![feature(coerce_unsized)] +#![feature(mapped_lock_guards)] +#![feature(ptr_metadata)] pub mod layout; pub mod render; diff --git a/core/src/render/primitive.rs b/core/src/render/primitive.rs index 2ba991a..4a75ba1 100644 --- a/core/src/render/primitive.rs +++ b/core/src/render/primitive.rs @@ -1,19 +1,17 @@ -use std::ops::{Deref, DerefMut}; - use crate::{ - layout::{Color, UiRegion}, + layout::{Color, UiRegion, WidgetId}, render::{ ArrBuf, data::{MaskIdx, PrimitiveInstance}, }, - util::Id, }; use bytemuck::Pod; +use std::ops::{Deref, DerefMut}; use wgpu::*; pub struct Primitives { instances: Vec, - assoc: Vec, + assoc: Vec, data: PrimitiveData, free: Vec, pub updated: bool, @@ -99,7 +97,7 @@ macro_rules! primitives { } pub struct PrimitiveInst

{ - pub id: Id, + pub id: WidgetId, pub primitive: P, pub region: UiRegion, pub mask_idx: MaskIdx, @@ -175,7 +173,7 @@ impl Primitives { } pub struct PrimitiveChange { - pub id: Id, + pub id: WidgetId, pub old: usize, pub new: usize, } diff --git a/core/src/util/handle.rs b/core/src/util/handle.rs index 4b208ea..2a9116a 100644 --- a/core/src/util/handle.rs +++ b/core/src/util/handle.rs @@ -1,28 +1,34 @@ use std::{ - cell::{Ref, RefCell, RefMut}, + any::{Any, TypeId}, marker::Unsize, ops::CoerceUnsized, - rc::{Rc, Weak}, + sync::{Arc, Mutex, Weak}, }; -pub struct Handle(Rc>); -pub struct WeakHandle(Weak>); +pub struct Handle(Arc>); +pub struct WeakHandle(Weak>); + +pub type Ref<'a, T> = std::sync::MutexGuard<'a, T>; +pub type RefMut<'a, T> = std::sync::MutexGuard<'a, T>; + +pub type RefMap<'a, T> = std::sync::MappedMutexGuard<'a, T>; +pub type RefMapMut<'a, T> = std::sync::MappedMutexGuard<'a, T>; impl Handle { pub fn get(&self) -> Ref<'_, T> { - self.0.borrow() + self.0.lock().unwrap() } pub fn get_mut(&self) -> RefMut<'_, T> { - self.0.borrow_mut() + self.0.lock().unwrap() } pub fn refs(&self) -> usize { - Rc::strong_count(&self.0) + Arc::strong_count(&self.0) } pub fn weak(&self) -> WeakHandle { - WeakHandle(Rc::downgrade(&self.0)) + WeakHandle(Arc::downgrade(&self.0)) } } @@ -30,6 +36,21 @@ impl WeakHandle { pub fn strong(&self) -> Option> { Some(Handle(self.0.upgrade()?)) } + + pub fn dropped(&self) -> bool { + self.0.strong_count() == 0 + } + + /// # Safety + /// you must guarantee the type outside + pub unsafe fn downcast(self) -> Option> + where + T: 'static, + { + let raw: *const Mutex = self.0.into_raw(); + let raw: *const Mutex = raw.cast(); + Some(WeakHandle(unsafe { Weak::from_raw(raw) })) + } } impl Clone for Handle { @@ -52,7 +73,7 @@ impl Default for Handle { impl From for Handle { fn from(value: T) -> Self { - Self(Rc::new(RefCell::new(value))) + Self(Arc::new(Mutex::new(value))) } } diff --git a/src/bin/test/main.rs b/src/bin/test/main.rs index be4c76c..d21b628 100644 --- a/src/bin/test/main.rs +++ b/src/bin/test/main.rs @@ -51,7 +51,7 @@ impl DefaultAppState for Client { .add(ui); let span_add = Span::empty(Dir::RIGHT).add(ui); - let span_add_ = span_add.clone(); + let span_add_ = span_add.weak(); let add_button = rect(Color::LIME) .radius(30) @@ -64,7 +64,6 @@ impl DefaultAppState for Client { .sized((150, 150)) .align(Align::BOT_RIGHT); - let span_add_ = span_add.clone(); let del_button = rect(Color::RED) .radius(30) .on(CursorSense::click(), move |_| { @@ -99,7 +98,8 @@ impl DefaultAppState for Client { .add(ui); let texts = Span::empty(Dir::DOWN).gap(10).add(ui); - let msg_area = texts.clone().scroll().masked().background(rect(Color::SKY)); + let texts_ = texts.weak(); + let msg_area = texts.scroll().masked().background(rect(Color::SKY)); let add_text = wtext("add") .editable(false) .text_align(Align::LEFT) @@ -114,18 +114,19 @@ impl DefaultAppState for Client { .wrap(true) .attr::(()); let msg_box = text.background(rect(Color::WHITE.darker(0.5))).add(ctx.ui); - texts.get_mut().children.push(msg_box); + texts_.get_mut().children.push(msg_box); }) .add(ui); + let add_text_ = add_text.weak(); let text_edit_scroll = ( msg_area.height(rest(1)), ( Rect::new(Color::WHITE.darker(0.9)), ( - add_text.clone().width(rest(1)), + add_text.width(rest(1)), Rect::new(Color::GREEN) .on(CursorSense::click(), move |ctx| { - ctx.ui.run_event(ctx.state, &add_text, Submit, ()); + ctx.ui.run_event(ctx.state, add_text_, Submit, ()); }) .sized((40, 40)), ) @@ -140,13 +141,24 @@ impl DefaultAppState for Client { .span(Dir::DOWN) .add(ui); - let main = pad_test.clone().pad(10).add(ui); + let main = pad_test.pad(10).add(ui); + let main_ = main.weak(); - let switch_button = |color, to: WidgetRef, label| { - let main_ = main.clone(); + let tab_handles = Handle::from((0, Vec::new())); + + let switch_button = |color, to: Option, label| { + let tab_handles = tab_handles.clone(); + let i = tab_handles.get().1.len(); + tab_handles.get_mut().1.push(to); let rect = rect(color) .on(CursorSense::click(), move |ctx| { - main_.get_mut().inner = to.clone(); + let (prev, all) = &mut *tab_handles.get_mut(); + if let Some(to) = &mut all[i] { + let mut main = main_.get_mut(); + std::mem::swap(&mut main.inner, to); + all.swap(*prev, i); + *prev = i; + } ctx.widget.get_mut().color = color.darker(0.3); }) .on( @@ -162,20 +174,21 @@ impl DefaultAppState for Client { }; let tabs = ( - switch_button(Color::RED, pad_test, "pad"), - switch_button(Color::GREEN, span_test, "span"), - switch_button(Color::BLUE, span_add_test, "image span"), - switch_button(Color::MAGENTA, text_test, "text layout"), + switch_button(Color::RED, None, "pad"), + switch_button(Color::GREEN, Some(span_test), "span"), + switch_button(Color::BLUE, Some(span_add_test), "image span"), + switch_button(Color::MAGENTA, Some(text_test), "text layout"), switch_button( Color::YELLOW.mul_rgb(0.5), - text_edit_scroll, + Some(text_edit_scroll), "text edit scroll", ), ) .span(Dir::RIGHT); - let info = wtext("").add(ui); - let info_sect = info.clone().pad(10).align(Align::RIGHT); + let info_ = wtext("").add(ui); + let info = info_.weak(); + let info_sect = info_.pad(10).align(Align::RIGHT); ((tabs.height(40), main).span(Dir::DOWN), info_sect) .stack() @@ -187,7 +200,7 @@ impl DefaultAppState for Client { fn window_event(&mut self, _: WindowEvent, ui: &mut Ui, state: &UiState) { let new = format!( "widgets: {}\nactive:{}\nviews: {}", - ui.num_widgets(), + total_widgets(), ui.active_widgets(), state.renderer.ui.view_count() ); diff --git a/src/default/attr.rs b/src/default/attr.rs index 298953a..2148c0f 100644 --- a/src/default/attr.rs +++ b/src/default/attr.rs @@ -1,4 +1,4 @@ -use crate::{prelude::*, default::UiState}; +use crate::{default::UiState, prelude::*}; use std::time::{Duration, Instant}; use winit::dpi::{LogicalPosition, LogicalSize}; @@ -7,21 +7,16 @@ pub struct Selector; impl WidgetAttr for Selector { type Input = WidgetRef; - fn run(ui: &mut Ui, container: &WidgetRef, id: Self::Input) { - let container = container.clone(); - ui.register_event( - &container.clone(), - CursorSense::click_or_drag(), - move |mut ctx| { - let ui = ctx.ui; - let region = ui.window_region(&id).unwrap(); - let id_pos = region.top_left; - let container_pos = ui.window_region(&container).unwrap().top_left; - ctx.data.cursor += container_pos - id_pos; - ctx.data.size = region.size(); - select(ui, id.clone(), ctx.state, ctx.data); - }, - ); + fn run(ui: &mut Ui, container: WidgetRef, id: Self::Input) { + ui.register_event(container, CursorSense::click_or_drag(), move |mut ctx| { + let ui = ctx.ui; + let region = ui.window_region(&id).unwrap(); + let id_pos = region.top_left; + let container_pos = ui.window_region(&container).unwrap().top_left; + ctx.data.cursor += container_pos - id_pos; + ctx.data.size = region.size(); + select(ui, id, ctx.state, ctx.data); + }); } } @@ -30,10 +25,9 @@ pub struct Selectable; impl WidgetAttr for Selectable { type Input = (); - fn run(ui: &mut Ui, id: &WidgetRef, _: Self::Input) { - let id = id.clone(); - ui.register_event(&id.clone(), CursorSense::click_or_drag(), move |ctx| { - select(ctx.ui, id.clone(), ctx.state, ctx.data); + fn run(ui: &mut Ui, id: WidgetRef, _: Self::Input) { + ui.register_event(id, CursorSense::click_or_drag(), move |ctx| { + select(ctx.ui, id, ctx.state, ctx.data); }); } } diff --git a/src/default/mod.rs b/src/default/mod.rs index f20fc4b..31eae5d 100644 --- a/src/default/mod.rs +++ b/src/default/mod.rs @@ -120,10 +120,9 @@ impl AppState for DefaultState { ui_state.renderer.resize(size) } WindowEvent::KeyboardInput { event, .. } => { - if let Some(sel) = &ui_state.focus + if let Some(sel) = ui_state.focus && event.state.is_pressed() { - let sel = &sel.clone(); let res = sel.get_mut().apply_event(event, &ui_state.input.modifiers); match res { TextInputResult::Unfocus => { diff --git a/src/event.rs b/src/event.rs index a8ca94b..a28ac51 100644 --- a/src/event.rs +++ b/src/event.rs @@ -13,7 +13,7 @@ pub mod eventable { ) -> impl WidgetIdFn { move |ui| { let id = self.add(ui); - ui.register_widget_event(&id, event, f); + ui.register_widget_event(id.weak(), event, f); id } } @@ -56,7 +56,7 @@ macro_rules! event_ctx { { fn on( &mut self, - widget: &WidgetRef, + widget: WidgetRef, event: E, f: impl WidgetEventFn, ); @@ -68,11 +68,11 @@ macro_rules! event_ctx { { fn on( &mut self, - widget: &WidgetRef, + widget: WidgetRef, event: E, f: impl WidgetEventFn<$ty, E::Data, W>, ) { - self.register_widget_event(&widget, event, f); + self.register_widget_event(widget, event, f); } } } diff --git a/src/widget/mask.rs b/src/widget/mask.rs index 6b9b582..5b16337 100644 --- a/src/widget/mask.rs +++ b/src/widget/mask.rs @@ -1,7 +1,7 @@ use crate::prelude::*; pub struct Masked { - pub inner: WidgetRef, + pub inner: WidgetHandle, } impl Widget for Masked { diff --git a/src/widget/position/align.rs b/src/widget/position/align.rs index 9310399..c475b4b 100644 --- a/src/widget/position/align.rs +++ b/src/widget/position/align.rs @@ -1,7 +1,7 @@ use crate::prelude::*; pub struct Aligned { - pub inner: WidgetRef, + pub inner: WidgetHandle, pub align: Align, } diff --git a/src/widget/position/layer.rs b/src/widget/position/layer.rs index e002871..ef55139 100644 --- a/src/widget/position/layer.rs +++ b/src/widget/position/layer.rs @@ -1,7 +1,7 @@ use crate::prelude::*; pub struct LayerOffset { - pub inner: WidgetRef, + pub inner: WidgetHandle, pub offset: usize, } diff --git a/src/widget/position/max_size.rs b/src/widget/position/max_size.rs index 635bfbb..83071d8 100644 --- a/src/widget/position/max_size.rs +++ b/src/widget/position/max_size.rs @@ -1,7 +1,7 @@ use crate::prelude::*; pub struct MaxSize { - pub inner: WidgetRef, + pub inner: WidgetHandle, pub x: Option, pub y: Option, } diff --git a/src/widget/position/offset.rs b/src/widget/position/offset.rs index 43cdf6c..e7a19d8 100644 --- a/src/widget/position/offset.rs +++ b/src/widget/position/offset.rs @@ -1,7 +1,7 @@ use crate::prelude::*; pub struct Offset { - pub inner: WidgetRef, + pub inner: WidgetHandle, pub amt: UiVec2, } diff --git a/src/widget/position/pad.rs b/src/widget/position/pad.rs index 2c23eb1..26554ee 100644 --- a/src/widget/position/pad.rs +++ b/src/widget/position/pad.rs @@ -2,7 +2,7 @@ use crate::prelude::*; pub struct Pad { pub padding: Padding, - pub inner: WidgetRef, + pub inner: WidgetHandle, } impl Widget for Pad { diff --git a/src/widget/position/scroll.rs b/src/widget/position/scroll.rs index 6acc299..c414b11 100644 --- a/src/widget/position/scroll.rs +++ b/src/widget/position/scroll.rs @@ -1,7 +1,7 @@ use crate::prelude::*; pub struct Scroll { - inner: WidgetRef, + inner: WidgetHandle, axis: Axis, amt: f32, snap_end: bool, @@ -41,7 +41,7 @@ impl Widget for Scroll { } impl Scroll { - pub fn new(inner: WidgetRef, axis: Axis) -> Self { + pub fn new(inner: WidgetHandle, axis: Axis) -> Self { Self { inner, axis, diff --git a/src/widget/position/sized.rs b/src/widget/position/sized.rs index b4def5c..8aed65e 100644 --- a/src/widget/position/sized.rs +++ b/src/widget/position/sized.rs @@ -1,7 +1,7 @@ use crate::prelude::*; pub struct Sized { - pub inner: WidgetRef, + pub inner: WidgetHandle, pub x: Option, pub y: Option, } diff --git a/src/widget/position/span.rs b/src/widget/position/span.rs index b3b1e3e..007455c 100644 --- a/src/widget/position/span.rs +++ b/src/widget/position/span.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use std::marker::PhantomData; pub struct Span { - pub children: Vec, + pub children: Vec, pub dir: Dir, pub gap: f32, } @@ -158,7 +158,7 @@ impl, Tag> FnOnce<(&mut Ui,)> extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { Span { - children: self.children.ui(args.0).arr.to_vec(), + children: self.children.ui(args.0).arr.into_iter().collect(), dir: self.dir, gap: self.gap, } @@ -182,7 +182,7 @@ impl, Tag> SpanBuilder; + type Target = Vec; fn deref(&self) -> &Self::Target { &self.children diff --git a/src/widget/position/stack.rs b/src/widget/position/stack.rs index b4e90cc..f25b874 100644 --- a/src/widget/position/stack.rs +++ b/src/widget/position/stack.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use crate::prelude::*; pub struct Stack { - pub children: Vec, + pub children: Vec, pub size: StackSize, } @@ -55,7 +55,7 @@ impl, Tag> FnOnce<(&mut Ui,)> extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { Stack { - children: self.children.ui(args.0).arr.to_vec(), + children: self.children.ui(args.0).arr.into_iter().collect(), size: self.size, } } diff --git a/src/widget/ptr.rs b/src/widget/ptr.rs index c969b40..9d0f3f6 100644 --- a/src/widget/ptr.rs +++ b/src/widget/ptr.rs @@ -2,7 +2,7 @@ use crate::prelude::*; #[derive(Default)] pub struct WidgetPtr { - pub inner: Option, + pub inner: Option, } impl Widget for WidgetPtr { diff --git a/src/widget/sense.rs b/src/widget/sense.rs index feedc8d..59ec132 100644 --- a/src/widget/sense.rs +++ b/src/widget/sense.rs @@ -1,15 +1,12 @@ use crate::prelude::*; -use crate::util::Id; - -use std::{ - ops::{BitOr, Deref, DerefMut}, - rc::Rc, -}; - use crate::{ layout::{UiModule, UiRegion, Vec2}, util::HashMap, }; +use std::{ + ops::{BitOr, Deref, DerefMut}, + rc::Rc, +}; #[derive(Clone, Copy, PartialEq)] pub enum CursorButton { @@ -100,10 +97,10 @@ pub enum ActivationState { /// but I'm not sure how or if worth it pub struct Sensor { pub senses: CursorSenses, - pub f: Rc>, + pub f: Box>, } -pub type SensorMap = HashMap>; +pub type SensorMap = HashMap>; pub type SenseShape = UiRegion; pub struct SensorGroup { pub hover: ActivationState, @@ -122,7 +119,7 @@ pub struct CursorData { pub struct CursorModule { map: SensorMap, - active: HashMap>, + active: HashMap>, } impl UiModule for CursorModule { @@ -141,7 +138,7 @@ impl UiModule for CursorModule { } } - fn on_remove(&mut self, id: &Id) { + fn on_remove(&mut self, id: &WidgetId) { self.map.remove(id); for layer in self.active.values_mut() { layer.remove(id); @@ -307,34 +304,34 @@ impl Event for CursorSense { impl::Data> + Into, Ctx: 'static> EventModule for CursorModule { - fn register(&mut self, id: Id, senses: E, f: impl EventFn::Data>) { + fn register(&mut self, id: WidgetId, senses: E, f: impl EventFn::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), + f: Box::new(f), }); } - fn run<'a>( - &self, - id: &Id, + fn run( + &mut self, + id: WidgetId, event: E, - ) -> Option::Data>) + use<'a, E, Ctx>> { + ) -> Option::Data>)> { let senses = event.into(); - if let Some(group) = self.map.get(id) { + if let Some(group) = self.map.get_mut(&id) { let fs: Vec<_> = group .sensors - .iter() + .iter_mut() .filter_map(|sensor| { if sensor.senses.iter().any(|s| senses.contains(s)) { - Some(sensor.f.clone()) + Some(&mut sensor.f) } else { None } }) .collect(); Some(move |ctx: EventCtx| { - for f in &fs { + for f in fs { f(EventCtx { state: ctx.state, ui: ctx.ui, diff --git a/src/widget/text/mod.rs b/src/widget/text/mod.rs index 8fd671d..dae3709 100644 --- a/src/widget/text/mod.rs +++ b/src/widget/text/mod.rs @@ -21,11 +21,11 @@ pub struct TextView { // cache tex: Option, width: Option, - pub hint: Option, + pub hint: Option, } impl TextView { - pub fn new(buf: TextBuffer, attrs: TextAttrs, hint: Option) -> Self { + pub fn new(buf: TextBuffer, attrs: TextAttrs, hint: Option) -> Self { Self { attrs: attrs.into(), buf: buf.into(), diff --git a/src/widget/trait_fns.rs b/src/widget/trait_fns.rs index a42ad1a..8ae4bf6 100644 --- a/src/widget/trait_fns.rs +++ b/src/widget/trait_fns.rs @@ -127,7 +127,7 @@ widget_trait! { |ui| self.add(ui) } - fn set_ptr(self, ptr: &WidgetRef, ui: &mut Ui) { + fn set_ptr(self, ptr: &WidgetHandle, ui: &mut Ui) { ptr.get_mut().inner = Some(self.add(ui)); } }