static ids
This commit is contained in:
@@ -2,7 +2,7 @@ use std::{any::TypeId, marker::PhantomData, sync::mpsc::Sender};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
layout::{FnTag, Ui, Widget, WidgetLike, WidgetTag},
|
layout::{FnTag, Ui, Widget, WidgetLike, WidgetTag},
|
||||||
util::{Id, RefCounter},
|
util::{CopyId, Id, RefCounter},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct AnyWidget;
|
pub struct AnyWidget;
|
||||||
@@ -13,12 +13,29 @@ pub struct AnyWidget;
|
|||||||
///
|
///
|
||||||
/// W does not need to implement widget so that AnyWidget is valid;
|
/// 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.
|
/// 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)]
|
#[repr(C)]
|
||||||
pub struct WidgetId<W = AnyWidget> {
|
pub struct WidgetId<W = AnyWidget> {
|
||||||
pub(super) ty: TypeId,
|
pub(super) ty: TypeId,
|
||||||
pub(super) id: Id,
|
pub(super) id: Id,
|
||||||
counter: RefCounter,
|
counter: RefCounter,
|
||||||
send: Sender<Id>,
|
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>,
|
_pd: PhantomData<W>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,18 +52,20 @@ impl<W> Clone for WidgetId<W> {
|
|||||||
ty: self.ty,
|
ty: self.ty,
|
||||||
counter: self.counter.clone(),
|
counter: self.counter.clone(),
|
||||||
send: self.send.clone(),
|
send: self.send.clone(),
|
||||||
|
is_static: self.is_static,
|
||||||
_pd: PhantomData,
|
_pd: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> WidgetId<W> {
|
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 {
|
Self {
|
||||||
ty,
|
ty,
|
||||||
id,
|
id,
|
||||||
counter: RefCounter::new(),
|
counter: RefCounter::new(),
|
||||||
send,
|
send,
|
||||||
|
is_static,
|
||||||
_pd: PhantomData,
|
_pd: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,11 +87,30 @@ impl<W> WidgetId<W> {
|
|||||||
pub fn refs(&self) -> u32 {
|
pub fn refs(&self) -> u32 {
|
||||||
self.counter.refs()
|
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> {
|
impl<W> Drop for WidgetId<W> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.counter.drop() {
|
if self.counter.drop() && !self.is_static {
|
||||||
let _ = self.send.send(self.id.duplicate());
|
let _ = self.send.send(self.id.duplicate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,6 +146,19 @@ pub trait Idable<Ctx, Tag> {
|
|||||||
id
|
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 {
|
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)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ use image::DynamicImage;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
layout::{
|
layout::{
|
||||||
ActiveSensors, Painter, SensorMap, TextData, TextureHandle, Textures, UiRegion, Vec2,
|
ActiveSensors, Painter, SensorMap, StaticWidgetId, TextData, TextureHandle, Textures,
|
||||||
Widget, WidgetId, WidgetLike,
|
UiRegion, Vec2, Widget, WidgetId, WidgetLike,
|
||||||
},
|
},
|
||||||
render::Primitives,
|
render::Primitives,
|
||||||
util::{HashMap, Id, IdTracker},
|
util::{HashMap, Id, IdTracker},
|
||||||
@@ -18,9 +18,9 @@ pub struct Ui<Ctx> {
|
|||||||
base: Option<WidgetId>,
|
base: Option<WidgetId>,
|
||||||
widgets: Widgets<Ctx>,
|
widgets: Widgets<Ctx>,
|
||||||
labels: HashMap<Id, String>,
|
labels: HashMap<Id, String>,
|
||||||
updates: Vec<WidgetId>,
|
updates: Vec<Id>,
|
||||||
recv: Receiver<Id>,
|
recv: Receiver<Id>,
|
||||||
send: Sender<Id>,
|
pub(super) send: Sender<Id>,
|
||||||
size: Vec2,
|
size: Vec2,
|
||||||
// TODO: make these non pub(crate)
|
// TODO: make these non pub(crate)
|
||||||
pub(crate) primitives: Primitives,
|
pub(crate) primitives: Primitives,
|
||||||
@@ -46,6 +46,14 @@ impl<Ctx> Ui<Ctx> {
|
|||||||
w.add(self)
|
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
|
/// useful for debugging
|
||||||
pub fn set_label<W>(&mut self, id: &WidgetId<W>, label: String) {
|
pub fn set_label<W>(&mut self, id: &WidgetId<W>, label: String) {
|
||||||
self.labels.insert(id.id.duplicate(), label);
|
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> {
|
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 {
|
pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle {
|
||||||
@@ -148,7 +166,7 @@ impl<Ctx> Ui<Ctx> {
|
|||||||
self.size,
|
self.size,
|
||||||
);
|
);
|
||||||
for id in self.updates.drain(..) {
|
for id in self.updates.drain(..) {
|
||||||
painter.redraw(&id.id);
|
painter.redraw(&id);
|
||||||
}
|
}
|
||||||
self.free();
|
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> {
|
impl<W: Widget<Ctx>, Ctx> IndexMut<&WidgetId<W>> for Ui<Ctx> {
|
||||||
fn index_mut(&mut self, id: &WidgetId<W>) -> &mut Self::Output {
|
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()
|
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> {
|
impl<Ctx> Widgets<Ctx> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -198,6 +231,18 @@ impl<Ctx> Widgets<Ctx> {
|
|||||||
self.map.get(id).unwrap().as_ref()
|
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> {
|
pub fn get<W: Widget<Ctx>>(&self, id: &WidgetId<W>) -> Option<&W> {
|
||||||
self.map.get(&id.id).unwrap().as_any().downcast_ref()
|
self.map.get(&id.id).unwrap().as_any().downcast_ref()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,14 +29,13 @@ pub struct ClientUi {
|
|||||||
impl Client {
|
impl Client {
|
||||||
pub fn create_ui() -> (Ui<Self>, ClientUi) {
|
pub fn create_ui() -> (Ui<Self>, ClientUi) {
|
||||||
let mut ui = Ui::new();
|
let mut ui = Ui::new();
|
||||||
let span_add = ui.id();
|
|
||||||
let rect = Rect {
|
let rect = Rect {
|
||||||
color: UiColor::WHITE,
|
color: UiColor::WHITE,
|
||||||
radius: 20.0,
|
radius: 20.0,
|
||||||
thickness: 0.0,
|
thickness: 0.0,
|
||||||
inner_radius: 0.0,
|
inner_radius: 0.0,
|
||||||
};
|
};
|
||||||
let pad_test = ui.add(
|
let pad_test = ui.add_static(
|
||||||
(
|
(
|
||||||
rect.color(UiColor::BLUE),
|
rect.color(UiColor::BLUE),
|
||||||
(
|
(
|
||||||
@@ -53,7 +52,7 @@ impl Client {
|
|||||||
)
|
)
|
||||||
.span(Dir::RIGHT, [1, 3]),
|
.span(Dir::RIGHT, [1, 3]),
|
||||||
);
|
);
|
||||||
let span_test = ui.add(
|
let span_test = ui.add_static(
|
||||||
(
|
(
|
||||||
rect.color(UiColor::GREEN),
|
rect.color(UiColor::GREEN),
|
||||||
rect.color(UiColor::ORANGE),
|
rect.color(UiColor::ORANGE),
|
||||||
@@ -74,20 +73,13 @@ impl Client {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
let span_add_test = ui.add(Span::empty(Dir::RIGHT).id(&span_add));
|
let span_add = ui.add_static(Span::empty(Dir::RIGHT));
|
||||||
let main: WidgetId<Regioned> = ui.id();
|
let main: StaticWidgetId<Regioned> = ui.id_static();
|
||||||
|
|
||||||
fn switch_button<To>(
|
let switch_button = |color, to, label| {
|
||||||
color: UiColor,
|
|
||||||
main: &WidgetId<Regioned>,
|
|
||||||
to: &WidgetId<To>,
|
|
||||||
label: impl Into<String>,
|
|
||||||
) -> impl WidgetLike<Client, FnTag> {
|
|
||||||
let main = main.clone();
|
|
||||||
let to = to.clone().erase_type();
|
|
||||||
let rect = Rect::new(color)
|
let rect = Rect::new(color)
|
||||||
.id_on(Sense::PressStart, move |id, ctx| {
|
.id_on(Sense::PressStart, move |id, ctx| {
|
||||||
ctx.ui[&main].inner = to.clone();
|
ctx.ui[main].inner.set_static(to);
|
||||||
ctx.ui[id].color = color.add_rgb(-0.2);
|
ctx.ui[id].color = color.add_rgb(-0.2);
|
||||||
})
|
})
|
||||||
.edit_on(Sense::HoverStart, move |r, _| {
|
.edit_on(Sense::HoverStart, move |r, _| {
|
||||||
@@ -100,17 +92,16 @@ impl Client {
|
|||||||
r.color = color;
|
r.color = color;
|
||||||
});
|
});
|
||||||
(rect, text(label).size(30)).stack()
|
(rect, text(label).size(30)).stack()
|
||||||
}
|
};
|
||||||
|
|
||||||
let tabs = ui.add(
|
let tabs = ui.add(
|
||||||
(
|
(
|
||||||
switch_button(UiColor::RED, &main, &pad_test, "pad test"),
|
switch_button(UiColor::RED, pad_test, "pad test"),
|
||||||
switch_button(UiColor::GREEN, &main, &span_test, "span test"),
|
switch_button(UiColor::GREEN, span_test, "span test"),
|
||||||
switch_button(UiColor::BLUE, &main, &span_add_test, "span add test"),
|
switch_button(UiColor::BLUE, span_add, "span add test"),
|
||||||
)
|
)
|
||||||
.span(Dir::RIGHT, [1, 1, 1]),
|
.span(Dir::RIGHT, [1, 1, 1]),
|
||||||
);
|
);
|
||||||
let s = span_add.clone();
|
|
||||||
let add_button = Rect::new(Color::LIME)
|
let add_button = Rect::new(Color::LIME)
|
||||||
.radius(30)
|
.radius(30)
|
||||||
.on(Sense::PressStart, move |ctx| {
|
.on(Sense::PressStart, move |ctx| {
|
||||||
@@ -118,7 +109,7 @@ impl Client {
|
|||||||
.ui
|
.ui
|
||||||
.add(image(include_bytes!("assets/sungals.png")))
|
.add(image(include_bytes!("assets/sungals.png")))
|
||||||
.erase_type();
|
.erase_type();
|
||||||
ctx.ui[&s].children.push((child, ratio(1)));
|
ctx.ui[span_add].children.push((child, ratio(1)));
|
||||||
})
|
})
|
||||||
.region(
|
.region(
|
||||||
UiPos::corner(Corner::BotRight)
|
UiPos::corner(Corner::BotRight)
|
||||||
@@ -126,11 +117,10 @@ impl Client {
|
|||||||
.shifted((-75, -75)),
|
.shifted((-75, -75)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let s = span_add.clone();
|
|
||||||
let del_button = Rect::new(Color::RED)
|
let del_button = Rect::new(Color::RED)
|
||||||
.radius(30)
|
.radius(30)
|
||||||
.on(Sense::PressStart, move |ctx| {
|
.on(Sense::PressStart, move |ctx| {
|
||||||
ctx.ui[&s].children.pop();
|
ctx.ui[span_add].children.pop();
|
||||||
})
|
})
|
||||||
.region(
|
.region(
|
||||||
UiPos::corner(Corner::BotLeft)
|
UiPos::corner(Corner::BotLeft)
|
||||||
@@ -148,7 +138,7 @@ impl Client {
|
|||||||
(
|
(
|
||||||
tabs.label("tabs"),
|
tabs.label("tabs"),
|
||||||
(
|
(
|
||||||
pad_test.pad(10).id(&main),
|
pad_test.pad(10).id_static(main),
|
||||||
add_button.label("add button"),
|
add_button.label("add button"),
|
||||||
del_button.label("del button"),
|
del_button.label("del button"),
|
||||||
info_sect.label("info sect"),
|
info_sect.label("info sect"),
|
||||||
|
|||||||
@@ -37,4 +37,20 @@ impl Id {
|
|||||||
pub fn duplicate(&self) -> Self {
|
pub fn duplicate(&self) -> Self {
|
||||||
Self(self.0)
|
Self(self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// this must be used carefully to make sure
|
||||||
|
/// all IDs are still valid references.
|
||||||
|
/// generally should not be used in "user" code
|
||||||
|
pub fn copyable(&self) -> CopyId {
|
||||||
|
CopyId(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, Hash, PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub struct CopyId(u64);
|
||||||
|
|
||||||
|
impl CopyId {
|
||||||
|
pub fn id(&self) -> Id {
|
||||||
|
Id(self.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user