use std::{marker::Unsize, mem::MaybeUninit, 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: unsafe { MaybeUninit::zeroed().assume_init() }, } } 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 fn id(&self) -> WidgetId { self.id } } impl Drop for WidgetHandle { fn drop(&mut self) { if self.counter.drop() { let _ = self.send.send(self.id); } } } pub trait WidgetIdFn: FnOnce(&mut State) -> WidgetHandle {} impl WidgetHandle> WidgetIdFn for F {} pub trait IdLike { type Widget: Widget + ?Sized + 'static; 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] } }