static ids

This commit is contained in:
2025-08-25 16:12:49 -04:00
parent 41103f2732
commit 7b21b0714d
4 changed files with 172 additions and 33 deletions

View File

@@ -2,7 +2,7 @@ use std::{any::TypeId, marker::PhantomData, sync::mpsc::Sender};
use crate::{
layout::{FnTag, Ui, Widget, WidgetLike, WidgetTag},
util::{Id, RefCounter},
util::{CopyId, Id, RefCounter},
};
pub struct AnyWidget;
@@ -13,12 +13,29 @@ pub struct AnyWidget;
///
/// 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<W = AnyWidget> {
pub(super) ty: TypeId,
pub(super) id: Id,
counter: RefCounter,
send: Sender<Id>,
is_static: bool,
_pd: PhantomData<W>,
}
/// 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.
pub struct StaticWidgetId<W = AnyWidget> {
pub(super) ty: TypeId,
pub(super) id: CopyId,
_pd: PhantomData<W>,
}
@@ -35,18 +52,20 @@ impl<W> Clone for WidgetId<W> {
ty: self.ty,
counter: self.counter.clone(),
send: self.send.clone(),
is_static: self.is_static,
_pd: PhantomData,
}
}
}
impl<W> WidgetId<W> {
pub(super) fn new(id: Id, ty: TypeId, send: Sender<Id>) -> Self {
pub(super) fn new(id: Id, ty: TypeId, send: Sender<Id>, is_static: bool) -> Self {
Self {
ty,
id,
counter: RefCounter::new(),
send,
is_static,
_pd: PhantomData,
}
}
@@ -68,11 +87,30 @@ impl<W> WidgetId<W> {
pub fn refs(&self) -> u32 {
self.counter.refs()
}
pub(super) fn into_static(self) -> StaticWidgetId<W> {
let s = std::mem::ManuallyDrop::new(self);
StaticWidgetId {
ty: s.ty,
id: s.id.copyable(),
_pd: PhantomData,
}
}
}
impl WidgetId {
pub fn set_static<W>(&mut self, other: StaticWidgetId<W>) {
let send = self.send.clone();
drop(std::mem::replace(
self,
Self::new(other.id.id(), self.ty, send, true),
));
}
}
impl<W> Drop for WidgetId<W> {
fn drop(&mut self) {
if self.counter.drop() {
if self.counter.drop() && !self.is_static {
let _ = self.send.send(self.id.duplicate());
}
}
@@ -108,6 +146,19 @@ pub trait Idable<Ctx, Tag> {
id
}
}
fn id_static<'a>(
self,
id: StaticWidgetId<Self::Widget>,
) -> WidgetIdFnRet!(Self::Widget, Ctx, 'a, Self, Ctx, Tag)
where
Self: Sized,
{
move |ui| {
let id = id.id(&ui.send);
self.set(ui, &id);
id
}
}
}
impl<W: Widget<Ctx>, Ctx> Idable<Ctx, WidgetTag> for W {
@@ -140,3 +191,40 @@ impl<W: 'static, F: FnOnce(&mut Ui<Ctx>) -> WidgetId<W>, Ctx> WidgetLike<Ctx, Id
self(ui)
}
}
impl<W> StaticWidgetId<W> {
pub fn to_id(&self, send: &Sender<Id>) -> WidgetId<W> {
WidgetId::new(self.id.id(), self.ty, send.clone(), true)
}
}
impl<W: 'static, Ctx> WidgetLike<Ctx, IdTag> for StaticWidgetId<W> {
type Widget = W;
fn add(self, ui: &mut Ui<Ctx>) -> WidgetId<W> {
self.id(&ui.send)
}
}
impl<W> Clone for StaticWidgetId<W> {
fn clone(&self) -> Self {
*self
}
}
impl<W> Copy for StaticWidgetId<W> {}
pub trait IdLike<W> {
fn id(self, send: &Sender<Id>) -> WidgetId<W>;
}
impl<W> IdLike<W> for &WidgetId<W> {
fn id(self, _: &Sender<Id>) -> WidgetId<W> {
self.clone()
}
}
impl<W> IdLike<W> for StaticWidgetId<W> {
fn id(self, send: &Sender<Id>) -> WidgetId<W> {
self.to_id(send)
}
}