6 Commits

Author SHA1 Message Date
123f3e6e2b stuffff 2025-12-11 01:34:14 -05:00
baaeb6b027 FINALLY transition events to global; slow text sending bug tho 2025-12-11 01:21:36 -05:00
2537284372 "nvmnd" (event FnMut) 2025-12-10 13:56:38 -05:00
76f75192d5 fix event stuff 2025-12-10 13:29:36 -05:00
6156c66a20 initial global widget store 2025-12-10 01:42:39 -05:00
7f4846a2d3 change macro a bit 2025-12-09 02:03:54 -05:00
42 changed files with 1135 additions and 839 deletions

View File

@@ -10,4 +10,3 @@ bytemuck ={ workspace = true }
image = { workspace = true } image = { workspace = true }
cosmic-text = { workspace = true } cosmic-text = { workspace = true }
fxhash = { workspace = true } fxhash = { workspace = true }

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,187 +1,209 @@
use std::{hash::Hash, rc::Rc};
use crate::{ use crate::{
layout::{IdFnTag, Ui, UiModule, WidgetIdFn, WidgetLike, WidgetRef}, layout::{LayerId, UiId, Widget, WidgetId, WidgetInstance, WidgetRef},
util::{HashMap, Id}, util::{Handle, HashMap, RefGuardMut},
};
use std::{
any::{Any, TypeId},
ops::DerefMut,
sync::{Arc, LazyLock, Mutex},
}; };
pub trait Event: Sized { pub trait Async: Send + Sync + 'static {}
type Module<Ctx: 'static>: EventModule<Self, Ctx>; impl<T: Send + Sync + 'static> Async for T {}
type Data: Clone;
}
pub struct EventCtx<'a, Ctx, Data> { pub trait Event: Sized + Async + Clone {
pub ui: &'a mut Ui, type Data<'a> = ();
pub state: &'a mut Ctx, type State: Sync + Send + Default = ();
pub data: Data, #[allow(unused_variables)]
} fn should_run(&self, data: &mut Self::Data<'_>) -> bool {
true
pub type ECtx<'a, Ctx, Data, W> = EventIdCtx<'a, Ctx, Data, W>;
pub struct EventIdCtx<'a, Ctx, Data, W: ?Sized> {
pub widget: &'a WidgetRef<W>,
pub ui: &'a mut Ui,
pub state: &'a mut Ctx,
pub data: Data,
}
pub trait EventFn<Ctx, Data>: Fn(EventCtx<Ctx, Data>) + 'static {}
impl<F: Fn(EventCtx<Ctx, Data>) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {}
pub trait WidgetEventFn<Ctx, Data, W: ?Sized>: Fn(EventIdCtx<Ctx, Data, W>) + 'static {}
impl<F: Fn(EventIdCtx<Ctx, Data, W>) + 'static, Ctx, Data, W: ?Sized> WidgetEventFn<Ctx, Data, W>
for F
{
}
impl Ui {
pub fn register_event<W: ?Sized, E: Event, Ctx: 'static>(
&mut self,
id: &WidgetRef<W>,
event: E,
f: impl EventFn<Ctx, E::Data>,
) {
self.data
.modules
.get_mut::<E::Module<Ctx>>()
.register(id.id(), event, f);
} }
pub fn register_widget_event<W: ?Sized + 'static, E: Event, Ctx: 'static>( /// THIS SHOULD NOT BE OVERWRITTEN; the default impl runs this event
&mut self, fn run<Ctx: 'static, W>(id: WidgetRef<W>, data: &mut Self::Data<'_>, state: &mut Ctx) {
id: &WidgetRef<W>, Events::<Self, Ctx>::run(id.id(), data, state);
event: E,
f: impl WidgetEventFn<Ctx, E::Data, W>,
) {
let id_ = id.weak();
self.data
.modules
.get_mut::<E::Module<Ctx>>()
.register(id.id(), event, move |ctx| {
f(EventIdCtx {
widget: &id_.expect_strong(),
ui: ctx.ui,
state: ctx.state,
data: ctx.data,
})
});
} }
} }
pub trait DefaultEvent: Hash + Eq + 'static { pub trait EventAlias {
type Data: Clone; type Event: Event;
fn into_event(self) -> Self::Event;
} }
impl<E: DefaultEvent> Event for E { impl<E: Event> EventAlias for E {
type Module<Ctx: 'static> = DefaultEventModule<E, Ctx>; type Event = Self;
type Data = E::Data;
fn into_event(self) -> Self::Event {
self
}
} }
pub trait EventModule<E: Event, Ctx>: UiModule + Default { type EventData<E, Ctx> = (E, Arc<dyn for<'a> EventFn<Ctx, <E as Event>::Data<'a>>>);
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, E::Data>); pub struct Events<E: Event, Ctx> {
fn run<'a>( active: HashMap<UiId, HashMap<LayerId, HashMap<WidgetId, E::State>>>,
&self, map: HashMap<WidgetId, Vec<EventData<E, Ctx>>>,
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>>>>; pub static EVENT_TYPES: LazyLock<Mutex<HashMap<TypeId, Handle<dyn EventManager>>>> =
pub struct DefaultEventModule<E: Event, Ctx> { LazyLock::new(|| Mutex::new(HashMap::default()));
map: HashMap<E, EventFnMap<Ctx, <E as Event>::Data>>,
pub trait EventManager: Any + Send + Sync {
fn remove(&mut self, id: WidgetId) -> Box<dyn Any>;
fn draw(&mut self, ui: UiId, inst: &WidgetInstance);
fn undraw(&mut self, ui: UiId, inst: &WidgetInstance);
} }
impl<E: Event + 'static, Ctx: 'static> UiModule for DefaultEventModule<E, Ctx> { impl Handle<dyn EventManager> {
fn on_remove(&mut self, id: &Id) { pub fn remove(&mut self, id: WidgetId) {
for map in self.map.values_mut() { let mut s = self.get_mut();
map.remove(id); // refer to s.remove documentation for why drop order
let contents = s.remove(id);
drop(s);
drop(contents);
}
pub fn draw(&mut self, ui: UiId, inst: &WidgetInstance) {
self.get_mut().draw(ui, inst);
}
pub fn undraw(&mut self, ui: UiId, inst: &WidgetInstance) {
self.get_mut().undraw(ui, inst);
}
}
impl<E: Event, Ctx: 'static> EventManager for Events<E, Ctx> {
/// so... there is a deadlock if this is called in WidgetData::drop
/// and some function in here owns another WidgetId
/// so for now you need to drop the lock on self, THEN drop the contents
/// which is implemented for Handle<dyn EventManager>
fn remove(&mut self, id: WidgetId) -> Box<dyn Any> {
let contents = self.map.remove(&id);
for ui in self.active.values_mut() {
for layer in ui.values_mut() {
layer.remove(&id);
}
}
Box::new(contents)
}
fn draw(&mut self, ui: UiId, inst: &WidgetInstance) {
self.active
.entry(ui)
.or_default()
.entry(inst.layer)
.or_default()
.entry(inst.id)
.or_default();
}
fn undraw(&mut self, ui: UiId, inst: &WidgetInstance) {
// rust pls
(|| {
self.active
.get_mut(&ui)?
.get_mut(&inst.layer)?
.remove(&inst.id);
Some(())
})();
}
}
pub type UiActive<State> = HashMap<LayerId, HashMap<WidgetId, State>>;
impl<E: Event, Ctx: 'static> Events<E, Ctx> {
fn id() -> TypeId {
TypeId::of::<Self>()
}
fn new() -> Self {
Self {
map: Default::default(),
active: Default::default(),
} }
} }
}
pub trait HashableEvent: Event + Hash + Eq + 'static {} pub fn active(id: UiId) -> impl DerefMut<Target = UiActive<E::State>> {
impl<E: Event + Hash + Eq + 'static> HashableEvent for E {} RefGuardMut::map(Self::get(), |s| s.active.entry(id).or_default())
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>) {
self.map
.entry(event)
.or_default()
.entry(id)
.or_default()
.push(Rc::new(f));
} }
fn run<'a>( fn register_<W: Widget + ?Sized>(
&self, id: WidgetRef<W>,
id: &Id, event: impl Into<E>,
event: E, f: impl for<'a> EventFn<Ctx, <E as Event>::Data<'a>>,
) -> Option<impl Fn(EventCtx<Ctx, E::Data>) + use<'a, E, Ctx>> { ) {
if let Some(map) = self.map.get(&event) let Some(mut data) = id.data_mut() else {
&& let Some(fs) = map.get(id) return;
{ };
let fs = fs.clone(); data.event_managers.insert(Self::id());
Some(move |ctx: EventCtx<Ctx, <E as Event>::Data>| { Self::get()
for f in &fs { .map
f(EventCtx { .entry(id.id())
ui: ctx.ui, .or_default()
state: ctx.state, .push((event.into(), Arc::new(f)));
data: ctx.data.clone(), }
})
} pub fn register<W: Widget + ?Sized + 'static>(
id: WidgetRef<W>,
event: impl Into<E>,
f: impl for<'a> EventIdFn<Ctx, <E as Event>::Data<'a>, W>,
) {
Self::register_(id, event, move |ctx| {
f(EventIdCtx {
widget: id,
state: ctx.state,
data: ctx.data,
}) })
} else { });
None
}
} }
}
impl<E: HashableEvent, Ctx: 'static> DefaultEventModule<E, Ctx> { pub fn run(id: WidgetId, data: &mut E::Data<'_>, state: &mut Ctx) {
pub fn run_all(&self, event: E, ctx: EventCtx<Ctx, E::Data>) let s = Self::get();
where if let Some(fs) = s.map.get(&id) {
E::Data: Clone, let fs = fs.clone();
{ drop(s);
if let Some(map) = self.map.get(&event) { for (e, f) in fs {
for fs in map.values() { if e.should_run(data) {
for f in fs { f(EventCtx { state, data })
f(EventCtx {
ui: ctx.ui,
state: ctx.state,
data: ctx.data.clone(),
})
} }
} }
} }
} }
}
impl<E: Event + 'static, Ctx: 'static> Default for DefaultEventModule<E, Ctx> { pub fn run_all(data: &mut E::Data<'_>, state: &mut Ctx) {
fn default() -> Self { let s = Self::get();
Self { // TODO: should probably document that added events won't run same frame
map: Default::default(), let mut map = s.map.clone();
drop(s);
for fs in map.values_mut() {
for (e, f) in fs {
if e.should_run(data) {
f(EventCtx { state, data })
}
}
} }
} }
/// this some bull
fn get<'a>() -> RefGuardMut<'a, Self> {
let any = EVENT_TYPES
.lock()
.unwrap()
.entry(Self::id())
.or_insert_with(|| Handle::from(Self::new()))
.clone();
unsafe { any.downcast() }.get_take_mut()
}
} }
impl Ui { pub struct EventCtx<'a, Ctx, Data> {
pub fn run_event<E: Event, Ctx: 'static, W: ?Sized>( pub state: &'a mut Ctx,
&mut self, pub data: &'a mut Data,
ctx: &mut Ctx,
id: &WidgetRef<W>,
event: E,
data: E::Data,
) {
if let Some(f) = self
.data
.modules
.get_mut::<E::Module<Ctx>>()
.run(&id.id(), event)
{
f(EventCtx {
ui: self,
state: ctx,
data,
});
}
}
} }
pub struct EventIdCtx<'a, Ctx, Data, W: ?Sized> {
pub widget: WidgetRef<W>,
pub state: &'a mut Ctx,
pub data: &'a mut Data,
}
pub trait EventFn<Ctx, Data>: Fn(EventCtx<Ctx, Data>) + Async {}
impl<F: Fn(EventCtx<Ctx, Data>) + Async, Ctx, Data> EventFn<Ctx, Data> for F {}
pub trait EventIdFn<Ctx, Data, W: ?Sized>: Fn(EventIdCtx<Ctx, Data, W>) + Async {}
impl<F: Fn(EventIdCtx<Ctx, Data, W>) + Async, Ctx, Data, W: ?Sized> EventIdFn<Ctx, Data, W> for F {}

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,92 +1,94 @@
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, LayerId, Len, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData,
TextureHandle, Textures, UiRegion, UiVec2, Vec2, Widget, WidgetRef, WidgetUpdate, Widgets, TextureHandle, Textures, UiId, 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, ui_id: UiId,
pub active: &'a mut HashMap<Id, WidgetInstance>, pub active: &'a mut HashMap<WidgetId, 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, send: &'a Sender<WidgetUpdate>,
pub cache_width: HashMap<Id, (UiVec2, Len)>, pub cache_width: HashMap<WidgetId, (UiVec2, Len)>,
pub cache_height: HashMap<Id, (UiVec2, Len)>, pub cache_height: HashMap<WidgetId, (UiVec2, Len)>,
pub needs_redraw: HashSet<Id>, pub needs_redraw: HashSet<WidgetId>,
draw_started: HashSet<Id>, draw_started: HashSet<WidgetId>,
} }
/// stores information for children about the highest level parent that needed their size /// stores information for children about the highest level parent that needed their size
/// so that they can redraw the parent if their size changes /// so that they can redraw the parent if their size changes
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct ResizeRef { pub struct ResizeRef {
x: Option<(Id, (UiVec2, Len))>, x: Option<(WidgetId, (UiVec2, Len))>,
y: Option<(Id, (UiVec2, Len))>, y: Option<(WidgetId, (UiVec2, Len))>,
} }
/// important non rendering data for retained drawing /// important non rendering data for retained drawing
#[derive(Debug)] #[derive(Debug)]
pub struct 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: LayerId,
} }
/// 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, ui_id: UiId,
pub active: HashMap<Id, WidgetInstance>, pub active: HashMap<WidgetId, 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 px_dependent: HashSet<WidgetId>,
pub px_dependent: HashSet<Id>,
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(ui_id: UiId, send: Sender<WidgetUpdate>) -> Self {
Self { Self {
widgets: Widgets::new(send), ui_id,
active: Default::default(), active: Default::default(),
layers: Default::default(), layers: Default::default(),
textures: Default::default(), textures: Default::default(),
text: Default::default(), text: Default::default(),
output_size: Default::default(), output_size: 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;
@@ -169,7 +175,7 @@ impl<'a> PainterCtx<'a> {
return finish(self, resize); return finish(self, resize);
} }
let Some(active) = self.remove(id) else { let Some(active) = self.remove(id, false) else {
return; return;
}; };
@@ -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
@@ -224,7 +231,7 @@ impl<'a> PainterCtx<'a> {
return; return;
} }
// if not, then maintain resize and track old children to remove unneeded // if not, then maintain resize and track old children to remove unneeded
let active = self.remove(id).unwrap(); let active = self.remove(id, false).unwrap();
old_children = active.children; old_children = active.children;
resize = active.resize; resize = active.resize;
} }
@@ -286,23 +293,20 @@ impl<'a> PainterCtx<'a> {
} }
} }
// update modules // run events
for m in self.modules.iter_mut() { id.map_event_managers(|mut m| {
m.on_draw(&instance); m.draw(self.ui_id, &instance);
} });
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);
*region = region.outside(&from).within(&to); *region = region.outside(&from).within(&to);
} }
active.region = active.region.outside(&from).within(&to); active.region = active.region.outside(&from).within(&to);
for m in self.modules.iter_mut() {
m.on_move(active);
}
// children will not be changed, so this technically should not be needed // children will not be changed, so this technically should not be needed
// probably need unsafe // probably need unsafe
let children = active.children.clone(); let children = active.children.clone();
@@ -312,7 +316,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, undraw: bool) -> 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 {
@@ -323,15 +327,17 @@ impl<'a> PainterCtx<'a> {
} }
inst.textures.clear(); inst.textures.clear();
self.textures.free(); self.textures.free();
for m in self.modules.iter_mut() { if undraw {
m.on_undraw(inst); id.map_event_managers(|mut m| {
m.undraw(self.ui_id, inst);
});
} }
} }
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, true);
if let Some(inst) = &inst { if let Some(inst) = &inst {
for c in &inst.children { for c in &inst.children {
self.remove_rec(*c); self.remove_rec(*c);
@@ -342,16 +348,16 @@ 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, ui_id: self.ui_id,
active: &mut self.active, active: &mut self.active,
layers: &mut self.layers, layers: &mut self.layers,
textures: &mut self.textures, textures: &mut self.textures,
text: &mut self.text, text: &mut self.text,
output_size: self.output_size, output_size: self.output_size,
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 +365,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 +415,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 +423,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 +431,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 +466,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 +512,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 +524,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 +570,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 +589,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,6 +2,8 @@ use std::ops::{Index, IndexMut};
use crate::render::{MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst, Primitives}; use crate::render::{MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst, Primitives};
pub type LayerId = usize;
struct LayerNode<T> { struct LayerNode<T> {
next: Ptr, next: Ptr,
prev: Ptr, prev: Ptr,

View File

@@ -2,32 +2,30 @@ use image::DynamicImage;
use crate::{ use crate::{
layout::{ layout::{
IdLike, PainterData, PixelRegion, TextureHandle, Vec2, Widget, WidgetInstance, WidgetLike, IdLike, PainterData, PixelRegion, TextureHandle, Vec2, WidgetHandle, WidgetId,
WidgetRef, WidgetUpdate, WidgetInstance, WidgetLike, WidgetUpdate,
}, },
util::{HashSet, Id}, util::{HashMap, HashSet, Id, StaticIdTracker},
}; };
use std::sync::mpsc::{Receiver, channel}; use std::sync::mpsc::{Receiver, channel};
static ID_TRACKER: StaticIdTracker = StaticIdTracker::new();
pub type UiId = Id;
pub struct Ui { pub struct Ui {
id: UiId,
pub(crate) data: PainterData, pub(crate) data: PainterData,
root: Option<WidgetRef>, root: Option<WidgetHandle>,
updates: HashSet<Id>, updates: HashSet<WidgetId>,
free: Vec<Id>,
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> { pub fn id(&self) -> UiId {
w.add(self) self.id
} }
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;
@@ -47,10 +45,10 @@ impl Ui {
} }
fn redraw_all(&mut self) { fn redraw_all(&mut self) {
for (_, inst) in self.data.active.drain() { for (id, inst) in self.data.active.drain() {
for m in self.data.modules.iter_mut() { id.map_event_managers(|mut m| {
m.on_undraw(&inst); m.undraw(self.id, &inst);
} });
} }
// free before bc nothing should exist // free before bc nothing should exist
self.free(); self.free();
@@ -60,15 +58,8 @@ impl Ui {
} }
pub fn update(&mut self) -> bool { pub fn update(&mut self) -> bool {
for update in self.recv.try_iter() { for id in self.recv.try_iter() {
match update { self.updates.insert(id);
WidgetUpdate::Drop(id) => {
self.free.push(id);
}
WidgetUpdate::Mutate(id) => {
self.updates.insert(id);
}
}
} }
if self.full_redraw { if self.full_redraw {
self.redraw_all(); self.redraw_all();
@@ -93,12 +84,6 @@ impl Ui {
/// free any resources that don't have references anymore /// free any resources that don't have references anymore
fn free(&mut self) { fn free(&mut self) {
for id in self.free.drain(..) {
for m in self.data.modules.iter_mut() {
m.on_remove(&id);
}
self.data.widgets.delete(id);
}
self.data.textures.free(); self.data.textures.free();
} }
@@ -106,14 +91,14 @@ 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()
} }
pub fn active(&self) -> &HashMap<WidgetId, WidgetInstance> {
&self.data.active
}
pub fn debug_layers(&self) { pub fn debug_layers(&self) {
for ((idx, depth), primitives) in self.data.layers.iter_depth() { for ((idx, depth), primitives) in self.data.layers.iter_depth() {
let indent = " ".repeat(depth * 2); let indent = " ".repeat(depth * 2);
@@ -133,7 +118,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 {
@@ -154,14 +139,21 @@ impl Ui {
impl Default for Ui { impl Default for Ui {
fn default() -> Self { fn default() -> Self {
let (send, recv) = channel(); let (send, recv) = channel();
let id = ID_TRACKER.next();
Self { Self {
data: PainterData::new(send), id,
data: PainterData::new(id, send),
root: Default::default(), root: Default::default(),
updates: Default::default(), updates: Default::default(),
free: Default::default(),
full_redraw: false, full_redraw: false,
recv, recv,
resized: false, resized: false,
} }
} }
} }
impl Drop for Ui {
fn drop(&mut self) {
ID_TRACKER.free(self.id);
}
}

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

@@ -0,0 +1,31 @@
use std::{any::TypeId, sync::mpsc::Sender};
use crate::{
layout::{EVENT_TYPES, EventManager, WIDGETS, WidgetId, WidgetUpdate},
util::{Handle, HashSet},
};
pub struct WidgetData<W: ?Sized> {
pub id: WidgetId,
pub send: Option<Sender<WidgetUpdate>>,
pub label: String,
pub event_managers: HashSet<TypeId>,
pub widget: W,
}
impl<W: ?Sized> WidgetData<W> {
pub(crate) fn event_managers(&self) -> impl Iterator<Item = Handle<dyn EventManager>> {
self.event_managers
.iter()
.map(|id| EVENT_TYPES.lock().unwrap().get_mut(id).unwrap().clone())
}
}
impl<W: ?Sized> Drop for WidgetData<W> {
fn drop(&mut self) {
WIDGETS.remove(self.id);
for mut m in self.event_managers() {
m.remove(self.id);
}
}
}

View File

@@ -1,40 +1,227 @@
use std::{ use std::{marker::Unsize, mem::MaybeUninit, ops::CoerceUnsized};
cell::{Ref, RefMut},
marker::Unsize,
ops::CoerceUnsized,
sync::mpsc::Sender,
};
use crate::{ use crate::{
layout::{IdFnTag, IdTag, Ui, Widget, WidgetLike}, layout::{EventManager, IdFnTag, IdTag, Ui, WIDGETS, Widget, WidgetData, WidgetLike},
util::{Handle, Id, WeakHandle}, util::{
Handle, MappedRefGuard, MappedRefGuardMut, Ref, RefGuard, RefGuardMut, 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,
} }
pub enum WidgetUpdate { /// A strong handle to a widget. Cannot be cloned, only one may exist
Drop(Id), /// Use .weak() to obtain weak references to this
Mutate(Id), 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 type WidgetUpdate = WidgetId;
impl<W: Widget> WidgetHandle<W> {
pub(super) fn new(id: WidgetId, widget: W) -> Self {
let mut label = std::any::type_name::<W>().to_string();
if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) {
label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1;
}
Self(
id,
WidgetData {
id,
widget,
send: None,
label,
event_managers: Default::default(),
}
.into(),
)
}
} }
impl<W> PartialEq for WidgetRef<W> { impl<W: Widget + ?Sized + Unsize<dyn Widget>> WidgetHandle<W> {
pub(super) fn weak_inner(&self) -> WeakHandle<WidgetData<W>> {
self.1.weak()
}
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> WidgetHandle<W> {
pub fn id(&self) -> WidgetId {
self.0
}
pub fn get(&self) -> RefMap<'_, W> {
Ref::map(self.1.get(), |i| &mut i.widget)
}
pub fn get_mut(&self) -> RefMapMut<'_, W> {
let inner = self.1.get_mut();
if let Some(send) = &inner.send {
let _ = send.send(self.0);
}
RefMut::map(inner, |i| &mut i.widget)
}
pub fn data(&self) -> Ref<'_, WidgetData<W>> {
self.1.get()
}
pub(crate) fn data_mut(&self) -> RefMut<'_, WidgetData<W>> {
self.1.get_mut()
}
pub(crate) 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<String>) {
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.1.refs()
}
pub fn weak(&self) -> WidgetRef<W> {
WidgetRef::new(self.0)
}
}
pub type WRef<'a, W> = MappedRefGuard<'a, WidgetData<W>, W>;
pub type WRefMut<'a, W> = MappedRefGuardMut<'a, WidgetData<W>, W>;
impl<W: Widget> WidgetRef<W> {
fn handle(&self) -> Handle<WidgetData<W>> {
WIDGETS.get_type(self.0).unwrap()
}
pub fn get<'a>(&self) -> WRef<'a, W> {
RefGuard::map(self.handle().get_take(), |i| &mut i.widget)
}
pub fn get_mut<'a>(&self) -> WRefMut<'a, W> {
let inner = self.handle().get_take_mut();
if let Some(send) = &inner.send {
let _ = send.send(self.0);
}
RefGuardMut::map(inner, |i| &mut i.widget)
}
}
impl<W: ?Sized + Widget> WidgetRef<W> {
pub fn data<'a>(&self) -> Option<RefGuard<'a, WidgetData<dyn Widget>>> {
Some(WIDGETS.get(self.0)?.get_take())
}
pub(crate) fn data_mut(&self) -> Option<RefGuardMut<'_, WidgetData<dyn Widget>>> {
Some(WIDGETS.get(self.0)?.get_take_mut())
}
}
impl<W: Widget + ?Sized + Unsize<dyn Widget>> WidgetRef<W> {
pub fn any(self) -> WidgetRef<dyn Widget> {
WidgetRef::new(self.0)
}
}
impl<W: ?Sized> WidgetRef<W> {
fn new(id: WidgetId) -> Self {
Self(id, unsafe { MaybeUninit::zeroed().assume_init() })
}
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)?))
}
pub fn weak(self) -> WidgetRef {
WidgetRef::new(self)
}
pub(crate) fn map_event_managers(&self, f: impl Fn(Handle<dyn EventManager>)) {
let Some(data) = self.weak().data() else {
return;
};
for m in data.event_managers() {
f(m)
}
}
}
pub trait WidgetIdFn<W: ?Sized = dyn Widget>: FnOnce(&mut Ui) -> WidgetHandle<W> {}
impl<W: ?Sized, F: FnOnce(&mut Ui) -> WidgetHandle<W>> WidgetIdFn<W> for F {}
pub trait WidgetRet: FnOnce(&mut Ui) -> WidgetHandle {}
impl<F: FnOnce(&mut Ui) -> WidgetHandle> WidgetRet for F {}
impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static> WidgetLike<IdTag> for WidgetHandle<W> {
type Widget = W;
fn add(self, _: &mut Ui) -> WidgetHandle<W> {
self
}
}
impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static, F: FnOnce(&mut Ui) -> WidgetHandle<W>>
WidgetLike<IdFnTag> for F
{
type Widget = W;
fn add(self, ui: &mut Ui) -> WidgetHandle<W> {
self(ui)
}
}
pub trait IdLike<W> {
fn id(&self) -> WidgetId;
}
impl<W> IdLike<W> for WidgetHandle<W> {
fn id(&self) -> WidgetId {
self.id()
}
}
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 { fn eq(&self, other: &Self) -> bool {
self.id() == other.id() self.id() == other.id()
} }
} }
impl<W> std::fmt::Debug for WidgetRef<W> { impl<W> std::fmt::Debug for WidgetHandle<W> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.id().fmt(f) self.id().fmt(f)
} }
@@ -42,122 +229,16 @@ impl<W> std::fmt::Debug for WidgetRef<W> {
impl<W: ?Sized> Clone for WidgetRef<W> { impl<W: ?Sized> Clone for WidgetRef<W> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self(self.0.clone()) *self
} }
} }
impl<W: Widget> WidgetRef<W> { impl<W: ?Sized> Copy for WidgetRef<W> {}
pub(super) fn new(id: Id, widget: W, send: Sender<WidgetUpdate>) -> Self {
let mut label = std::any::type_name::<W>().to_string(); impl<W: ?Sized> PartialEq for WidgetRef<W> {
if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) { fn eq(&self, other: &Self) -> bool {
label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1; self.0 == other.0
}
Self(
Inner {
widget,
id,
send,
label,
}
.into(),
)
} }
} }
unsafe impl<W: ?Sized> Send for WidgetRef<W> {}
impl<W: Widget + ?Sized + Unsize<dyn Widget>> WidgetRef<W> { unsafe impl<W: ?Sized> Sync for WidgetRef<W> {}
pub fn any(self) -> WidgetRef<dyn Widget> {
WidgetRef(self.0)
}
pub fn as_any(&self) -> WidgetRef<dyn Widget> {
WidgetRef(self.0.clone())
}
}
impl<W: ?Sized> WidgetRef<W> {
pub fn id(&self) -> Id {
self.0.get().id
}
pub fn get(&self) -> Ref<'_, W> {
Ref::map(self.0.get(), |i| &i.widget)
}
pub fn get_mut(&self) -> RefMut<'_, W> {
let inner = self.0.get_mut();
let _ = inner.send.send(WidgetUpdate::Mutate(inner.id));
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 fn get_label(&self) -> Ref<'_, String> {
Ref::map(self.0.get(), |i| &i.label)
}
pub fn set_label(&self, label: impl Into<String>) {
self.0.get_mut().label = label.into();
}
pub fn refs(&self) -> usize {
self.0.refs()
}
pub fn weak(&self) -> WeakWidgetRef<W> {
WeakWidgetRef(self.0.weak())
}
}
impl<W: ?Sized> WeakWidgetRef<W> {
/// should guarantee that widget is still valid to prevent indexing failures
pub(crate) fn expect_strong(&self) -> WidgetRef<W> {
WidgetRef(self.0.strong().expect("widget should not be dropped"))
}
}
impl<W: Widget + ?Sized + Unsize<dyn Widget>> WeakWidgetRef<W> {
pub fn any(self) -> WeakWidgetRef<dyn Widget> {
WeakWidgetRef(self.0.clone())
}
}
impl<W: ?Sized> Drop for Inner<W> {
fn drop(&mut self) {
let _ = self.send.send(WidgetUpdate::Drop(self.id));
}
}
pub trait WidgetIdFn<W: ?Sized = dyn Widget>: FnOnce(&mut Ui) -> WidgetRef<W> {}
impl<W: ?Sized, F: FnOnce(&mut Ui) -> WidgetRef<W>> WidgetIdFn<W> for F {}
pub trait WidgetRet: FnOnce(&mut Ui) -> WidgetRef {}
impl<F: FnOnce(&mut Ui) -> WidgetRef> WidgetRet for F {}
impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static> WidgetLike<IdTag> for WidgetRef<W> {
type Widget = W;
fn add(self, _: &mut Ui) -> WidgetRef<W> {
self
}
}
impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static, F: FnOnce(&mut Ui) -> WidgetRef<W>>
WidgetLike<IdFnTag> for F
{
type Widget = W;
fn add(self, ui: &mut Ui) -> WidgetRef<W> {
self(ui)
}
}
pub trait IdLike<W> {
fn id(&self) -> Id;
}
impl<W> IdLike<W> for WidgetRef<W> {
fn id(&self) -> Id {
self.id()
}
}
impl<W: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<WidgetRef<U>> for WidgetRef<W> {}

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

@@ -1,8 +1,10 @@
mod data;
mod handle; mod handle;
mod like; mod like;
mod tag; mod tag;
mod widgets; mod widgets;
pub use data::*;
pub use handle::*; pub use handle::*;
pub use like::*; pub use like::*;
pub use tag::*; pub use tag::*;
@@ -10,7 +12,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 +36,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,100 @@
use std::sync::mpsc::Sender; use std::sync::{Mutex, MutexGuard};
use crate::{ use crate::{
layout::{WeakWidgetRef, Widget, WidgetRef, WidgetUpdate}, layout::{
util::{HashMap, Id, IdTracker}, Widget, WidgetData, WidgetHandle,
widget::{SlotTy, WidgetId},
},
util::{Handle, WeakHandle},
}; };
pub struct Widgets { pub(crate) static WIDGETS: Widgets = Widgets::new();
ids: IdTracker, pub(crate) struct Widgets(Mutex<Inner>);
map: HashMap<Id, WeakWidgetRef>,
send: Sender<WidgetUpdate>, 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() }.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

@@ -7,7 +7,7 @@ pub struct Arena<T, I> {
tracker: IdTracker<I>, tracker: IdTracker<I>,
} }
impl<T, I: IdNum> Arena<T, I> { impl<T, I: const IdNum> Arena<T, I> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
data: Vec::new(), data: Vec::new(),
@@ -36,7 +36,7 @@ impl<T, I: IdNum> Arena<T, I> {
} }
} }
impl<T, I: IdNum> Default for Arena<T, I> { impl<T, I: const IdNum> Default for Arena<T, I> {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
@@ -48,7 +48,7 @@ pub struct TrackedArena<T, I> {
pub changed: bool, pub changed: bool,
} }
impl<T, I: IdNum> TrackedArena<T, I> { impl<T, I: const IdNum> TrackedArena<T, I> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
inner: Arena::default(), inner: Arena::default(),
@@ -86,7 +86,7 @@ impl<T, I: IdNum> TrackedArena<T, I> {
} }
} }
impl<T, I: IdNum> Default for TrackedArena<T, I> { impl<T, I: const IdNum> Default for TrackedArena<T, I> {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }

View File

@@ -1,28 +1,75 @@
//! A helper type for Arc<Mutex<T>>
//! Currently there are mut versions (of Ref stuff) which don't do anything different under the hood,
//! leaving them in for now in case I decide RwLock is a desired feature
use std::{ use std::{
cell::{Ref, RefCell, RefMut},
marker::Unsize, marker::Unsize,
ops::CoerceUnsized, ops::{CoerceUnsized, Deref, DerefMut},
rc::{Rc, Weak}, sync::{Arc, MappedMutexGuard, Mutex, MutexGuard, 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> = MutexGuard<'a, T>;
pub type RefMut<'a, T> = MutexGuard<'a, T>;
pub type RefMap<'a, T> = MappedMutexGuard<'a, T>;
pub type RefMapMut<'a, T> = 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 get_take<'a>(self) -> RefGuard<'a, T> {
let handle = self;
RefGuard {
guard: unsafe {
std::mem::transmute::<MutexGuard<'_, T>, MutexGuard<'_, T>>(
handle.0.lock().unwrap(),
)
},
handle,
}
}
pub fn get_take_mut<'a>(self) -> RefGuardMut<'a, T> {
let handle = self;
RefGuardMut {
guard: unsafe {
std::mem::transmute::<MutexGuard<'_, T>, MutexGuard<'_, T>>(
handle.0.lock().unwrap(),
)
},
handle,
}
} }
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))
}
/// # Safety
/// you must guarantee the type outside
/// ideally you check the typeid, but this is often used
/// when the trait object is wrapped one or more times
/// and you'd have to implement each one individually
pub unsafe fn downcast<U: 'static>(self) -> Handle<U>
where
T: 'static,
{
let raw: *const Mutex<T> = Arc::into_raw(self.0);
let raw: *const Mutex<U> = raw.cast();
Handle(unsafe { Arc::from_raw(raw) })
} }
} }
@@ -30,6 +77,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) -> WeakHandle<U>
where
T: 'static,
{
let raw: *const Mutex<T> = self.0.into_raw();
let raw: *const Mutex<U> = raw.cast();
WeakHandle(unsafe { Weak::from_raw(raw) })
}
} }
impl<T: ?Sized> Clone for Handle<T> { impl<T: ?Sized> Clone for Handle<T> {
@@ -52,9 +114,114 @@ 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)))
} }
} }
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Handle<U>> for Handle<T> {} impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Handle<U>> for Handle<T> {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<WeakHandle<U>> for WeakHandle<T> {} impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<WeakHandle<U>> for WeakHandle<T> {}
// yucky
// TODO: is drop order important here?
// something stupid could happen that I don't know about
// if handle is dropped before guard
pub struct RefGuard<'a, T: ?Sized> {
guard: MutexGuard<'a, T>,
handle: Handle<T>,
}
pub struct RefGuardMut<'a, T: ?Sized> {
guard: MutexGuard<'a, T>,
handle: Handle<T>,
}
pub struct MappedRefGuard<'a, T: ?Sized, U> {
guard: MappedMutexGuard<'a, U>,
handle: Handle<T>,
}
pub struct MappedRefGuardMut<'a, T: ?Sized, U> {
guard: MappedMutexGuard<'a, U>,
handle: Handle<T>,
}
impl<'a, T: ?Sized> RefGuard<'a, T> {
pub fn map<U>(s: Self, f: impl FnOnce(&mut T) -> &mut U) -> MappedRefGuard<'a, T, U> {
MappedRefGuard {
guard: MutexGuard::map(s.guard, f),
handle: s.handle,
}
}
}
impl<'a, T: ?Sized> RefGuardMut<'a, T> {
pub fn map<U>(s: Self, f: impl FnOnce(&mut T) -> &mut U) -> MappedRefGuardMut<'a, T, U> {
MappedRefGuardMut {
guard: MutexGuard::map(s.guard, f),
handle: s.handle,
}
}
}
impl<'a, T: ?Sized, U> MappedRefGuard<'a, T, U> {
pub fn map<U2>(s: Self, f: impl FnOnce(&mut U) -> &mut U2) -> MappedRefGuard<'a, T, U2> {
MappedRefGuard {
guard: MappedMutexGuard::map(s.guard, f),
handle: s.handle,
}
}
}
impl<'a, T: ?Sized, U> MappedRefGuardMut<'a, T, U> {
pub fn map<U2>(s: Self, f: impl FnOnce(&mut U) -> &mut U2) -> MappedRefGuardMut<'a, T, U2> {
MappedRefGuardMut {
guard: MappedMutexGuard::map(s.guard, f),
handle: s.handle,
}
}
}
impl<T: ?Sized> Deref for RefGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.guard
}
}
impl<T: ?Sized> Deref for RefGuardMut<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.guard
}
}
impl<T: ?Sized> DerefMut for RefGuardMut<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.guard
}
}
impl<T: ?Sized, U> Deref for MappedRefGuard<'_, T, U> {
type Target = U;
fn deref(&self) -> &Self::Target {
&self.guard
}
}
impl<T: ?Sized, U> Deref for MappedRefGuardMut<'_, T, U> {
type Target = U;
fn deref(&self) -> &Self::Target {
&self.guard
}
}
impl<T: ?Sized, U> DerefMut for MappedRefGuardMut<'_, T, U> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.guard
}
}

