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)
}
}

View File

@@ -2,8 +2,8 @@ use image::DynamicImage;
use crate::{
layout::{
ActiveSensors, Painter, SensorMap, TextData, TextureHandle, Textures, UiRegion, Vec2,
Widget, WidgetId, WidgetLike,
ActiveSensors, Painter, SensorMap, StaticWidgetId, TextData, TextureHandle, Textures,
UiRegion, Vec2, Widget, WidgetId, WidgetLike,
},
render::Primitives,
util::{HashMap, Id, IdTracker},
@@ -18,9 +18,9 @@ pub struct Ui<Ctx> {
base: Option<WidgetId>,
widgets: Widgets<Ctx>,
labels: HashMap<Id, String>,
updates: Vec<WidgetId>,
updates: Vec<Id>,
recv: Receiver<Id>,
send: Sender<Id>,
pub(super) send: Sender<Id>,
size: Vec2,
// TODO: make these non pub(crate)
pub(crate) primitives: Primitives,
@@ -46,6 +46,14 @@ impl<Ctx> Ui<Ctx> {
w.add(self)
}
pub fn add_static<W: Widget<Ctx>, Tag>(
&mut self,
w: impl WidgetLike<Ctx, Tag, Widget = W>,
) -> StaticWidgetId<W> {
let id = w.add(self);
id.into_static()
}
/// useful for debugging
pub fn set_label<W>(&mut self, id: &WidgetId<W>, label: String) {
self.labels.insert(id.id.duplicate(), label);
@@ -86,7 +94,17 @@ impl<Ctx> Ui<Ctx> {
}
pub fn id<W: Widget<Ctx>>(&mut self) -> WidgetId<W> {
WidgetId::new(self.widgets.reserve(), TypeId::of::<W>(), self.send.clone())
WidgetId::new(
self.widgets.reserve(),
TypeId::of::<W>(),
self.send.clone(),
false,
)
}
pub fn id_static<W: Widget<Ctx>>(&mut self) -> StaticWidgetId<W> {
let id = self.id();
id.into_static()
}
pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle {
@@ -148,7 +166,7 @@ impl<Ctx> Ui<Ctx> {
self.size,
);
for id in self.updates.drain(..) {
painter.redraw(&id.id);
painter.redraw(&id);
}
self.free();
}
@@ -181,11 +199,26 @@ impl<W: Widget<Ctx>, Ctx> Index<&WidgetId<W>> for Ui<Ctx> {
impl<W: Widget<Ctx>, Ctx> IndexMut<&WidgetId<W>> for Ui<Ctx> {
fn index_mut(&mut self, id: &WidgetId<W>) -> &mut Self::Output {
self.updates.push(id.clone().erase_type());
self.updates.push(id.id.duplicate());
self.get_mut(id).unwrap()
}
}
impl<W: Widget<Ctx>, Ctx> Index<StaticWidgetId<W>> for Ui<Ctx> {
type Output = W;
fn index(&self, id: StaticWidgetId<W>) -> &Self::Output {
self.widgets.get_static(&id).unwrap()
}
}
impl<W: Widget<Ctx>, Ctx> IndexMut<StaticWidgetId<W>> for Ui<Ctx> {
fn index_mut(&mut self, id: StaticWidgetId<W>) -> &mut Self::Output {
self.updates.push(id.id.id());
self.widgets.get_static_mut(&id).unwrap()
}
}
impl<Ctx> Widgets<Ctx> {
pub fn new() -> Self {
Self {
@@ -198,6 +231,18 @@ impl<Ctx> Widgets<Ctx> {
self.map.get(id).unwrap().as_ref()
}
pub fn get_static<W: Widget<Ctx>>(&self, id: &StaticWidgetId<W>) -> Option<&W> {
self.map.get(&id.id.id()).unwrap().as_any().downcast_ref()
}
pub fn get_static_mut<W: Widget<Ctx>>(&mut self, id: &StaticWidgetId<W>) -> Option<&mut W> {
self.map
.get_mut(&id.id.id())
.unwrap()
.as_any_mut()
.downcast_mut()
}
pub fn get<W: Widget<Ctx>>(&self, id: &WidgetId<W>) -> Option<&W> {
self.map.get(&id.id).unwrap().as_any().downcast_ref()
}