initial global widget store

This commit is contained in:
2025-12-10 01:42:39 -05:00
parent 7f4846a2d3
commit 6156c66a20
31 changed files with 536 additions and 352 deletions

View File

@@ -2,7 +2,7 @@ use crate::layout::{Ui, WidgetIdFn, WidgetLike, WidgetRef};
pub trait WidgetAttr<W: ?Sized> { pub trait WidgetAttr<W: ?Sized> {
type Input; type Input;
fn run(ui: &mut Ui, id: &WidgetRef<W>, input: Self::Input); fn run(ui: &mut Ui, id: WidgetRef<W>, input: Self::Input);
} }
pub trait Attrable<W: ?Sized, Tag> { pub trait Attrable<W: ?Sized, Tag> {
@@ -13,7 +13,7 @@ impl<WL: WidgetLike<Tag>, Tag> Attrable<WL::Widget, Tag> for WL {
fn attr<A: WidgetAttr<WL::Widget>>(self, input: A::Input) -> impl WidgetIdFn<WL::Widget> { fn attr<A: WidgetAttr<WL::Widget>>(self, input: A::Input) -> impl WidgetIdFn<WL::Widget> {
|ui| { |ui| {
let id = self.add(ui); let id = self.add(ui);
A::run(ui, &id, input); A::run(ui, id.weak(), input);
id id
} }
} }

View File

@@ -1,8 +1,8 @@
use std::{hash::Hash, rc::Rc}; use std::hash::Hash;
use crate::{ use crate::{
layout::{IdFnTag, Ui, UiModule, WidgetIdFn, WidgetLike, WidgetRef}, layout::{Ui, UiModule, WidgetId, WidgetRef},
util::{HashMap, Id}, util::HashMap,
}; };
pub trait Event: Sized { 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 type ECtx<'a, Ctx, Data, W> = EventIdCtx<'a, Ctx, Data, W>;
pub struct EventIdCtx<'a, Ctx, Data, W: ?Sized> { pub struct EventIdCtx<'a, Ctx, Data, W: ?Sized> {
pub widget: &'a WidgetRef<W>, pub widget: WidgetRef<W>,
pub ui: &'a mut Ui, pub ui: &'a mut Ui,
pub state: &'a mut Ctx, pub state: &'a mut Ctx,
pub data: Data, pub data: Data,
} }
pub trait EventFn<Ctx, Data>: Fn(EventCtx<Ctx, Data>) + 'static {} pub trait EventFn<Ctx, Data>: FnMut(EventCtx<Ctx, Data>) + 'static {}
impl<F: Fn(EventCtx<Ctx, Data>) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {} impl<F: FnMut(EventCtx<Ctx, Data>) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {}
pub trait WidgetEventFn<Ctx, Data, W: ?Sized>: Fn(EventIdCtx<Ctx, Data, W>) + 'static {} pub trait WidgetEventFn<Ctx, Data, W: ?Sized>: FnMut(EventIdCtx<Ctx, Data, W>) + 'static {}
impl<F: Fn(EventIdCtx<Ctx, Data, W>) + 'static, Ctx, Data, W: ?Sized> WidgetEventFn<Ctx, Data, W> impl<F: FnMut(EventIdCtx<Ctx, Data, W>) + 'static, Ctx, Data, W: ?Sized> WidgetEventFn<Ctx, Data, W>
for F for F
{ {
} }
impl Ui { impl Ui {
pub fn register_event<W: ?Sized, E: Event, Ctx: 'static>( pub fn register_event<W: ?Sized, E: Event, Ctx: 'static>(
&mut self, &mut self,
id: &WidgetRef<W>, id: WidgetRef<W>,
event: E, event: E,
f: impl EventFn<Ctx, E::Data>, f: impl EventFn<Ctx, E::Data>,
) { ) {
@@ -49,17 +48,16 @@ impl Ui {
pub fn register_widget_event<W: ?Sized + 'static, E: Event, Ctx: 'static>( pub fn register_widget_event<W: ?Sized + 'static, E: Event, Ctx: 'static>(
&mut self, &mut self,
id: &WidgetRef<W>, id: WidgetRef<W>,
event: E, event: E,
f: impl WidgetEventFn<Ctx, E::Data, W>, mut f: impl WidgetEventFn<Ctx, E::Data, W>,
) { ) {
let id_ = id.weak();
self.data self.data
.modules .modules
.get_mut::<E::Module<Ctx>>() .get_mut::<E::Module<Ctx>>()
.register(id.id(), event, move |ctx| { .register(id.id(), event, move |ctx| {
f(EventIdCtx { (&mut f)(EventIdCtx {
widget: &id_.expect_strong(), widget: id,
ui: ctx.ui, ui: ctx.ui,
state: ctx.state, state: ctx.state,
data: ctx.data, data: ctx.data,
@@ -78,21 +76,17 @@ impl<E: DefaultEvent> Event for E {
} }
pub trait EventModule<E: Event, Ctx>: UiModule + Default { pub trait EventModule<E: Event, Ctx>: UiModule + Default {
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, E::Data>); fn register(&mut self, id: WidgetId, event: E, f: impl EventFn<Ctx, E::Data>);
fn run<'a>( fn run(&mut self, id: WidgetId, event: E) -> Option<impl FnOnce(EventCtx<Ctx, E::Data>)>;
&self,
id: &Id,
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<Box<dyn EventFn<Ctx, Data>>>>;
pub struct DefaultEventModule<E: Event, Ctx> { pub struct DefaultEventModule<E: Event, Ctx> {
map: HashMap<E, EventFnMap<Ctx, <E as Event>::Data>>, map: HashMap<E, EventFnMap<Ctx, <E as Event>::Data>>,
} }
impl<E: Event + 'static, Ctx: 'static> UiModule for DefaultEventModule<E, Ctx> { impl<E: Event + 'static, Ctx: 'static> UiModule for DefaultEventModule<E, Ctx> {
fn on_remove(&mut self, id: &Id) { fn on_remove(&mut self, id: &WidgetId) {
for map in self.map.values_mut() { for map in self.map.values_mut() {
map.remove(id); map.remove(id);
} }
@@ -103,26 +97,21 @@ pub trait HashableEvent: Event + Hash + Eq + 'static {}
impl<E: Event + Hash + Eq + 'static> HashableEvent for E {} impl<E: Event + Hash + Eq + 'static> HashableEvent for E {}
impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<E, Ctx> { impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<E, Ctx> {
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, <E as Event>::Data>) { fn register(&mut self, id: WidgetId, event: E, f: impl EventFn<Ctx, <E as Event>::Data>) {
self.map self.map
.entry(event) .entry(event)
.or_default() .or_default()
.entry(id) .entry(id)
.or_default() .or_default()
.push(Rc::new(f)); .push(Box::new(f));
} }
fn run<'a>( fn run(&mut self, id: WidgetId, event: E) -> Option<impl FnOnce(EventCtx<Ctx, E::Data>)> {
&self, if let Some(map) = self.map.get_mut(&event)
id: &Id, && let Some(fs) = map.get_mut(&id)
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 fs = fs.clone();
Some(move |ctx: EventCtx<Ctx, <E as Event>::Data>| { Some(move |ctx: EventCtx<Ctx, <E as Event>::Data>| {
for f in &fs { for f in fs {
f(EventCtx { f(EventCtx {
ui: ctx.ui, ui: ctx.ui,
state: ctx.state, state: ctx.state,
@@ -137,12 +126,12 @@ impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<
} }
impl<E: HashableEvent, Ctx: 'static> DefaultEventModule<E, Ctx> { impl<E: HashableEvent, Ctx: 'static> DefaultEventModule<E, Ctx> {
pub fn run_all(&self, event: E, ctx: EventCtx<Ctx, E::Data>) pub fn run_all(&mut self, event: E, ctx: EventCtx<Ctx, E::Data>)
where where
E::Data: Clone, E::Data: Clone,
{ {
if let Some(map) = self.map.get(&event) { if let Some(map) = self.map.get_mut(&event) {
for fs in map.values() { for fs in map.values_mut() {
for f in fs { for f in fs {
f(EventCtx { f(EventCtx {
ui: ctx.ui, ui: ctx.ui,
@@ -167,7 +156,7 @@ impl Ui {
pub fn run_event<E: Event, Ctx: 'static, W: ?Sized>( pub fn run_event<E: Event, Ctx: 'static, W: ?Sized>(
&mut self, &mut self,
ctx: &mut Ctx, ctx: &mut Ctx,
id: &WidgetRef<W>, id: WidgetRef<W>,
event: E, event: E,
data: E::Data, data: E::Data,
) { ) {
@@ -175,10 +164,11 @@ impl Ui {
.data .data
.modules .modules
.get_mut::<E::Module<Ctx>>() .get_mut::<E::Module<Ctx>>()
.run(&id.id(), event) .run(id.id(), event)
{ {
eprintln!("FIXME");
f(EventCtx { f(EventCtx {
ui: self, ui: &mut Ui::new(),
state: ctx, state: ctx,
data, data,
}); });

View File

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

View File

@@ -1,84 +1,85 @@
use std::{cell::Ref, marker::Unsize, sync::mpsc::Sender}; use std::{marker::Unsize, sync::mpsc::Sender};
use crate::{ use crate::{
layout::{ layout::{
Axis, Len, Modules, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData, 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}, render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst},
util::{HashMap, HashSet, Id, TrackedArena}, util::{HashMap, HashSet, RefMap, TrackedArena},
}; };
/// makes your surfaces look pretty /// makes your surfaces look pretty
pub struct Painter<'a, 'c> { pub struct Painter<'a, 'c> {
ctx: &'a mut PainterCtx<'c>, ctx: &'a mut PainterCtx<'c>,
widget: WidgetRef, widget: WidgetHandle,
id: Id, id: WidgetId,
region: UiRegion, region: UiRegion,
mask: MaskIdx, mask: MaskIdx,
textures: Vec<TextureHandle>, textures: Vec<TextureHandle>,
primitives: Vec<PrimitiveHandle>, primitives: Vec<PrimitiveHandle>,
children: Vec<Id>, children: Vec<WidgetId>,
children_width: HashMap<Id, (UiVec2, Len)>, children_width: HashMap<WidgetId, (UiVec2, Len)>,
children_height: HashMap<Id, (UiVec2, Len)>, children_height: HashMap<WidgetId, (UiVec2, Len)>,
pub layer: usize, pub layer: usize,
} }
/// context for a painter; lets you draw and redraw widgets /// context for a painter; lets you draw and redraw widgets
struct PainterCtx<'a> { struct PainterCtx<'a> {
pub widgets: &'a Widgets, pub active: &'a mut HashMap<WidgetId, WidgetInstance>,
pub active: &'a mut HashMap<Id, WidgetInstance>,
pub layers: &'a mut PrimitiveLayers, pub layers: &'a mut PrimitiveLayers,
pub textures: &'a mut Textures, pub textures: &'a mut Textures,
pub masks: &'a mut TrackedArena<Mask, u32>, pub masks: &'a mut TrackedArena<Mask, u32>,
pub text: &'a mut TextData, pub text: &'a mut TextData,
pub output_size: Vec2, pub output_size: Vec2,
pub modules: &'a mut Modules, pub modules: &'a mut Modules,
pub cache_width: HashMap<Id, (UiVec2, Len)>, send: &'a Sender<WidgetUpdate>,
pub cache_height: HashMap<Id, (UiVec2, Len)>, pub cache_width: HashMap<WidgetId, (UiVec2, Len)>,
pub needs_redraw: HashSet<Id>, pub cache_height: HashMap<WidgetId, (UiVec2, Len)>,
draw_started: HashSet<Id>, pub needs_redraw: HashSet<WidgetId>,
draw_started: HashSet<WidgetId>,
} }
/// stores information for children about the highest level parent that needed their size /// stores information for children about the highest level parent that needed their size
/// so that they can redraw the parent if their size changes /// so that they can redraw the parent if their size changes
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct ResizeRef { pub struct ResizeRef {
x: Option<(Id, (UiVec2, Len))>, x: Option<(WidgetId, (UiVec2, Len))>,
y: Option<(Id, (UiVec2, Len))>, y: Option<(WidgetId, (UiVec2, Len))>,
} }
/// important non rendering data for retained drawing /// important non rendering data for retained drawing
#[derive(Debug)] #[derive(Debug)]
pub struct WidgetInstance { pub struct WidgetInstance {
pub id: Id, pub id: WidgetId,
pub region: UiRegion, pub region: UiRegion,
pub parent: Option<Id>, pub parent: Option<WidgetId>,
pub textures: Vec<TextureHandle>, pub textures: Vec<TextureHandle>,
pub primitives: Vec<PrimitiveHandle>, pub primitives: Vec<PrimitiveHandle>,
pub children: Vec<Id>, pub children: Vec<WidgetId>,
pub resize: ResizeRef, pub resize: ResizeRef,
pub mask: MaskIdx, pub mask: MaskIdx,
pub layer: usize, pub layer: usize,
} }
/// data to be stored in Ui to create PainterCtxs easily /// data to be stored in Ui to create PainterCtxs easily
/// TODO: actually use this LMAO
pub struct PainterData { pub struct PainterData {
pub widgets: Widgets, pub active: HashMap<WidgetId, WidgetInstance>,
pub active: HashMap<Id, WidgetInstance>,
pub layers: PrimitiveLayers, pub layers: PrimitiveLayers,
pub textures: Textures, pub textures: Textures,
pub text: TextData, pub text: TextData,
pub output_size: Vec2, pub output_size: Vec2,
pub modules: Modules, pub modules: Modules,
pub px_dependent: HashSet<Id>, pub px_dependent: HashSet<WidgetId>,
pub masks: TrackedArena<Mask, u32>, pub masks: TrackedArena<Mask, u32>,
send: Sender<WidgetUpdate>,
} }
impl PainterData { impl PainterData {
pub fn new(send: Sender<WidgetUpdate>) -> Self { pub fn new(send: Sender<WidgetUpdate>) -> Self {
Self { Self {
widgets: Widgets::new(send),
active: Default::default(), active: Default::default(),
layers: Default::default(), layers: Default::default(),
textures: Default::default(), textures: Default::default(),
@@ -87,6 +88,7 @@ impl PainterData {
modules: Default::default(), modules: Default::default(),
px_dependent: Default::default(), px_dependent: Default::default(),
masks: Default::default(), masks: Default::default(),
send,
} }
} }
} }
@@ -95,7 +97,7 @@ impl<'a> PainterCtx<'a> {
/// redraws a widget that's currently active (drawn) /// redraws a widget that's currently active (drawn)
/// can be called on something already drawn or removed, /// can be called on something already drawn or removed,
/// will just return if so /// will just return if so
pub fn redraw<W: Widget + ?Sized + Unsize<dyn Widget>>(&mut self, widget: &WidgetRef<W>) { pub fn redraw<W: Widget + ?Sized + Unsize<dyn Widget>>(&mut self, widget: &WidgetHandle<W>) {
let id = widget.id(); let id = widget.id();
self.needs_redraw.remove(&id); self.needs_redraw.remove(&id);
if self.draw_started.contains(&id) { if self.draw_started.contains(&id) {
@@ -131,9 +133,11 @@ impl<'a> PainterCtx<'a> {
id, id,
} }
.width(widget); .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 // unsure if I need to walk down the tree here
self.redraw(&self.widgets.get(*rid)); self.redraw(&w);
*old_desired = new_desired; *old_desired = new_desired;
if self.draw_started.contains(&id) { if self.draw_started.contains(&id) {
ret = true; ret = true;
@@ -156,8 +160,10 @@ impl<'a> PainterCtx<'a> {
id, id,
} }
.height(widget); .height(widget);
if new_desired != *old_desired { if new_desired != *old_desired
self.redraw(&self.widgets.get(*rid)); && let Some(w) = rid.strong()
{
self.redraw(&w);
*old_desired = new_desired; *old_desired = new_desired;
if self.draw_started.contains(&id) { if self.draw_started.contains(&id) {
ret = true; ret = true;
@@ -187,13 +193,14 @@ impl<'a> PainterCtx<'a> {
fn draw_inner<W: Widget + ?Sized + Unsize<dyn Widget>>( fn draw_inner<W: Widget + ?Sized + Unsize<dyn Widget>>(
&mut self, &mut self,
layer: usize, layer: usize,
widget: &WidgetRef<W>, widget: &WidgetHandle<W>,
region: UiRegion, region: UiRegion,
parent: Option<Id>, parent: Option<WidgetId>,
mask: MaskIdx, mask: MaskIdx,
old_children: Option<Vec<Id>>, old_children: Option<Vec<WidgetId>>,
) { ) {
let id = widget.id(); let id = widget.id();
widget.data_mut().send = Some(self.send.clone());
// I have no idea if these checks work lol // I have no idea if these checks work lol
// the idea is u can't redraw stuff u already drew, // the idea is u can't redraw stuff u already drew,
// and if parent is different then there's another copy with a different parent // 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); self.active.insert(id, instance);
} }
fn mov(&mut self, id: Id, from: UiRegion, to: UiRegion) { fn mov(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) {
let active = self.active.get_mut(&id).unwrap(); let active = self.active.get_mut(&id).unwrap();
for h in &active.primitives { for h in &active.primitives {
let region = self.layers[h.layer].region_mut(h); let region = self.layers[h.layer].region_mut(h);
@@ -312,7 +319,7 @@ impl<'a> PainterCtx<'a> {
} }
/// NOTE: instance textures are cleared and self.textures freed /// NOTE: instance textures are cleared and self.textures freed
fn remove(&mut self, id: Id) -> Option<WidgetInstance> { fn remove(&mut self, id: WidgetId) -> Option<WidgetInstance> {
let mut inst = self.active.remove(&id); let mut inst = self.active.remove(&id);
if let Some(inst) = &mut inst { if let Some(inst) = &mut inst {
for h in &inst.primitives { for h in &inst.primitives {
@@ -330,7 +337,7 @@ impl<'a> PainterCtx<'a> {
inst inst
} }
fn remove_rec(&mut self, id: Id) -> Option<WidgetInstance> { fn remove_rec(&mut self, id: WidgetId) -> Option<WidgetInstance> {
let inst = self.remove(id); let inst = self.remove(id);
if let Some(inst) = &inst { if let Some(inst) = &inst {
for c in &inst.children { for c in &inst.children {
@@ -342,9 +349,8 @@ impl<'a> PainterCtx<'a> {
} }
impl PainterData { impl PainterData {
fn ctx(&mut self, needs_redraw: HashSet<Id>) -> PainterCtx<'_> { fn ctx(&mut self, needs_redraw: HashSet<WidgetId>) -> PainterCtx<'_> {
PainterCtx { PainterCtx {
widgets: &self.widgets,
active: &mut self.active, active: &mut self.active,
layers: &mut self.layers, layers: &mut self.layers,
textures: &mut self.textures, textures: &mut self.textures,
@@ -352,6 +358,7 @@ impl PainterData {
output_size: self.output_size, output_size: self.output_size,
modules: &mut self.modules, modules: &mut self.modules,
masks: &mut self.masks, masks: &mut self.masks,
send: &self.send,
cache_width: Default::default(), cache_width: Default::default(),
cache_height: Default::default(), cache_height: Default::default(),
draw_started: Default::default(), draw_started: Default::default(),
@@ -359,17 +366,19 @@ impl PainterData {
} }
} }
pub fn draw<W: Widget + ?Sized + Unsize<dyn Widget>>(&mut self, id: &WidgetRef<W>) { pub fn draw<W: Widget + ?Sized + Unsize<dyn Widget>>(&mut self, id: &WidgetHandle<W>) {
let mut ctx = self.ctx(Default::default()); let mut ctx = self.ctx(Default::default());
ctx.draw_started.clear(); ctx.draw_started.clear();
ctx.layers.clear(); ctx.layers.clear();
ctx.draw_inner(0, id, UiRegion::FULL, None, MaskIdx::NONE, None); ctx.draw_inner(0, id, UiRegion::FULL, None, MaskIdx::NONE, None);
} }
pub fn redraw(&mut self, ids: HashSet<Id>) { pub fn redraw(&mut self, ids: HashSet<WidgetId>) {
let mut ctx = self.ctx(ids); let mut ctx = self.ctx(ids);
while let Some(&id) = ctx.needs_redraw.iter().next() { while let Some(&id) = ctx.needs_redraw.iter().next() {
ctx.redraw(&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. /// Draws a widget within this widget's region.
pub fn widget<W: Widget + ?Sized + Unsize<dyn Widget>>(&mut self, id: &WidgetRef<W>) { pub fn widget<W: Widget + ?Sized + Unsize<dyn Widget>>(&mut self, id: &WidgetHandle<W>) {
self.widget_at(id, self.region); self.widget_at(id, self.region);
} }
@@ -415,7 +424,7 @@ impl<'a, 'c> Painter<'a, 'c> {
/// Useful for drawing child widgets in select areas. /// Useful for drawing child widgets in select areas.
pub fn widget_within<W: Widget + ?Sized + Unsize<dyn Widget>>( pub fn widget_within<W: Widget + ?Sized + Unsize<dyn Widget>>(
&mut self, &mut self,
id: &WidgetRef<W>, id: &WidgetHandle<W>,
region: UiRegion, region: UiRegion,
) { ) {
self.widget_at(id, region.within(&self.region)); self.widget_at(id, region.within(&self.region));
@@ -423,7 +432,7 @@ impl<'a, 'c> Painter<'a, 'c> {
fn widget_at<W: Widget + ?Sized + Unsize<dyn Widget>>( fn widget_at<W: Widget + ?Sized + Unsize<dyn Widget>>(
&mut self, &mut self,
id: &WidgetRef<W>, id: &WidgetHandle<W>,
region: UiRegion, region: UiRegion,
) { ) {
self.children.push(id.id()); self.children.push(id.id());
@@ -458,11 +467,11 @@ impl<'a, 'c> Painter<'a, 'c> {
self.region self.region
} }
pub fn size<W: Widget + ?Sized>(&mut self, id: &WidgetRef<W>) -> Size { pub fn size<W: Widget + ?Sized>(&mut self, id: &WidgetHandle<W>) -> Size {
self.size_ctx().size(id) self.size_ctx().size(id)
} }
pub fn len_axis<W: Widget + ?Sized>(&mut self, id: &WidgetRef<W>, axis: Axis) -> Len { pub fn len_axis<W: Widget + ?Sized>(&mut self, id: &WidgetHandle<W>, axis: Axis) -> Len {
match axis { match axis {
Axis::X => self.size_ctx().width(id), Axis::X => self.size_ctx().width(id),
Axis::Y => self.size_ctx().height(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); self.layer = self.ctx.layers.next(self.layer);
} }
pub fn label(&self) -> Ref<'_, String> { pub fn label(&self) -> RefMap<'_, String> {
self.widget.get_label() self.widget.get_label()
} }
pub fn id(&self) -> Id { pub fn id(&self) -> WidgetId {
self.id self.id
} }
} }
@@ -516,27 +525,27 @@ impl<'a, 'c> Painter<'a, 'c> {
pub struct SizeCtx<'a> { pub struct SizeCtx<'a> {
pub text: &'a mut TextData, pub text: &'a mut TextData,
pub textures: &'a mut Textures, pub textures: &'a mut Textures,
source: Id, source: WidgetId,
cache_width: &'a mut HashMap<Id, (UiVec2, Len)>, cache_width: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
cache_height: &'a mut HashMap<Id, (UiVec2, Len)>, cache_height: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
checked_width: &'a mut HashMap<Id, (UiVec2, Len)>, checked_width: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
checked_height: &'a mut HashMap<Id, (UiVec2, Len)>, checked_height: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
/// TODO: should this be pub? rn used for sized /// TODO: should this be pub? rn used for sized
pub outer: UiVec2, pub outer: UiVec2,
output_size: Vec2, output_size: Vec2,
id: Id, id: WidgetId,
} }
impl SizeCtx<'_> { impl SizeCtx<'_> {
pub fn id(&self) -> &Id { pub fn id(&self) -> &WidgetId {
&self.id &self.id
} }
pub fn source(&self) -> &Id { pub fn source(&self) -> &WidgetId {
&self.source &self.source
} }
pub fn width<W: Widget + ?Sized>(&mut self, widget: &WidgetRef<W>) -> Len { pub fn width<W: Widget + ?Sized>(&mut self, widget: &WidgetHandle<W>) -> Len {
// first check cache // first check cache
// TODO: is this needed? broken rn bc does not store children during upper size check, // TODO: is this needed? broken rn bc does not store children during upper size check,
// so if something actually using check_* hits cache it fails to add them // so if something actually using check_* hits cache it fails to add them
@@ -562,7 +571,7 @@ impl SizeCtx<'_> {
} }
// TODO: should be refactored to share code w width_inner // TODO: should be refactored to share code w width_inner
pub fn height<W: Widget + ?Sized>(&mut self, widget: &WidgetRef<W>) -> Len { pub fn height<W: Widget + ?Sized>(&mut self, widget: &WidgetHandle<W>) -> Len {
// if let Some(&(outer, len)) = self.cache_height.get(&id) // if let Some(&(outer, len)) = self.cache_height.get(&id)
// && outer == self.outer // && outer == self.outer
// { // {
@@ -581,14 +590,14 @@ impl SizeCtx<'_> {
len len
} }
pub fn len_axis<W: Widget + ?Sized>(&mut self, id: &WidgetRef<W>, axis: Axis) -> Len { pub fn len_axis<W: Widget + ?Sized>(&mut self, id: &WidgetHandle<W>, axis: Axis) -> Len {
match axis { match axis {
Axis::X => self.width(id), Axis::X => self.width(id),
Axis::Y => self.height(id), Axis::Y => self.height(id),
} }
} }
pub fn size<W: Widget + ?Sized>(&mut self, id: &WidgetRef<W>) -> Size { pub fn size<W: Widget + ?Sized>(&mut self, id: &WidgetHandle<W>) -> Size {
Size { Size {
x: self.width(id), x: self.width(id),
y: self.height(id), y: self.height(id),

View File

@@ -2,32 +2,24 @@ use image::DynamicImage;
use crate::{ use crate::{
layout::{ layout::{
IdLike, PainterData, PixelRegion, TextureHandle, Vec2, Widget, WidgetInstance, WidgetLike, IdLike, PainterData, PixelRegion, TextureHandle, Vec2, Widget, WidgetHandle, WidgetId,
WidgetRef, WidgetUpdate, WidgetInstance, WidgetLike, WidgetUpdate,
}, },
util::{HashSet, Id}, util::HashSet,
}; };
use std::sync::mpsc::{Receiver, channel}; use std::sync::mpsc::{Receiver, channel};
pub struct Ui { pub struct Ui {
pub(crate) data: PainterData, pub(crate) data: PainterData,
root: Option<WidgetRef>, root: Option<WidgetHandle>,
updates: HashSet<Id>, updates: HashSet<WidgetId>,
free: Vec<Id>, free: Vec<WidgetId>,
recv: Receiver<WidgetUpdate>, recv: Receiver<WidgetUpdate>,
full_redraw: bool, full_redraw: bool,
resized: bool, resized: bool,
} }
impl Ui { impl Ui {
pub fn add<W: Widget, Tag>(&mut self, w: impl WidgetLike<Tag, Widget = W>) -> WidgetRef<W> {
w.add(self)
}
pub fn add_widget<W: Widget>(&mut self, w: W) -> WidgetRef<W> {
self.data.widgets.insert(w)
}
pub fn set_root<Tag>(&mut self, w: impl WidgetLike<Tag>) { pub fn set_root<Tag>(&mut self, w: impl WidgetLike<Tag>) {
self.root = Some(w.add(self)); self.root = Some(w.add(self));
self.full_redraw = true; self.full_redraw = true;
@@ -97,7 +89,6 @@ impl Ui {
for m in self.data.modules.iter_mut() { for m in self.data.modules.iter_mut() {
m.on_remove(&id); m.on_remove(&id);
} }
self.data.widgets.delete(id);
} }
self.data.textures.free(); self.data.textures.free();
} }
@@ -106,10 +97,6 @@ impl Ui {
self.full_redraw || !self.updates.is_empty() self.full_redraw || !self.updates.is_empty()
} }
pub fn num_widgets(&self) -> usize {
self.data.widgets.len()
}
pub fn active_widgets(&self) -> usize { pub fn active_widgets(&self) -> usize {
self.data.active.len() self.data.active.len()
} }
@@ -133,7 +120,7 @@ impl Ui {
pub fn debug(&self, label: &str) -> impl Iterator<Item = &WidgetInstance> { pub fn debug(&self, label: &str) -> impl Iterator<Item = &WidgetInstance> {
self.data.active.iter().filter_map(move |(id, inst)| { 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 { if widget.get_label().as_str() == label {
Some(inst) Some(inst)
} else { } else {

View File

@@ -1,9 +1,9 @@
use crate::layout::{Widget, WidgetLike, WidgetRef}; use crate::layout::{Widget, WidgetHandle, WidgetLike};
use std::marker::Unsize; use std::marker::Unsize;
pub trait WidgetView { pub trait WidgetView {
type Widget: Widget + ?Sized + Unsize<dyn Widget> = dyn Widget; type Widget: Widget + ?Sized + Unsize<dyn Widget> = dyn Widget;
fn view(&self) -> &WidgetRef<Self::Widget>; fn view(self) -> WidgetHandle<Self::Widget>;
} }
pub struct ViewTag; pub struct ViewTag;
@@ -11,7 +11,7 @@ pub struct ViewTag;
impl<WV: WidgetView> WidgetLike<ViewTag> for WV { impl<WV: WidgetView> WidgetLike<ViewTag> for WV {
type Widget = WV::Widget; type Widget = WV::Widget;
fn add(self, _ui: &mut super::Ui) -> WidgetRef<Self::Widget> { fn add(self, _ui: &mut super::Ui) -> WidgetHandle<Self::Widget> {
self.view().clone() self.view()
} }
} }

View File

@@ -1,62 +1,53 @@
use std::{ use std::{
cell::{Ref, RefMut},
marker::Unsize, marker::Unsize,
ops::CoerceUnsized, mem::MaybeUninit,
ops::{CoerceUnsized, Deref, DerefMut},
sync::mpsc::Sender, sync::mpsc::Sender,
}; };
use crate::{ use crate::{
layout::{IdFnTag, IdTag, Ui, Widget, WidgetLike}, layout::{IdFnTag, IdTag, Ui, WIDGETS, Widget, WidgetLike},
util::{Handle, Id, WeakHandle}, util::{Handle, Ref, RefMap, RefMapMut, RefMut, WeakHandle},
}; };
/// An handle for a widget in a UI. pub(super) type SlotTy = u32;
///
/// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs?
pub struct WidgetRef<W: ?Sized = dyn Widget>(Handle<Inner<W>>);
pub struct WeakWidgetRef<W: ?Sized = dyn Widget>(WeakHandle<Inner<W>>);
struct Inner<W: ?Sized> { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
id: Id, pub struct WidgetId {
send: Sender<WidgetUpdate>, pub(super) i: SlotTy,
label: String, pub(super) genr: SlotTy,
widget: W, }
/// A strong handle to a widget. Cannot be cloned, only one may exist
/// Use .weak() to obtain weak references to this
pub struct WidgetHandle<W: ?Sized = dyn Widget>(WidgetId, Handle<WidgetData<W>>);
/// A weak handle to a widget. Implements Copy so you can easily pass into closures
pub struct WidgetRef<W: ?Sized = dyn Widget>(WidgetId, *const W);
pub(crate) struct WidgetData<W: ?Sized> {
pub id: WidgetId,
pub send: Option<Sender<WidgetUpdate>>,
pub label: String,
pub widget: W,
} }
pub enum WidgetUpdate { pub enum WidgetUpdate {
Drop(Id), Drop(WidgetId),
Mutate(Id), Mutate(WidgetId),
} }
impl<W> PartialEq for WidgetRef<W> { impl<W: Widget> WidgetHandle<W> {
fn eq(&self, other: &Self) -> bool { pub(super) fn new(id: WidgetId, widget: W) -> Self {
self.id() == other.id()
}
}
impl<W> std::fmt::Debug for WidgetRef<W> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.id().fmt(f)
}
}
impl<W: ?Sized> Clone for WidgetRef<W> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<W: Widget> WidgetRef<W> {
pub(super) fn new(id: Id, widget: W, send: Sender<WidgetUpdate>) -> Self {
let mut label = std::any::type_name::<W>().to_string(); let mut label = std::any::type_name::<W>().to_string();
if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) { if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) {
label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1; label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1;
} }
Self( Self(
Inner {
widget,
id, id,
send, WidgetData {
id,
widget,
send: None,
label, label,
} }
.into(), .into(),
@@ -64,100 +55,225 @@ impl<W: Widget> WidgetRef<W> {
} }
} }
impl<W: Widget + ?Sized + Unsize<dyn Widget>> WidgetRef<W> { impl<W: Widget + ?Sized + Unsize<dyn Widget>> WidgetHandle<W> {
pub fn any(self) -> WidgetRef<dyn Widget> { pub(super) fn weak_inner(&self) -> WeakHandle<WidgetData<W>> {
WidgetRef(self.0) self.1.weak()
} }
pub fn as_any(&self) -> WidgetRef<dyn Widget> {
WidgetRef(self.0.clone()) pub fn any(self) -> WidgetHandle<dyn Widget> {
self
}
/// DO NOT CALL OTHER THAN TEMP IN PAINTER
pub(crate) fn as_any(&self) -> WidgetHandle<dyn Widget> {
WidgetHandle(self.0, self.1.clone())
} }
} }
impl<W: ?Sized> WidgetRef<W> { impl<W: ?Sized> WidgetHandle<W> {
pub fn id(&self) -> Id { pub fn id(&self) -> WidgetId {
self.0.get().id self.0
} }
pub fn get(&self) -> Ref<'_, W> { pub fn get(&self) -> RefMap<'_, W> {
Ref::map(self.0.get(), |i| &i.widget) Ref::map(self.1.get(), |i| &mut i.widget)
} }
pub fn get_mut(&self) -> RefMut<'_, W> { pub fn get_mut(&self) -> RefMapMut<'_, W> {
let inner = self.0.get_mut(); let inner = self.1.get_mut();
let _ = inner.send.send(WidgetUpdate::Mutate(inner.id)); if let Some(send) = &inner.send {
let _ = send.send(WidgetUpdate::Mutate(self.0));
}
RefMut::map(inner, |i| &mut i.widget) RefMut::map(inner, |i| &mut i.widget)
} }
pub fn get_mut_quiet(&self) -> RefMut<'_, W> { pub(crate) fn data_mut(&self) -> RefMut<'_, WidgetData<W>> {
RefMut::map(self.0.get_mut(), |i| &mut i.widget) self.1.get_mut()
} }
pub fn get_label(&self) -> Ref<'_, String> { pub fn get_mut_quiet(&self) -> RefMapMut<'_, W> {
Ref::map(self.0.get(), |i| &i.label) 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<String>) { pub fn set_label(&self, label: impl Into<String>) {
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 { pub fn refs(&self) -> usize {
self.0.refs() self.1.refs()
} }
pub fn weak(&self) -> WeakWidgetRef<W> { pub fn weak(&self) -> WidgetRef<W> {
WeakWidgetRef(self.0.weak()) WidgetRef(self.0, null_ptr())
} }
} }
impl<W: ?Sized> WeakWidgetRef<W> { impl<W: Widget> WidgetRef<W> {
/// should guarantee that widget is still valid to prevent indexing failures fn handle(&self) -> Handle<WidgetData<W>> {
pub(crate) fn expect_strong(&self) -> WidgetRef<W> { WIDGETS.get_type(self.0).unwrap()
WidgetRef(self.0.strong().expect("widget should not be dropped")) }
pub fn get<'a>(&self) -> WRef<'a, W> {
let handle = self.handle();
WRef {
val: unsafe {
std::mem::transmute::<RefMap<'_, W>, RefMap<'_, W>>(Ref::map(handle.get(), |i| {
&mut i.widget
}))
},
_handle: handle,
} }
} }
impl<W: Widget + ?Sized + Unsize<dyn Widget>> WeakWidgetRef<W> { pub fn get_mut<'a>(&self) -> WRefMut<'a, W> {
pub fn any(self) -> WeakWidgetRef<dyn Widget> { let handle = self.handle();
WeakWidgetRef(self.0.clone()) if let Some(send) = &handle.get().send {
let _ = send.send(WidgetUpdate::Mutate(self.0));
}
WRefMut {
val: unsafe {
std::mem::transmute::<RefMapMut<'_, W>, RefMapMut<'_, W>>(RefMut::map(
handle.get_mut(),
|i| &mut i.widget,
))
},
_handle: handle,
}
} }
} }
impl<W: ?Sized> Drop for Inner<W> { pub struct WRef<'a, W> {
_handle: Handle<WidgetData<W>>,
val: RefMap<'a, W>,
}
pub struct WRefMut<'a, W> {
_handle: Handle<WidgetData<W>>,
val: RefMapMut<'a, W>,
}
impl<W: Widget + ?Sized + Unsize<dyn Widget>> WidgetRef<W> {
pub fn any(self) -> WidgetRef<dyn Widget> {
WidgetRef(self.0, null_ptr())
}
}
fn null_ptr<T: ?Sized>() -> *const T {
unsafe { MaybeUninit::zeroed().assume_init() }
}
impl<W: ?Sized> WidgetRef<W> {
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<WidgetHandle> {
Some(WidgetHandle(self, WIDGETS.get(self)?))
}
}
impl<W: ?Sized> Drop for WidgetData<W> {
fn drop(&mut self) { 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<W: ?Sized = dyn Widget>: FnOnce(&mut Ui) -> WidgetRef<W> {} pub trait WidgetIdFn<W: ?Sized = dyn Widget>: FnOnce(&mut Ui) -> WidgetHandle<W> {}
impl<W: ?Sized, F: FnOnce(&mut Ui) -> WidgetRef<W>> WidgetIdFn<W> for F {} impl<W: ?Sized, F: FnOnce(&mut Ui) -> WidgetHandle<W>> WidgetIdFn<W> for F {}
pub trait WidgetRet: FnOnce(&mut Ui) -> WidgetRef {} pub trait WidgetRet: FnOnce(&mut Ui) -> WidgetHandle {}
impl<F: FnOnce(&mut Ui) -> WidgetRef> WidgetRet for F {} impl<F: FnOnce(&mut Ui) -> WidgetHandle> WidgetRet for F {}
impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static> WidgetLike<IdTag> for WidgetRef<W> { impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static> WidgetLike<IdTag> for WidgetHandle<W> {
type Widget = W; type Widget = W;
fn add(self, _: &mut Ui) -> WidgetRef<W> { fn add(self, _: &mut Ui) -> WidgetHandle<W> {
self self
} }
} }
impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static, F: FnOnce(&mut Ui) -> WidgetRef<W>> impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static, F: FnOnce(&mut Ui) -> WidgetHandle<W>>
WidgetLike<IdFnTag> for F WidgetLike<IdFnTag> for F
{ {
type Widget = W; type Widget = W;
fn add(self, ui: &mut Ui) -> WidgetRef<W> { fn add(self, ui: &mut Ui) -> WidgetHandle<W> {
self(ui) self(ui)
} }
} }
pub trait IdLike<W> { pub trait IdLike<W> {
fn id(&self) -> Id; fn id(&self) -> WidgetId;
} }
impl<W> IdLike<W> for WidgetRef<W> { impl<W> IdLike<W> for WidgetHandle<W> {
fn id(&self) -> Id { fn id(&self) -> WidgetId {
self.id() self.id()
} }
} }
impl<W: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<WidgetRef<U>> for WidgetRef<W> {} impl<W> IdLike<W> for WidgetRef<W> {
fn id(&self) -> WidgetId {
self.id()
}
}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<WidgetHandle<U>> for WidgetHandle<T> {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<WidgetRef<U>> for WidgetRef<T> {}
impl<W> PartialEq for WidgetHandle<W> {
fn eq(&self, other: &Self) -> bool {
self.id() == other.id()
}
}
impl<W> std::fmt::Debug for WidgetHandle<W> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.id().fmt(f)
}
}
impl<W: ?Sized> Clone for WidgetRef<W> {
fn clone(&self) -> Self {
*self
}
}
impl<W: ?Sized> Copy for WidgetRef<W> {}
impl<W: ?Sized> PartialEq for WidgetRef<W> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0 && self.1 == other.1
}
}
impl<T> Deref for WRef<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.val
}
}
impl<T> Deref for WRefMut<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.val
}
}
impl<T> DerefMut for WRefMut<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.val
}
}

View File

@@ -4,11 +4,11 @@ use std::marker::Unsize;
pub trait WidgetLike<Tag> { pub trait WidgetLike<Tag> {
type Widget: Widget + ?Sized + Unsize<dyn Widget> + 'static; type Widget: Widget + ?Sized + Unsize<dyn Widget> + 'static;
fn add(self, ui: &mut Ui) -> WidgetRef<Self::Widget>; fn add(self, ui: &mut Ui) -> WidgetHandle<Self::Widget>;
fn with_id<W2>( fn with_id<W2>(
self, self,
f: impl FnOnce(&mut Ui, WidgetRef<Self::Widget>) -> WidgetRef<W2>, f: impl FnOnce(&mut Ui, WidgetHandle<Self::Widget>) -> WidgetHandle<W2>,
) -> impl WidgetIdFn<W2> ) -> impl WidgetIdFn<W2>
where where
Self: Sized, Self: Sized,
@@ -28,11 +28,11 @@ pub trait WidgetLike<Tag> {
} }
pub struct WidgetArr<const LEN: usize> { pub struct WidgetArr<const LEN: usize> {
pub arr: [WidgetRef; LEN], pub arr: [WidgetHandle; LEN],
} }
impl<const LEN: usize> WidgetArr<LEN> { impl<const LEN: usize> WidgetArr<LEN> {
pub fn new(arr: [WidgetRef; LEN]) -> Self { pub fn new(arr: [WidgetHandle; LEN]) -> Self {
Self { arr } Self { arr }
} }
} }

View File

@@ -10,7 +10,7 @@ pub use widgets::*;
use crate::layout::{Len, Painter, SizeCtx, Ui}; 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 draw(&mut self, painter: &mut Painter);
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len; fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len;
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len; fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len;
@@ -34,30 +34,30 @@ impl<W: Widget, F: FnOnce(&mut Ui) -> W> WidgetFn<W> for F {}
impl<W: Widget, F: FnOnce(&mut Ui) -> W> WidgetLike<FnTag> for F { impl<W: Widget, F: FnOnce(&mut Ui) -> W> WidgetLike<FnTag> for F {
type Widget = W; type Widget = W;
fn add(self, ui: &mut Ui) -> WidgetRef<W> { fn add(self, ui: &mut Ui) -> WidgetHandle<W> {
self(ui).add(ui) self(ui).add(ui)
} }
} }
impl<W: Widget> WidgetLike<WidgetTag> for W { impl<W: Widget> WidgetLike<WidgetTag> for W {
type Widget = W; type Widget = W;
fn add(self, ui: &mut Ui) -> WidgetRef<W> { fn add(self, _: &mut Ui) -> WidgetHandle<W> {
ui.add_widget(self) WIDGETS.insert(self)
} }
} }
pub trait WidgetOption { pub trait WidgetOption {
fn get(self, ui: &mut Ui) -> Option<WidgetRef>; fn get(self, ui: &mut Ui) -> Option<WidgetHandle>;
} }
impl WidgetOption for () { impl WidgetOption for () {
fn get(self, _: &mut Ui) -> Option<WidgetRef> { fn get(self, _: &mut Ui) -> Option<WidgetHandle> {
None None
} }
} }
impl<F: FnOnce(&mut Ui) -> Option<WidgetRef>> WidgetOption for F { impl<F: FnOnce(&mut Ui) -> Option<WidgetHandle>> WidgetOption for F {
fn get(self, ui: &mut Ui) -> Option<WidgetRef> { fn get(self, ui: &mut Ui) -> Option<WidgetHandle> {
self(ui) self(ui)
} }
} }

View File

@@ -1,50 +1,108 @@
use std::sync::mpsc::Sender; use std::{
any::Any,
use crate::{ sync::{Mutex, MutexGuard},
layout::{WeakWidgetRef, Widget, WidgetRef, WidgetUpdate},
util::{HashMap, Id, IdTracker},
}; };
pub struct Widgets { use crate::{
ids: IdTracker, layout::{
map: HashMap<Id, WeakWidgetRef>, Widget, WidgetData, WidgetHandle,
send: Sender<WidgetUpdate>, widget::{SlotTy, WidgetId},
},
util::{Handle, WeakHandle},
};
pub(crate) static WIDGETS: Widgets = Widgets::new();
pub(crate) struct Widgets(Mutex<Inner>);
pub fn total_widgets() -> usize {
WIDGETS.len()
}
pub struct WidgetSlot {
genr: SlotTy,
data: WeakHandle<WidgetData<dyn Widget + Sync + Send>>,
}
struct Inner {
cur_id: SlotTy,
vec: Vec<WidgetSlot>,
free: Vec<SlotTy>,
} }
impl Widgets { impl Widgets {
pub fn new(send: Sender<WidgetUpdate>) -> Self { pub const fn new() -> Self {
Self { Self(Mutex::new(Inner {
ids: IdTracker::default(), cur_id: 0,
map: HashMap::default(), vec: Vec::new(),
send, free: Vec::new(),
}))
}
fn expect(&self) -> MutexGuard<'_, Inner> {
self.0.lock().unwrap()
}
pub fn get_type<W: 'static>(&self, id: WidgetId) -> Option<Handle<WidgetData<W>>> {
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 { pub fn get(&self, id: WidgetId) -> Option<Handle<WidgetData<dyn Widget>>> {
self.map.get(&id).unwrap().expect_strong() let slot = &self.expect().vec[id.i as usize];
if slot.genr != id.genr {
None
} else {
Some(slot.data.clone().strong()?)
}
} }
pub fn insert<W: Widget>(&mut self, widget: W) -> WidgetRef<W> { pub fn insert<W: Widget>(&self, widget: W) -> WidgetHandle<W> {
let id = self.ids.next(); let mut s = self.expect();
let rf = WidgetRef::new(id, widget, self.send.clone()); let id = s
self.map.insert(id, rf.weak().any()); .free
rf .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) { pub fn remove(&self, id: WidgetId) {
self.map.remove(&id); let mut s = self.expect();
self.ids.free(id); s.vec[id.i as usize].genr += 1;
} s.free.push(id.i);
pub fn reserve(&mut self) -> Id {
self.ids.next()
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.map.len() let s = self.expect();
} s.vec.len() - s.free.len()
pub fn is_empty(&self) -> bool {
self.map.is_empty()
} }
} }

View File

@@ -11,6 +11,8 @@
#![feature(associated_type_defaults)] #![feature(associated_type_defaults)]
#![feature(unsize)] #![feature(unsize)]
#![feature(coerce_unsized)] #![feature(coerce_unsized)]
#![feature(mapped_lock_guards)]
#![feature(ptr_metadata)]
pub mod layout; pub mod layout;
pub mod render; pub mod render;

View File

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

View File

@@ -1,28 +1,34 @@
use std::{ use std::{
cell::{Ref, RefCell, RefMut}, any::{Any, TypeId},
marker::Unsize, marker::Unsize,
ops::CoerceUnsized, ops::CoerceUnsized,
rc::{Rc, Weak}, sync::{Arc, Mutex, Weak},
}; };
pub struct Handle<T: ?Sized>(Rc<RefCell<T>>); pub struct Handle<T: ?Sized>(Arc<Mutex<T>>);
pub struct WeakHandle<T: ?Sized>(Weak<RefCell<T>>); pub struct WeakHandle<T: ?Sized>(Weak<Mutex<T>>);
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<T: ?Sized> Handle<T> { impl<T: ?Sized> Handle<T> {
pub fn get(&self) -> Ref<'_, T> { pub fn get(&self) -> Ref<'_, T> {
self.0.borrow() self.0.lock().unwrap()
} }
pub fn get_mut(&self) -> RefMut<'_, T> { pub fn get_mut(&self) -> RefMut<'_, T> {
self.0.borrow_mut() self.0.lock().unwrap()
} }
pub fn refs(&self) -> usize { pub fn refs(&self) -> usize {
Rc::strong_count(&self.0) Arc::strong_count(&self.0)
} }
pub fn weak(&self) -> WeakHandle<T> { pub fn weak(&self) -> WeakHandle<T> {
WeakHandle(Rc::downgrade(&self.0)) WeakHandle(Arc::downgrade(&self.0))
} }
} }
@@ -30,6 +36,21 @@ impl<T: ?Sized> WeakHandle<T> {
pub fn strong(&self) -> Option<Handle<T>> { pub fn strong(&self) -> Option<Handle<T>> {
Some(Handle(self.0.upgrade()?)) 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<U: 'static>(self) -> Option<WeakHandle<U>>
where
T: 'static,
{
let raw: *const Mutex<T> = self.0.into_raw();
let raw: *const Mutex<U> = raw.cast();
Some(WeakHandle(unsafe { Weak::from_raw(raw) }))
}
} }
impl<T: ?Sized> Clone for Handle<T> { impl<T: ?Sized> Clone for Handle<T> {
@@ -52,7 +73,7 @@ impl<T: Default> Default for Handle<T> {
impl<T> From<T> for Handle<T> { impl<T> From<T> for Handle<T> {
fn from(value: T) -> Self { fn from(value: T) -> Self {
Self(Rc::new(RefCell::new(value))) Self(Arc::new(Mutex::new(value)))
} }
} }

View File

@@ -51,7 +51,7 @@ impl DefaultAppState for Client {
.add(ui); .add(ui);
let span_add = Span::empty(Dir::RIGHT).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) let add_button = rect(Color::LIME)
.radius(30) .radius(30)
@@ -64,7 +64,6 @@ impl DefaultAppState for Client {
.sized((150, 150)) .sized((150, 150))
.align(Align::BOT_RIGHT); .align(Align::BOT_RIGHT);
let span_add_ = span_add.clone();
let del_button = rect(Color::RED) let del_button = rect(Color::RED)
.radius(30) .radius(30)
.on(CursorSense::click(), move |_| { .on(CursorSense::click(), move |_| {
@@ -99,7 +98,8 @@ impl DefaultAppState for Client {
.add(ui); .add(ui);
let texts = Span::empty(Dir::DOWN).gap(10).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") let add_text = wtext("add")
.editable(false) .editable(false)
.text_align(Align::LEFT) .text_align(Align::LEFT)
@@ -114,18 +114,19 @@ impl DefaultAppState for Client {
.wrap(true) .wrap(true)
.attr::<Selectable>(()); .attr::<Selectable>(());
let msg_box = text.background(rect(Color::WHITE.darker(0.5))).add(ctx.ui); 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); .add(ui);
let add_text_ = add_text.weak();
let text_edit_scroll = ( let text_edit_scroll = (
msg_area.height(rest(1)), msg_area.height(rest(1)),
( (
Rect::new(Color::WHITE.darker(0.9)), Rect::new(Color::WHITE.darker(0.9)),
( (
add_text.clone().width(rest(1)), add_text.width(rest(1)),
Rect::new(Color::GREEN) Rect::new(Color::GREEN)
.on(CursorSense::click(), move |ctx| { .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)), .sized((40, 40)),
) )
@@ -140,13 +141,24 @@ impl DefaultAppState for Client {
.span(Dir::DOWN) .span(Dir::DOWN)
.add(ui); .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 tab_handles = Handle::from((0, Vec::new()));
let main_ = main.clone();
let switch_button = |color, to: Option<WidgetHandle>, 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) let rect = rect(color)
.on(CursorSense::click(), move |ctx| { .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); ctx.widget.get_mut().color = color.darker(0.3);
}) })
.on( .on(
@@ -162,20 +174,21 @@ impl DefaultAppState for Client {
}; };
let tabs = ( let tabs = (
switch_button(Color::RED, pad_test, "pad"), switch_button(Color::RED, None, "pad"),
switch_button(Color::GREEN, span_test, "span"), switch_button(Color::GREEN, Some(span_test), "span"),
switch_button(Color::BLUE, span_add_test, "image span"), switch_button(Color::BLUE, Some(span_add_test), "image span"),
switch_button(Color::MAGENTA, text_test, "text layout"), switch_button(Color::MAGENTA, Some(text_test), "text layout"),
switch_button( switch_button(
Color::YELLOW.mul_rgb(0.5), Color::YELLOW.mul_rgb(0.5),
text_edit_scroll, Some(text_edit_scroll),
"text edit scroll", "text edit scroll",
), ),
) )
.span(Dir::RIGHT); .span(Dir::RIGHT);
let info = wtext("").add(ui); let info_ = wtext("").add(ui);
let info_sect = info.clone().pad(10).align(Align::RIGHT); let info = info_.weak();
let info_sect = info_.pad(10).align(Align::RIGHT);
((tabs.height(40), main).span(Dir::DOWN), info_sect) ((tabs.height(40), main).span(Dir::DOWN), info_sect)
.stack() .stack()
@@ -187,7 +200,7 @@ impl DefaultAppState for Client {
fn window_event(&mut self, _: WindowEvent, ui: &mut Ui, state: &UiState) { fn window_event(&mut self, _: WindowEvent, ui: &mut Ui, state: &UiState) {
let new = format!( let new = format!(
"widgets: {}\nactive:{}\nviews: {}", "widgets: {}\nactive:{}\nviews: {}",
ui.num_widgets(), total_widgets(),
ui.active_widgets(), ui.active_widgets(),
state.renderer.ui.view_count() state.renderer.ui.view_count()
); );

View File

@@ -1,4 +1,4 @@
use crate::{prelude::*, default::UiState}; use crate::{default::UiState, prelude::*};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use winit::dpi::{LogicalPosition, LogicalSize}; use winit::dpi::{LogicalPosition, LogicalSize};
@@ -7,21 +7,16 @@ pub struct Selector;
impl<W: 'static> WidgetAttr<W> for Selector { impl<W: 'static> WidgetAttr<W> for Selector {
type Input = WidgetRef<TextEdit>; type Input = WidgetRef<TextEdit>;
fn run(ui: &mut Ui, container: &WidgetRef<W>, id: Self::Input) { fn run(ui: &mut Ui, container: WidgetRef<W>, id: Self::Input) {
let container = container.clone(); ui.register_event(container, CursorSense::click_or_drag(), move |mut ctx| {
ui.register_event(
&container.clone(),
CursorSense::click_or_drag(),
move |mut ctx| {
let ui = ctx.ui; let ui = ctx.ui;
let region = ui.window_region(&id).unwrap(); let region = ui.window_region(&id).unwrap();
let id_pos = region.top_left; let id_pos = region.top_left;
let container_pos = ui.window_region(&container).unwrap().top_left; let container_pos = ui.window_region(&container).unwrap().top_left;
ctx.data.cursor += container_pos - id_pos; ctx.data.cursor += container_pos - id_pos;
ctx.data.size = region.size(); ctx.data.size = region.size();
select(ui, id.clone(), ctx.state, ctx.data); select(ui, id, ctx.state, ctx.data);
}, });
);
} }
} }
@@ -30,10 +25,9 @@ pub struct Selectable;
impl WidgetAttr<TextEdit> for Selectable { impl WidgetAttr<TextEdit> for Selectable {
type Input = (); type Input = ();
fn run(ui: &mut Ui, id: &WidgetRef<TextEdit>, _: Self::Input) { fn run(ui: &mut Ui, id: WidgetRef<TextEdit>, _: Self::Input) {
let id = id.clone(); ui.register_event(id, CursorSense::click_or_drag(), move |ctx| {
ui.register_event(&id.clone(), CursorSense::click_or_drag(), move |ctx| { select(ctx.ui, id, ctx.state, ctx.data);
select(ctx.ui, id.clone(), ctx.state, ctx.data);
}); });
} }
} }

View File

@@ -120,10 +120,9 @@ impl<State: DefaultAppState> AppState for DefaultState<State> {
ui_state.renderer.resize(size) ui_state.renderer.resize(size)
} }
WindowEvent::KeyboardInput { event, .. } => { WindowEvent::KeyboardInput { event, .. } => {
if let Some(sel) = &ui_state.focus if let Some(sel) = ui_state.focus
&& event.state.is_pressed() && event.state.is_pressed()
{ {
let sel = &sel.clone();
let res = sel.get_mut().apply_event(event, &ui_state.input.modifiers); let res = sel.get_mut().apply_event(event, &ui_state.input.modifiers);
match res { match res {
TextInputResult::Unfocus => { TextInputResult::Unfocus => {

View File

@@ -13,7 +13,7 @@ pub mod eventable {
) -> impl WidgetIdFn<WL::Widget> { ) -> impl WidgetIdFn<WL::Widget> {
move |ui| { move |ui| {
let id = self.add(ui); let id = self.add(ui);
ui.register_widget_event(&id, event, f); ui.register_widget_event(id.weak(), event, f);
id id
} }
} }
@@ -56,7 +56,7 @@ macro_rules! event_ctx {
{ {
fn on<E: Event>( fn on<E: Event>(
&mut self, &mut self,
widget: &WidgetRef<W>, widget: WidgetRef<W>,
event: E, event: E,
f: impl WidgetEventFn<Ctx, E::Data, W>, f: impl WidgetEventFn<Ctx, E::Data, W>,
); );
@@ -68,11 +68,11 @@ macro_rules! event_ctx {
{ {
fn on<E: Event>( fn on<E: Event>(
&mut self, &mut self,
widget: &WidgetRef<W>, widget: WidgetRef<W>,
event: E, event: E,
f: impl WidgetEventFn<$ty, E::Data, W>, f: impl WidgetEventFn<$ty, E::Data, W>,
) { ) {
self.register_widget_event(&widget, event, f); self.register_widget_event(widget, event, f);
} }
} }
} }

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct Masked { pub struct Masked {
pub inner: WidgetRef, pub inner: WidgetHandle,
} }
impl Widget for Masked { impl Widget for Masked {

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct Aligned { pub struct Aligned {
pub inner: WidgetRef, pub inner: WidgetHandle,
pub align: Align, pub align: Align,
} }

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct LayerOffset { pub struct LayerOffset {
pub inner: WidgetRef, pub inner: WidgetHandle,
pub offset: usize, pub offset: usize,
} }

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct MaxSize { pub struct MaxSize {
pub inner: WidgetRef, pub inner: WidgetHandle,
pub x: Option<Len>, pub x: Option<Len>,
pub y: Option<Len>, pub y: Option<Len>,
} }

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct Offset { pub struct Offset {
pub inner: WidgetRef, pub inner: WidgetHandle,
pub amt: UiVec2, pub amt: UiVec2,
} }

View File

@@ -2,7 +2,7 @@ use crate::prelude::*;
pub struct Pad { pub struct Pad {
pub padding: Padding, pub padding: Padding,
pub inner: WidgetRef, pub inner: WidgetHandle,
} }
impl Widget for Pad { impl Widget for Pad {

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct Scroll { pub struct Scroll {
inner: WidgetRef, inner: WidgetHandle,
axis: Axis, axis: Axis,
amt: f32, amt: f32,
snap_end: bool, snap_end: bool,
@@ -41,7 +41,7 @@ impl Widget for Scroll {
} }
impl Scroll { impl Scroll {
pub fn new(inner: WidgetRef, axis: Axis) -> Self { pub fn new(inner: WidgetHandle, axis: Axis) -> Self {
Self { Self {
inner, inner,
axis, axis,

View File

@@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct Sized { pub struct Sized {
pub inner: WidgetRef, pub inner: WidgetHandle,
pub x: Option<Len>, pub x: Option<Len>,
pub y: Option<Len>, pub y: Option<Len>,
} }

View File

@@ -2,7 +2,7 @@ use crate::prelude::*;
use std::marker::PhantomData; use std::marker::PhantomData;
pub struct Span { pub struct Span {
pub children: Vec<WidgetRef>, pub children: Vec<WidgetHandle>,
pub dir: Dir, pub dir: Dir,
pub gap: f32, pub gap: f32,
} }
@@ -158,7 +158,7 @@ impl<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> FnOnce<(&mut Ui,)>
extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output {
Span { Span {
children: self.children.ui(args.0).arr.to_vec(), children: self.children.ui(args.0).arr.into_iter().collect(),
dir: self.dir, dir: self.dir,
gap: self.gap, gap: self.gap,
} }
@@ -182,7 +182,7 @@ impl<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> SpanBuilder<LEN, Wa, Ta
} }
impl std::ops::Deref for Span { impl std::ops::Deref for Span {
type Target = Vec<WidgetRef>; type Target = Vec<WidgetHandle>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.children &self.children

View File

@@ -3,7 +3,7 @@ use std::marker::PhantomData;
use crate::prelude::*; use crate::prelude::*;
pub struct Stack { pub struct Stack {
pub children: Vec<WidgetRef>, pub children: Vec<WidgetHandle>,
pub size: StackSize, pub size: StackSize,
} }
@@ -55,7 +55,7 @@ impl<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> FnOnce<(&mut Ui,)>
extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output {
Stack { Stack {
children: self.children.ui(args.0).arr.to_vec(), children: self.children.ui(args.0).arr.into_iter().collect(),
size: self.size, size: self.size,
} }
} }

View File

@@ -2,7 +2,7 @@ use crate::prelude::*;
#[derive(Default)] #[derive(Default)]
pub struct WidgetPtr { pub struct WidgetPtr {
pub inner: Option<WidgetRef>, pub inner: Option<WidgetHandle>,
} }
impl Widget for WidgetPtr { impl Widget for WidgetPtr {

View File

@@ -1,15 +1,12 @@
use crate::prelude::*; use crate::prelude::*;
use crate::util::Id;
use std::{
ops::{BitOr, Deref, DerefMut},
rc::Rc,
};
use crate::{ use crate::{
layout::{UiModule, UiRegion, Vec2}, layout::{UiModule, UiRegion, Vec2},
util::HashMap, util::HashMap,
}; };
use std::{
ops::{BitOr, Deref, DerefMut},
rc::Rc,
};
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub enum CursorButton { pub enum CursorButton {
@@ -100,10 +97,10 @@ pub enum ActivationState {
/// but I'm not sure how or if worth it /// but I'm not sure how or if worth it
pub struct Sensor<Ctx, Data> { pub struct Sensor<Ctx, Data> {
pub senses: CursorSenses, pub senses: CursorSenses,
pub f: Rc<dyn EventFn<Ctx, Data>>, pub f: Box<dyn EventFn<Ctx, Data>>,
} }
pub type SensorMap<Ctx, Data> = HashMap<Id, SensorGroup<Ctx, Data>>; pub type SensorMap<Ctx, Data> = HashMap<WidgetId, SensorGroup<Ctx, Data>>;
pub type SenseShape = UiRegion; pub type SenseShape = UiRegion;
pub struct SensorGroup<Ctx, Data> { pub struct SensorGroup<Ctx, Data> {
pub hover: ActivationState, pub hover: ActivationState,
@@ -122,7 +119,7 @@ pub struct CursorData {
pub struct CursorModule<Ctx> { pub struct CursorModule<Ctx> {
map: SensorMap<Ctx, CursorData>, map: SensorMap<Ctx, CursorData>,
active: HashMap<usize, HashMap<Id, SenseShape>>, active: HashMap<usize, HashMap<WidgetId, SenseShape>>,
} }
impl<Ctx: 'static> UiModule for CursorModule<Ctx> { impl<Ctx: 'static> UiModule for CursorModule<Ctx> {
@@ -141,7 +138,7 @@ impl<Ctx: 'static> UiModule for CursorModule<Ctx> {
} }
} }
fn on_remove(&mut self, id: &Id) { fn on_remove(&mut self, id: &WidgetId) {
self.map.remove(id); self.map.remove(id);
for layer in self.active.values_mut() { for layer in self.active.values_mut() {
layer.remove(id); layer.remove(id);
@@ -307,34 +304,34 @@ impl Event for CursorSense {
impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: 'static> impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: 'static>
EventModule<E, Ctx> for CursorModule<Ctx> EventModule<E, Ctx> for CursorModule<Ctx>
{ {
fn register(&mut self, id: Id, senses: E, f: impl EventFn<Ctx, <E as Event>::Data>) { fn register(&mut self, id: WidgetId, senses: E, f: impl EventFn<Ctx, <E as Event>::Data>) {
// TODO: does not add to active if currently active // TODO: does not add to active if currently active
self.map.entry(id).or_default().sensors.push(Sensor { self.map.entry(id).or_default().sensors.push(Sensor {
senses: senses.into(), senses: senses.into(),
f: Rc::new(f), f: Box::new(f),
}); });
} }
fn run<'a>( fn run(
&self, &mut self,
id: &Id, id: WidgetId,
event: E, event: E,
) -> Option<impl Fn(EventCtx<Ctx, <E as Event>::Data>) + use<'a, E, Ctx>> { ) -> Option<impl FnOnce(EventCtx<Ctx, <E as Event>::Data>)> {
let senses = event.into(); 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 let fs: Vec<_> = group
.sensors .sensors
.iter() .iter_mut()
.filter_map(|sensor| { .filter_map(|sensor| {
if sensor.senses.iter().any(|s| senses.contains(s)) { if sensor.senses.iter().any(|s| senses.contains(s)) {
Some(sensor.f.clone()) Some(&mut sensor.f)
} else { } else {
None None
} }
}) })
.collect(); .collect();
Some(move |ctx: EventCtx<Ctx, CursorData>| { Some(move |ctx: EventCtx<Ctx, CursorData>| {
for f in &fs { for f in fs {
f(EventCtx { f(EventCtx {
state: ctx.state, state: ctx.state,
ui: ctx.ui, ui: ctx.ui,

View File

@@ -21,11 +21,11 @@ pub struct TextView {
// cache // cache
tex: Option<RenderedText>, tex: Option<RenderedText>,
width: Option<f32>, width: Option<f32>,
pub hint: Option<WidgetRef>, pub hint: Option<WidgetHandle>,
} }
impl TextView { impl TextView {
pub fn new(buf: TextBuffer, attrs: TextAttrs, hint: Option<WidgetRef>) -> Self { pub fn new(buf: TextBuffer, attrs: TextAttrs, hint: Option<WidgetHandle>) -> Self {
Self { Self {
attrs: attrs.into(), attrs: attrs.into(),
buf: buf.into(), buf: buf.into(),

View File

@@ -127,7 +127,7 @@ widget_trait! {
|ui| self.add(ui) |ui| self.add(ui)
} }
fn set_ptr(self, ptr: &WidgetRef<WidgetPtr>, ui: &mut Ui) { fn set_ptr(self, ptr: &WidgetHandle<WidgetPtr>, ui: &mut Ui) {
ptr.get_mut().inner = Some(self.add(ui)); ptr.get_mut().inner = Some(self.add(ui));
} }
} }