use std::{ any::TypeId, marker::PhantomData, sync::{ Arc, atomic::{AtomicBool, Ordering}, mpsc::Sender, }, }; use crate::{ layout::{Ui, WidgetLike}, util::{Id, RefCounter}, }; pub struct AnyWidget; /// An identifier for a widget that can index a UI to get the associated widget. /// It should always remain valid; it keeps a ref count and removes the widget from the UI if all /// references are dropped. /// /// W does not need to implement widget so that AnyWidget is valid; /// Instead, add generic bounds on methods that take an ID if they need specific data. /// /// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs? #[repr(C)] pub struct WidgetId { pub(super) ty: TypeId, pub(super) id: Id, counter: RefCounter, send: Sender, is_static: Arc, _pd: PhantomData, } impl PartialEq for WidgetId { fn eq(&self, other: &Self) -> bool { self.ty == other.ty && self.id == other.id } } /// A WidgetId for a static widget that cannot be removed from a Ui. /// Useful because ergonomic clones don't exist yet so you can easily use these in closures. /// Do not use this if you want the widget to be freeable. /// /// This is currently not perfectly efficient and just creates new WidgetIds every time it's used, /// but they don't send drop messages to Ui. /// Ideally I'd have an enum or something that lets you use either, but that doesn't seem worth it /// right now; it's good enough and relatively cheap. #[repr(C)] pub struct StaticWidgetId { pub(super) ty: TypeId, pub(super) id: Id, _pd: PhantomData, } impl std::fmt::Debug for WidgetId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.id.fmt(f) } } impl Clone for WidgetId { fn clone(&self) -> Self { Self { id: self.id, ty: self.ty, counter: self.counter.clone(), send: self.send.clone(), is_static: self.is_static.clone(), _pd: PhantomData, } } } impl WidgetId { pub(super) fn new(id: Id, ty: TypeId, send: Sender, is_static: bool) -> Self { Self { ty, id, counter: RefCounter::new(), send, is_static: Arc::new(is_static.into()), _pd: PhantomData, } } pub fn any(self) -> WidgetId { self.cast_type() } pub fn as_any(&self) -> &WidgetId { // SAFETY: self is repr(C) and generic only used for phantom data unsafe { std::mem::transmute(self) } } pub fn key(&self) -> Id { self.id } pub(super) fn cast_type(self) -> WidgetId { // SAFETY: self is repr(C) and generic only used for phantom data unsafe { std::mem::transmute(self) } } pub fn refs(&self) -> u32 { self.counter.refs() } pub fn into_static(self) -> StaticWidgetId { self.is_static.store(true, Ordering::Release); StaticWidgetId { ty: self.ty, id: self.id, _pd: PhantomData, } } } impl WidgetId { pub fn set_static(&mut self, other: StaticWidgetId) { let send = self.send.clone(); drop(std::mem::replace( self, Self::new(other.id, self.ty, send, true), )); } } impl Drop for WidgetId { fn drop(&mut self) { if self.counter.drop() && !self.is_static.load(Ordering::Acquire) { let _ = self.send.send(self.id); } } } pub struct IdTag; pub struct IdFnTag; pub trait WidgetIdFn: FnOnce(&mut Ui) -> WidgetId {} impl WidgetId> WidgetIdFn for F {} impl WidgetLike for WidgetId { type Widget = W; fn add(self, _: &mut Ui) -> WidgetId { self } } impl WidgetId> WidgetLike for F { type Widget = W; fn add(self, ui: &mut Ui) -> WidgetId { self(ui) } } impl StaticWidgetId { pub fn to_id(&self, send: &Sender) -> WidgetId { WidgetId::new(self.id, self.ty, send.clone(), true) } pub fn any(self) -> StaticWidgetId { // SAFETY: self is repr(C) unsafe { std::mem::transmute(self) } } } impl WidgetLike for StaticWidgetId { type Widget = W; fn add(self, ui: &mut Ui) -> WidgetId { self.id(&ui.send) } } impl Clone for StaticWidgetId { fn clone(&self) -> Self { *self } } impl Copy for StaticWidgetId {} pub trait WidgetIdLike { fn id(self, send: &Sender) -> WidgetId; } impl WidgetIdLike for &WidgetId { fn id(self, _: &Sender) -> WidgetId { self.clone() } } impl WidgetIdLike for StaticWidgetId { fn id(self, send: &Sender) -> WidgetId { self.to_id(send) } } pub trait IdLike { fn id(&self) -> Id; } impl IdLike for WidgetId { fn id(&self) -> Id { self.id } } impl IdLike for StaticWidgetId { fn id(&self) -> Id { self.id } }