use std::{marker::Unsize, ops::CoerceUnsized, sync::mpsc::Sender}; use crate::{ HasUi, Widget, util::{RefCounter, SlotId}, }; pub type WidgetId = SlotId; /// An identifier for a widget that can index a UI or event ctx to get it. /// This is a strong handle that does not impl Clone, and when it is dropped, /// a signal is sent to the owning UI to clean up the resources. /// /// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs? pub struct WidgetHandle { pub(super) id: WidgetId, counter: RefCounter, send: Sender, ty: *const W, } /// A weak handle to a widget. /// Will not keep it alive, but can still be used for indexing like WidgetHandle. pub struct WidgetRef { pub(super) id: WidgetId, #[allow(unused)] ty: *const W, } pub struct WidgetHandles { pub h: WidgetHandle, pub r: WidgetRef, } impl> WidgetHandle { pub fn any(self) -> WidgetHandle { self } } impl WidgetHandle { pub(crate) fn new(id: WidgetId, send: Sender) -> Self { Self { id, counter: RefCounter::new(), send, ty: null_ptr(), } } pub fn id(&self) -> WidgetId { self.id } pub fn refs(&self) -> u32 { self.counter.refs() } pub fn weak(&self) -> WidgetRef { let Self { ty, id, .. } = *self; WidgetRef { ty, id } } pub fn handles(self) -> WidgetHandles { let r = self.weak(); WidgetHandles { h: self, r } } } impl WidgetRef { pub(crate) fn new(id: WidgetId) -> Self { Self { id, ty: null_ptr() } } pub fn id(&self) -> WidgetId { self.id } #[track_caller] pub fn upgrade(self, ui: &mut impl HasUi) -> WidgetHandle { ui.ui_mut().widgets.upgrade(self) } } impl Drop for WidgetHandle { fn drop(&mut self) { if self.counter.drop() { let _ = self.send.send(self.id); } } } pub trait WidgetIdFn: FnOnce(&mut State) -> WidgetRef {} impl WidgetRef> WidgetIdFn for F {} pub trait IdLike { type Widget: ?Sized; fn id(&self) -> WidgetId; } impl IdLike for &WidgetHandle { type Widget = W; fn id(&self) -> WidgetId { self.id } } impl IdLike for WidgetHandle { type Widget = W; fn id(&self) -> WidgetId { self.id } } impl IdLike for WidgetRef { type Widget = W; fn id(&self) -> WidgetId { self.id } } impl IdLike for WidgetId { type Widget = dyn Widget; fn id(&self) -> WidgetId { *self } } impl, U: ?Sized> CoerceUnsized> for WidgetHandle {} impl, U: ?Sized> CoerceUnsized> for WidgetRef {} impl Clone for WidgetRef { fn clone(&self) -> Self { *self } } impl Copy for WidgetRef {} impl PartialEq for WidgetRef { fn eq(&self, other: &Self) -> bool { self.id == other.id } } impl PartialEq for WidgetHandle { fn eq(&self, other: &Self) -> bool { self.id == other.id } } impl std::fmt::Debug for WidgetHandle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.id.fmt(f) } } impl<'a, W: Widget + 'a, State: HasUi> FnOnce<(&'a mut State,)> for WidgetRef { type Output = &'a mut W; extern "rust-call" fn call_once(self, args: (&'a mut State,)) -> Self::Output { &mut args.0.ui_mut()[self] } } fn null_ptr() -> *const W { if size_of::<&W>() == size_of::<*const dyn Widget>() { let w: *const dyn Widget = &(); unsafe { std::mem::transmute_copy(&w) } } else { unsafe { std::mem::transmute_copy(&[0usize; 1]) } } }