View File

@@ -1,3 +1,5 @@
use std::sync::Mutex;
#[repr(C)] #[repr(C)]
#[derive(Eq, Hash, PartialEq, Debug, Clone, Copy, bytemuck::Zeroable)] #[derive(Eq, Hash, PartialEq, Debug, Clone, Copy, bytemuck::Zeroable)]
pub struct Id<I = u64>(I); pub struct Id<I = u64>(I);
@@ -9,7 +11,14 @@ pub struct IdTracker<I = u64> {
cur: Id<I>, cur: Id<I>,
} }
impl<I: IdNum> IdTracker<I> { impl<I: const IdNum> IdTracker<I> {
pub const fn new() -> Self {
Self {
free: Vec::new(),
cur: Id(I::first()),
}
}
#[allow(clippy::should_implement_trait)] #[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Id<I> { pub fn next(&mut self) -> Id<I> {
if let Some(id) = self.free.pop() { if let Some(id) = self.free.pop() {
@@ -43,22 +52,19 @@ impl<I: IdNum> Id<I> {
} }
} }
impl<I: IdNum> Default for IdTracker<I> { impl<I: const IdNum> Default for IdTracker<I> {
fn default() -> Self { fn default() -> Self {
Self { Self::new()
free: Vec::new(),
cur: Id(I::first()),
}
} }
} }
pub trait IdNum { pub const trait IdNum {
fn first() -> Self; fn first() -> Self;
fn next(&self) -> Self; fn next(&self) -> Self;
fn idx(&self) -> usize; fn idx(&self) -> usize;
} }
impl IdNum for u64 { impl const IdNum for u64 {
fn first() -> Self { fn first() -> Self {
0 0
} }
@@ -72,7 +78,7 @@ impl IdNum for u64 {
} }
} }
impl IdNum for u32 { impl const IdNum for u32 {
fn first() -> Self { fn first() -> Self {
0 0
} }
@@ -85,3 +91,33 @@ impl IdNum for u32 {
*self as usize *self as usize
} }
} }
pub struct StaticIdTracker<I = u64>(Mutex<IdTracker<I>>);
impl<I: const IdNum> StaticIdTracker<I> {
pub const fn new() -> Self {
Self(Mutex::new(IdTracker::new()))
}
#[allow(clippy::should_implement_trait)]
pub fn next(&self) -> Id<I> {
let mut s = self.0.lock().unwrap();
if let Some(id) = s.free.pop() {
return id;
}
let next = s.cur.next();
std::mem::replace(&mut s.cur, next)
}
#[allow(dead_code)]
pub fn free(&self, id: Id<I>) {
let mut s = self.0.lock().unwrap();
s.free.push(id);
}
}
impl<I: const IdNum> Default for StaticIdTracker<I> {
fn default() -> Self {
Self::new()
}
}

