use std::{marker::Unsize, ops::CoerceUnsized, sync::mpsc::Sender}; use crate::{ UiRsc, 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 StrongWidget { 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 WeakWidget { pub(super) id: WidgetId, #[allow(unused)] ty: *const W, } impl> StrongWidget { pub fn any(self) -> StrongWidget { self } } impl StrongWidget { 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) -> WeakWidget { let Self { ty, id, .. } = *self; WeakWidget { ty, id } } } impl WeakWidget { 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 UiRsc) -> StrongWidget { ui.widgets_mut().upgrade(self) } } impl Drop for StrongWidget { fn drop(&mut self) { if self.counter.drop() { let _ = self.send.send(self.id); } } } pub trait WidgetIdFn: FnOnce(&mut Rsc) -> WeakWidget {} impl WeakWidget> WidgetIdFn for F {} pub trait IdLike { type Widget: ?Sized; fn id(&self) -> WidgetId; } impl IdLike for &StrongWidget { type Widget = W; fn id(&self) -> WidgetId { self.id } } impl IdLike for StrongWidget { type Widget = W; fn id(&self) -> WidgetId { self.id } } impl IdLike for WeakWidget { 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 StrongWidget {} impl, U: ?Sized> CoerceUnsized> for WeakWidget {} impl Clone for WeakWidget { fn clone(&self) -> Self { *self } } impl Copy for WeakWidget {} impl PartialEq for WeakWidget { fn eq(&self, other: &Self) -> bool { self.id == other.id } } impl PartialEq for StrongWidget { fn eq(&self, other: &Self) -> bool { self.id == other.id } } impl std::fmt::Debug for StrongWidget { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.id.fmt(f) } } impl<'a, W: Widget + 'a, State: UiRsc> FnOnce<(&'a mut State,)> for WeakWidget { type Output = &'a mut W; extern "rust-call" fn call_once(self, args: (&'a mut State,)) -> Self::Output { &mut args.0.widgets_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]) } } } unsafe impl Send for WeakWidget {} unsafe impl Sync for WeakWidget {}