142 lines
3.6 KiB
Rust
142 lines
3.6 KiB
Rust
use std::{any::TypeId, marker::PhantomData};
|
|
|
|
use crate::{
|
|
layout::{FnTag, Ui, UiMsg, UiMsgSender, Widget, WidgetLike, WidgetTag},
|
|
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.
|
|
#[repr(C)]
|
|
pub struct WidgetId<W = AnyWidget> {
|
|
pub(super) ty: TypeId,
|
|
pub(super) id: Id,
|
|
counter: RefCounter,
|
|
send: UiMsgSender,
|
|
_pd: PhantomData<W>,
|
|
}
|
|
|
|
impl<W> std::fmt::Debug for WidgetId<W> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
self.id.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl<W> Clone for WidgetId<W> {
|
|
fn clone(&self) -> Self {
|
|
Self {
|
|
id: self.id.duplicate(),
|
|
ty: self.ty,
|
|
counter: self.counter.clone(),
|
|
send: self.send.clone(),
|
|
_pd: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<W> WidgetId<W> {
|
|
pub(super) fn new(id: Id, ty: TypeId, send: UiMsgSender) -> Self {
|
|
Self {
|
|
ty,
|
|
id,
|
|
counter: RefCounter::new(),
|
|
send,
|
|
_pd: PhantomData,
|
|
}
|
|
}
|
|
|
|
pub fn erase_type(self) -> WidgetId<AnyWidget> {
|
|
self.cast_type()
|
|
}
|
|
|
|
pub fn as_any(&self) -> &WidgetId<AnyWidget> {
|
|
// safety: self is repr(C) and generic only used for phantom data
|
|
unsafe { std::mem::transmute(self) }
|
|
}
|
|
|
|
pub(super) fn cast_type<W2>(self) -> WidgetId<W2> {
|
|
// 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()
|
|
}
|
|
}
|
|
|
|
impl<W> Drop for WidgetId<W> {
|
|
fn drop(&mut self) {
|
|
if self.counter.drop() {
|
|
let _ = self.send.send(UiMsg::FreeWidget(self.id.duplicate()));
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct IdTag;
|
|
|
|
// pub trait WidgetIdFn<W, Ctx> = FnOnce(&mut Ui<Ctx>) -> WidgetId<W>;
|
|
macro_rules! WidgetIdFnRet {
|
|
($W:ty, $Ctx:ty) => {
|
|
impl FnOnce(&mut $crate::layout::Ui<$Ctx>) -> $crate::layout::WidgetId<$W>
|
|
};
|
|
($W:ty, $Ctx:ty, $($use:tt)*) => {
|
|
impl FnOnce(&mut $crate::layout::Ui<$Ctx>) -> $crate::layout::WidgetId<$W> + use<$($use)*>
|
|
};
|
|
}
|
|
pub(crate) use WidgetIdFnRet;
|
|
|
|
pub trait Idable<Ctx, Tag> {
|
|
type Widget: Widget<Ctx>;
|
|
fn set(self, ui: &mut Ui<Ctx>, id: &WidgetId<Self::Widget>);
|
|
fn id<'a>(
|
|
self,
|
|
id: &WidgetId<Self::Widget>,
|
|
) -> WidgetIdFnRet!(Self::Widget, Ctx, 'a, Self, Ctx, Tag)
|
|
where
|
|
Self: Sized,
|
|
{
|
|
let id = id.clone();
|
|
move |ui| {
|
|
self.set(ui, &id);
|
|
id
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<W: Widget<Ctx>, Ctx> Idable<Ctx, WidgetTag> for W {
|
|
type Widget = W;
|
|
|
|
fn set(self, ui: &mut Ui<Ctx>, id: &WidgetId<Self::Widget>) {
|
|
ui.set(id, self);
|
|
}
|
|
}
|
|
|
|
impl<F: FnOnce(&mut Ui<Ctx>) -> W, W: Widget<Ctx>, Ctx> Idable<Ctx, FnTag> for F {
|
|
type Widget = W;
|
|
|
|
fn set(self, ui: &mut Ui<Ctx>, id: &WidgetId<Self::Widget>) {
|
|
let w = self(ui);
|
|
ui.set(id, w);
|
|
}
|
|
}
|
|
|
|
impl<W: 'static, Ctx> WidgetLike<Ctx, FnTag> for WidgetId<W> {
|
|
type Widget = W;
|
|
fn add(self, _: &mut Ui<Ctx>) -> WidgetId<W> {
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<W: 'static, F: FnOnce(&mut Ui<Ctx>) -> WidgetId<W>, Ctx> WidgetLike<Ctx, IdTag> for F {
|
|
type Widget = W;
|
|
fn add(self, ui: &mut Ui<Ctx>) -> WidgetId<W> {
|
|
self(ui)
|
|
}
|
|
}
|