View File

@@ -24,6 +24,12 @@ impl RefCounter {
} }
} }
impl Default for RefCounter {
fn default() -> Self {
Self::new()
}
}
impl Clone for RefCounter { impl Clone for RefCounter {
fn clone(&self) -> Self { fn clone(&self) -> Self {
self.0.fetch_add(1, Ordering::Release); self.0.fetch_add(1, Ordering::Release);

View File

@@ -2,12 +2,13 @@ extern crate proc_macro;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{ use syn::{
Block, Ident, Signature, Token, Visibility, Attribute, Block, Ident, ItemTrait, Signature, Token, Visibility,
parse::{Parse, ParseStream, Result}, parse::{Parse, ParseStream, Result},
parse_macro_input, parse_macro_input, parse_quote,
}; };
struct Input { struct Input {
attrs: Vec<Attribute>,
vis: Visibility, vis: Visibility,
name: Ident, name: Ident,
fns: Vec<InputFn>, fns: Vec<InputFn>,
@@ -20,7 +21,9 @@ struct InputFn {
impl Parse for Input { impl Parse for Input {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let vis = input.parse()?; let vis = input.parse()?;
input.parse::<Token![trait]>()?;
let name = input.parse()?; let name = input.parse()?;
input.parse::<Token![;]>()?; input.parse::<Token![;]>()?;
let mut fns = Vec::new(); let mut fns = Vec::new();
@@ -32,27 +35,23 @@ impl Parse for Input {
if !input.is_empty() { if !input.is_empty() {
input.error("function expected"); input.error("function expected");
} }
Ok(Input { vis, name, fns }) Ok(Input {
attrs,
vis,
name,
fns,
})
} }
} }
// pub trait $name<W: WidgetLike<Tag>, Tag> {
// $(
// fn $fn $(<$($T $(: $TT $(+ $TL)?)?,)*>)?($self $(, $arg: $ty)*) -> $ret;
// )*
// }
//
// impl<W: WidgetLike<Tag>, Tag> $name<W, Tag> for W {
// $(
// fn $fn $(<$($T $(: $TT $(+ $TL)?)?,)*>)?($self $(, $arg: $ty)*) -> $ret {
// $code
// }
// )*
// }
#[proc_macro] #[proc_macro]
pub fn widget_trait(input: TokenStream) -> TokenStream { pub fn widget_trait(input: TokenStream) -> TokenStream {
let Input { vis, name, fns } = parse_macro_input!(input as Input); let Input {
attrs,
vis,
name,
fns,
} = parse_macro_input!(input as Input);
let sigs: Vec<_> = fns.iter().map(|f| f.sig.clone()).collect(); let sigs: Vec<_> = fns.iter().map(|f| f.sig.clone()).collect();
let impls: Vec<_> = fns let impls: Vec<_> = fns
@@ -60,10 +59,16 @@ pub fn widget_trait(input: TokenStream) -> TokenStream {
.map(|InputFn { sig, body }| quote! { #sig #body }) .map(|InputFn { sig, body }| quote! { #sig #body })
.collect(); .collect();
TokenStream::from(quote! { let mut trai: ItemTrait = parse_quote!(
#vis trait #name<WL: WidgetLike<Tag>, Tag> { #vis trait #name<WL: WidgetLike<Tag>, Tag> {
#(#sigs;)* #(#sigs;)*
} }
);
trai.attrs = attrs;
TokenStream::from(quote! {
#trai
impl<WL: WidgetLike<Tag>, Tag> #name<WL, Tag> for WL { impl<WL: WidgetLike<Tag>, Tag> #name<WL, Tag> for WL {
#(#impls)* #(#impls)*

View File

@@ -51,20 +51,19 @@ 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)
.on(CursorSense::click(), move |ctx| { .on(CursorSense::click(), move |ctx| {
let child = image(include_bytes!("assets/sungals.png")) let child = image(include_bytes!("assets/sungals.png"))
.center() .center()
.add(ctx.ui); .add(ctx.data.ui);
span_add_.get_mut().children.push(child); span_add_.get_mut().children.push(child);
}) })
.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)
@@ -113,19 +113,22 @@ impl DefaultAppState for Client {
.text_align(Align::LEFT) .text_align(Align::LEFT)
.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
texts.get_mut().children.push(msg_box); .background(rect(Color::WHITE.darker(0.5)))
.add(&mut Ui::new());
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, ()); Events::<Submit, _>::run(add_text_.id(), &mut (), ctx.state);
}) })
.sized((40, 40)), .sized((40, 40)),
) )
@@ -140,13 +143,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 +176,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 +202,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,27 +1,24 @@
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};
event_ctx!(UiState);
pub struct Selector; pub struct Selector;
impl<W: 'static> WidgetAttr<W> for Selector { impl<W: Widget + '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(); container.on(CursorSense::click_or_drag(), move |ctx| {
ui.register_event( let ui = &mut ctx.data.ui;
&container.clone(), let region = ui.window_region(&id).unwrap();
CursorSense::click_or_drag(), let id_pos = region.top_left;
move |mut ctx| { let container_pos = ui.window_region(&container).unwrap().top_left;
let ui = ctx.ui; let pos = ctx.data.pos + container_pos - id_pos;
let region = ui.window_region(&id).unwrap(); let size = region.size();
let id_pos = region.top_left; select(ui, id, ctx.state, pos, size, ctx.data.sense.is_dragging());
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);
},
);
} }
} }
@@ -30,20 +27,32 @@ 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(); id.on(CursorSense::click_or_drag(), move |ctx| {
ui.register_event(&id.clone(), CursorSense::click_or_drag(), move |ctx| { select(
select(ctx.ui, id.clone(), ctx.state, ctx.data); ctx.data.ui,
id,
ctx.state,
ctx.data.pos,
ctx.data.size,
ctx.data.sense.is_dragging(),
);
}); });
} }
} }
fn select(ui: &mut Ui, id: WidgetRef<TextEdit>, state: &mut UiState, data: CursorData) { fn select(
ui: &mut Ui,
id: WidgetRef<TextEdit>,
state: &mut UiState,
pos: Vec2,
size: Vec2,
dragging: bool,
) {
let now = Instant::now(); let now = Instant::now();
let recent = (now - state.last_click) < Duration::from_millis(300); let recent = (now - state.last_click) < Duration::from_millis(300);
state.last_click = now; state.last_click = now;
id.get_mut() id.get_mut().select(pos, size, dragging, recent);
.select(data.cursor, data.size, data.sense.is_dragging(), recent);
if let Some(region) = ui.window_region(&id) { if let Some(region) = ui.window_region(&id) {
state.window.set_ime_allowed(true); state.window.set_ime_allowed(true);
state.window.set_ime_cursor_area( state.window.set_ime_cursor_area(

View File

@@ -1,15 +1,9 @@
use crate::layout::DefaultEvent; use iris_core::layout::Event;
#[derive(Eq, PartialEq, Hash, Clone)] #[derive(Clone)]
pub struct Submit; pub struct Submit;
impl Event for Submit {}
#[derive(Eq, PartialEq, Hash, Clone)] #[derive(Clone)]
pub struct Edited; pub struct Edited;
impl Event for Edited {}
impl DefaultEvent for Submit {
type Data = ();
}
impl DefaultEvent for Edited {
type Data = ();
}

View File

@@ -91,7 +91,7 @@ impl<State: DefaultAppState> AppState for DefaultState<State> {
let input_changed = ui_state.input.event(&event); let input_changed = ui_state.input.event(&event);
let cursor_state = ui_state.cursor_state().clone(); let cursor_state = ui_state.cursor_state().clone();
let old = ui_state.focus.clone(); let old = ui_state.focus;
if cursor_state.buttons.left.is_start() { if cursor_state.buttons.left.is_start() {
ui_state.focus = None; ui_state.focus = None;
} }
@@ -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 => {
@@ -131,13 +130,13 @@ impl<State: DefaultAppState> AppState for DefaultState<State> {
ui_state.window.set_ime_allowed(false); ui_state.window.set_ime_allowed(false);
} }
TextInputResult::Submit => { TextInputResult::Submit => {
ui.run_event(app_state, sel, Submit, ()); Events::<Submit, _>::run(sel.id(), &mut (), app_state);
} }
TextInputResult::Paste => { TextInputResult::Paste => {
if let Ok(t) = ui_state.clipboard.get_text() { if let Ok(t) = ui_state.clipboard.get_text() {
sel.get_mut().insert(&t); sel.get_mut().insert(&t);
} }
ui.run_event(app_state, sel, Edited, ()); Events::<Edited, _>::run(sel.id(), &mut (), app_state);
} }
TextInputResult::Copy(text) => { TextInputResult::Copy(text) => {
if let Err(err) = ui_state.clipboard.set_text(text) { if let Err(err) = ui_state.clipboard.set_text(text) {
@@ -145,7 +144,7 @@ impl<State: DefaultAppState> AppState for DefaultState<State> {
} }
} }
TextInputResult::Used => { TextInputResult::Used => {
ui.run_event(app_state, sel, Edited, ()); Events::<Edited, _>::run(sel.id(), &mut (), app_state);
} }
TextInputResult::Unused => {} TextInputResult::Unused => {}
} }

66
src/event.rs Normal file
View File

@@ -0,0 +1,66 @@
use crate::prelude::*;
use iris_macro::widget_trait;
pub mod eventable {
use super::*;
widget_trait! {
pub trait Eventable;
fn on<E: EventAlias, Ctx: 'static>(
self,
event: E,
f: impl for<'a> EventIdFn<Ctx, <E::Event as Event>::Data<'a>, WL::Widget>,
) -> impl WidgetIdFn<WL::Widget> {
move |ui| {
let id = self.add(ui);
Events::<E::Event, Ctx>::register(id.weak(), event.into_event(), f);
id
}
}
}
}
// TODO: naming in here is a bit weird like eventable
#[macro_export]
macro_rules! event_ctx {
($ty: ty) => {
mod local_event_trait {
use super::*;
#[allow(unused_imports)]
use $crate::prelude::*;
widget_trait! {
#[allow(unused)]
pub trait EventableCtx;
fn on<E: EventAlias>(
self,
event: E,
f: impl for<'a> EventIdFn<$ty, <E::Event as Event>::Data<'a>, WL::Widget>,
) -> impl WidgetIdFn<WL::Widget> {
eventable::Eventable::on(self, event, f)
}
}
use std::marker::Sized;
#[allow(unused)]
pub trait EventableCtxRef<W: Widget + ?Sized> {
fn on<E: EventAlias>(
self,
event: E,
f: impl for<'a> EventIdFn<$ty, <E::Event as Event>::Data<'a>, W>,
);
}
impl<W: Widget + ?Sized> EventableCtxRef<W> for WidgetRef<W> {
fn on<E: EventAlias>(
self,
event: E,
f: impl for<'a> EventIdFn<$ty, <E::Event as Event>::Data<'a>, W>,
) {
Events::<E::Event, $ty>::register(self, event.into_event(), f);
}
}
}
use local_event_trait::*;
};
}
pub use event_ctx;

View File

@@ -4,7 +4,7 @@
#![feature(associated_type_defaults)] #![feature(associated_type_defaults)]
mod default; mod default;
mod traits; mod event;
mod widget; mod widget;
pub use iris_core::*; pub use iris_core::*;
@@ -12,7 +12,7 @@ pub use iris_macro::*;
pub mod prelude { pub mod prelude {
pub use super::default::*; pub use super::default::*;
pub use super::traits::*; pub use super::event::*;
pub use super::widget::*; pub use super::widget::*;
pub use iris_core::layout::*; pub use iris_core::layout::*;

View File

@@ -1,82 +0,0 @@
use crate::prelude::*;
use iris_macro::widget_trait;
// TODO: naming in here is a bit weird like eventable
#[macro_export]
macro_rules! event_ctx {
($ty: ty) => {
mod local_event_trait {
use super::*;
use std::marker::Sized;
#[allow(unused_imports)]
use $crate::prelude::*;
#[allow(unused)]
pub trait EventableCtx<W: ?Sized, Tag, Ctx: 'static> {
fn on<E: Event>(
self,
event: E,
f: impl WidgetEventFn<Ctx, E::Data, W>,
) -> impl WidgetIdFn<W> + EventableCtx<W, IdFnTag, Ctx>;
}
impl<WL: WidgetLike<Tag>, Tag> EventableCtx<WL::Widget, Tag, $ty> for WL {
fn on<E: Event>(
self,
event: E,
f: impl WidgetEventFn<$ty, E::Data, WL::Widget>,
) -> impl WidgetIdFn<WL::Widget> + EventableCtx<WL::Widget, IdFnTag, $ty> {
eventable::Eventable::on(self, event, f)
}
}
#[allow(unused)]
pub trait EventableCtxUi<W: ?Sized, Tag, Ctx: 'static>
where
WidgetRef<W>: EventableCtx<W, Tag, Ctx>,
{
fn on<E: Event>(
&mut self,
widget: &WidgetRef<W>,
event: E,
f: impl WidgetEventFn<Ctx, E::Data, W>,
);
}
impl<W: ?Sized + 'static, Tag> EventableCtxUi<W, Tag, $ty> for Ui
where
WidgetRef<W>: EventableCtx<W, Tag, $ty>,
{
fn on<E: Event>(
&mut self,
widget: &WidgetRef<W>,
event: E,
f: impl WidgetEventFn<$ty, E::Data, W>,
) {
self.register_widget_event(&widget, event, f);
}
}
}
use local_event_trait::*;
};
}
pub use event_ctx;
pub mod eventable {
use super::*;
widget_trait!(
pub Eventable;
fn on<E: Event, Ctx: 'static>(
self,
event: E,
f: impl WidgetEventFn<Ctx, E::Data, WL::Widget>,
) -> impl WidgetIdFn<WL::Widget> {
move |ui| {
let id = self.add(ui);
ui.register_widget_event(&id, event, f);
id
}
}
);
}

View File

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,16 +1,10 @@
use crate::layout::{UiRegion, Vec2};
use crate::prelude::*; use crate::prelude::*;
use crate::util::Id;
use std::{ use std::{
ops::{BitOr, Deref, DerefMut}, ops::{BitOr, Deref, DerefMut},
rc::Rc, rc::Rc,
}; };
use crate::{
layout::{UiModule, UiRegion, Vec2},
util::HashMap,
};
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub enum CursorButton { pub enum CursorButton {
Left, Left,
@@ -29,8 +23,22 @@ pub enum CursorSense {
Scroll, Scroll,
} }
#[derive(Clone)]
pub struct CursorSenses(Vec<CursorSense>); pub struct CursorSenses(Vec<CursorSense>);
impl Event for CursorSenses {
type Data<'a> = CursorData<'a>;
type State = SensorState;
fn should_run(&self, data: &mut Self::Data<'_>) -> bool {
if let Some(sense) = should_run(self, data.cursor, data.hover) {
data.sense = sense;
true
} else {
false
}
}
}
impl CursorSense { impl CursorSense {
pub fn click() -> Self { pub fn click() -> Self {
Self::PressStart(CursorButton::Left) Self::PressStart(CursorButton::Left)
@@ -75,6 +83,16 @@ impl CursorButtons {
self.middle.end_frame(); self.middle.end_frame();
self.right.end_frame(); self.right.end_frame();
} }
pub fn iter(&self) -> impl Iterator<Item = (CursorButton, &ActivationState)> {
[
CursorButton::Left,
CursorButton::Middle,
CursorButton::Right,
]
.into_iter()
.map(|b| (b, self.select(&b)))
}
} }
impl CursorState { impl CursorState {
@@ -103,70 +121,25 @@ pub struct Sensor<Ctx, Data> {
pub f: Rc<dyn EventFn<Ctx, Data>>, pub f: Rc<dyn EventFn<Ctx, Data>>,
} }
pub type SensorMap<Ctx, Data> = HashMap<Id, SensorGroup<Ctx, Data>>;
pub type SenseShape = UiRegion; pub type SenseShape = UiRegion;
pub struct SensorGroup<Ctx, Data> {
#[derive(Default, Debug)]
pub struct SensorState {
pub hover: ActivationState, pub hover: ActivationState,
pub sensors: Vec<Sensor<Ctx, Data>>,
} }
#[derive(Clone)] pub struct CursorData<'a> {
pub struct CursorData { /// where this widget was hit
pub cursor: Vec2, pub pos: Vec2,
pub size: Vec2, pub size: Vec2,
pub scroll_delta: Vec2, pub scroll_delta: Vec2,
/// the (first) sense that triggered this event pub hover: ActivationState,
/// the senses are checked in order pub cursor: &'a CursorState,
pub ui: &'a mut Ui,
/// the first sense that triggered this
pub sense: CursorSense, pub sense: CursorSense,
} }
pub struct CursorModule<Ctx> {
map: SensorMap<Ctx, CursorData>,
active: HashMap<usize, HashMap<Id, SenseShape>>,
}
impl<Ctx: 'static> UiModule for CursorModule<Ctx> {
fn on_draw(&mut self, inst: &WidgetInstance) {
if self.map.contains_key(&inst.id) {
self.active
.entry(inst.layer)
.or_default()
.insert(inst.id, inst.region);
}
}
fn on_undraw(&mut self, inst: &WidgetInstance) {
if let Some(layer) = self.active.get_mut(&inst.layer) {
layer.remove(&inst.id);
}
}
fn on_remove(&mut self, id: &Id) {
self.map.remove(id);
for layer in self.active.values_mut() {
layer.remove(id);
}
}
fn on_move(&mut self, inst: &WidgetInstance) {
if let Some(map) = self.active.get_mut(&inst.layer)
&& let Some(region) = map.get_mut(&inst.id)
{
*region = inst.region;
}
}
}
impl<Ctx> CursorModule<Ctx> {
pub fn merge(&mut self, other: Self) {
for (id, group) in other.map {
for sensor in group.sensors {
self.map.entry(id).or_default().sensors.push(sensor);
}
}
}
}
pub trait SensorUi { pub trait SensorUi {
fn run_sensors<Ctx: 'static>(&mut self, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2); fn run_sensors<Ctx: 'static>(&mut self, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2);
} }
@@ -178,55 +151,41 @@ impl SensorUi for Ui {
cursor: &CursorState, cursor: &CursorState,
window_size: Vec2, window_size: Vec2,
) { ) {
CursorModule::<Ctx>::run(self, ctx, cursor, window_size); let ui_id = self.id();
} let layers = std::mem::take(&mut self.data_mut().layers);
} let mut active = std::mem::take(&mut *Events::<CursorSenses, Ctx>::active(ui_id));
impl<Ctx: 'static> CursorModule<Ctx> {
pub fn run(ui: &mut Ui, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) {
let layers = std::mem::take(&mut ui.data_mut().layers);
let mut module = std::mem::take(ui.data_mut().modules.get_mut::<Self>());
for i in layers.indices().rev() { for i in layers.indices().rev() {
let Some(list) = module.active.get_mut(&i) else {
continue;
};
let mut sensed = false; let mut sensed = false;
for (id, shape) in list.iter() { for (id, state) in active.get_mut(&i).into_iter().flatten() {
let group = module.map.get_mut(id).unwrap(); let shape = self.active().get(id).unwrap().region;
let region = shape.to_px(window_size); let region = shape.to_px(window_size);
let in_shape = cursor.exists && region.contains(cursor.pos); let in_shape = cursor.exists && region.contains(cursor.pos);
group.hover.update(in_shape); state.hover.update(in_shape);
if group.hover == ActivationState::Off { if state.hover == ActivationState::Off {
continue; continue;
} }
sensed = true; sensed = true;
for sensor in &mut group.sensors { let mut data = CursorData {
if let Some(sense) = should_run(&sensor.senses, cursor, group.hover) { pos: cursor.pos - region.top_left,
let data = CursorData { size: region.bot_right - region.top_left,
cursor: cursor.pos - region.top_left, scroll_delta: cursor.scroll_delta,
size: region.bot_right - region.top_left, hover: state.hover,
scroll_delta: cursor.scroll_delta, cursor,
sense, ui: self,
}; // this does not have any meaning;
(sensor.f)(EventCtx { // might wanna set up Event to have a prepare stage
ui, sense: CursorSense::Hovering,
state: ctx, };
data, Events::<CursorSenses, Ctx>::run(*id, &mut data, ctx);
});
}
}
} }
if sensed { if sensed {
break; break;
} }
} }
let ui_mod = ui.data_mut().modules.get_mut::<Self>(); *Events::<CursorSenses, Ctx>::active(ui_id) = active;
std::mem::swap(ui_mod, &mut module); self.data_mut().layers = layers;
ui_mod.merge(module);
ui.data_mut().layers = layers;
} }
} }
@@ -294,66 +253,10 @@ impl ActivationState {
} }
} }
impl Event for CursorSenses { impl EventAlias for CursorSense {
type Module<Ctx: 'static> = CursorModule<Ctx>; type Event = CursorSenses;
type Data = CursorData; fn into_event(self) -> Self::Event {
} CursorSenses::from(self)
impl Event for CursorSense {
type Module<Ctx: 'static> = CursorModule<Ctx>;
type Data = CursorData;
}
impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: 'static>
EventModule<E, Ctx> for CursorModule<Ctx>
{
fn register(&mut self, id: Id, senses: E, f: impl EventFn<Ctx, <E as Event>::Data>) {
// TODO: does not add to active if currently active
self.map.entry(id).or_default().sensors.push(Sensor {
senses: senses.into(),
f: Rc::new(f),
});
}
fn run<'a>(
&self,
id: &Id,
event: E,
) -> Option<impl Fn(EventCtx<Ctx, <E as Event>::Data>) + use<'a, E, Ctx>> {
let senses = event.into();
if let Some(group) = self.map.get(id) {
let fs: Vec<_> = group
.sensors
.iter()
.filter_map(|sensor| {
if sensor.senses.iter().any(|s| senses.contains(s)) {
Some(sensor.f.clone())
} else {
None
}
})
.collect();
Some(move |ctx: EventCtx<Ctx, CursorData>| {
for f in &fs {
f(EventCtx {
state: ctx.state,
ui: ctx.ui,
data: ctx.data.clone(),
});
}
})
} else {
None
}
}
}
impl<Ctx, Data> Default for SensorGroup<Ctx, Data> {
fn default() -> Self {
Self {
hover: Default::default(),
sensors: Default::default(),
}
} }
} }
@@ -393,12 +296,3 @@ impl BitOr<CursorSense> for CursorSenses {
self self
} }
} }
impl<Ctx> Default for CursorModule<Ctx> {
fn default() -> Self {
Self {
map: Default::default(),
active: Default::default(),
}
}
}

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

@@ -4,8 +4,8 @@ use iris_macro::widget_trait;
// these methods should "not require any context" (require unit) because they're in core // these methods should "not require any context" (require unit) because they're in core
event_ctx!(()); event_ctx!(());
widget_trait!( widget_trait! {
pub CoreWidget; pub trait CoreWidget;
fn pad(self, padding: impl Into<Padding>) -> impl WidgetFn<Pad> { fn pad(self, padding: impl Into<Padding>) -> impl WidgetFn<Pad> {
|ui| Pad { |ui| Pad {
@@ -127,10 +127,10 @@ 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));
} }
); }
pub trait CoreWidgetArr<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> { pub trait CoreWidgetArr<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> {
fn span(self, dir: Dir) -> SpanBuilder<LEN, Wa, Tag>; fn span(self, dir: Dir) -> SpanBuilder<LEN, Wa, Tag>;