learn how workspaces + proc macros work & restructure everything
This commit is contained in:
163
core/src/layout/widget/handle.rs
Normal file
163
core/src/layout/widget/handle.rs
Normal file
@@ -0,0 +1,163 @@
|
||||
use std::{
|
||||
cell::{Ref, RefMut},
|
||||
marker::Unsize,
|
||||
ops::CoerceUnsized,
|
||||
sync::mpsc::Sender,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
layout::{IdFnTag, IdTag, Ui, Widget, WidgetLike},
|
||||
util::{Handle, Id, WeakHandle},
|
||||
};
|
||||
|
||||
/// An handle for a widget in a UI.
|
||||
///
|
||||
/// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs?
|
||||
pub struct WidgetRef<W: ?Sized = dyn Widget>(Handle<Inner<W>>);
|
||||
pub struct WeakWidgetRef<W: ?Sized = dyn Widget>(WeakHandle<Inner<W>>);
|
||||
|
||||
struct Inner<W: ?Sized> {
|
||||
id: Id,
|
||||
send: Sender<WidgetUpdate>,
|
||||
label: String,
|
||||
widget: W,
|
||||
}
|
||||
|
||||
pub enum WidgetUpdate {
|
||||
Drop(Id),
|
||||
Mutate(Id),
|
||||
}
|
||||
|
||||
impl<W> PartialEq for WidgetRef<W> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id() == other.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> std::fmt::Debug for WidgetRef<W> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.id().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized> Clone for WidgetRef<W> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget> WidgetRef<W> {
|
||||
pub(super) fn new(id: Id, widget: W, send: Sender<WidgetUpdate>) -> Self {
|
||||
let mut label = std::any::type_name::<W>().to_string();
|
||||
if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) {
|
||||
label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1;
|
||||
}
|
||||
Self(
|
||||
Inner {
|
||||
widget,
|
||||
id,
|
||||
send,
|
||||
label,
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget + ?Sized + Unsize<dyn Widget>> WidgetRef<W> {
|
||||
pub fn any(self) -> WidgetRef<dyn Widget> {
|
||||
WidgetRef(self.0)
|
||||
}
|
||||
pub fn as_any(&self) -> WidgetRef<dyn Widget> {
|
||||
WidgetRef(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized> WidgetRef<W> {
|
||||
pub fn id(&self) -> Id {
|
||||
self.0.get().id
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Ref<'_, W> {
|
||||
Ref::map(self.0.get(), |i| &i.widget)
|
||||
}
|
||||
|
||||
pub fn get_mut(&self) -> RefMut<'_, W> {
|
||||
let inner = self.0.get_mut();
|
||||
let _ = inner.send.send(WidgetUpdate::Mutate(inner.id));
|
||||
RefMut::map(inner, |i| &mut i.widget)
|
||||
}
|
||||
|
||||
pub fn get_mut_quiet(&self) -> RefMut<'_, W> {
|
||||
RefMut::map(self.0.get_mut(), |i| &mut i.widget)
|
||||
}
|
||||
|
||||
pub fn get_label(&self) -> Ref<'_, String> {
|
||||
Ref::map(self.0.get(), |i| &i.label)
|
||||
}
|
||||
|
||||
pub fn set_label(&self, label: impl Into<String>) {
|
||||
self.0.get_mut().label = label.into();
|
||||
}
|
||||
|
||||
pub fn refs(&self) -> usize {
|
||||
self.0.refs()
|
||||
}
|
||||
|
||||
pub fn weak(&self) -> WeakWidgetRef<W> {
|
||||
WeakWidgetRef(self.0.weak())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized> WeakWidgetRef<W> {
|
||||
/// should guarantee that widget is still valid to prevent indexing failures
|
||||
pub(crate) fn expect_strong(&self) -> WidgetRef<W> {
|
||||
WidgetRef(self.0.strong().expect("widget should not be dropped"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget + ?Sized + Unsize<dyn Widget>> WeakWidgetRef<W> {
|
||||
pub fn any(self) -> WeakWidgetRef<dyn Widget> {
|
||||
WeakWidgetRef(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized> Drop for Inner<W> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.send.send(WidgetUpdate::Drop(self.id));
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WidgetIdFn<W: ?Sized = dyn Widget>: FnOnce(&mut Ui) -> WidgetRef<W> {}
|
||||
impl<W: ?Sized, F: FnOnce(&mut Ui) -> WidgetRef<W>> WidgetIdFn<W> for F {}
|
||||
|
||||
pub trait WidgetRet: FnOnce(&mut Ui) -> WidgetRef {}
|
||||
impl<F: FnOnce(&mut Ui) -> WidgetRef> WidgetRet for F {}
|
||||
|
||||
impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static> WidgetLike<IdTag> for WidgetRef<W> {
|
||||
type Widget = W;
|
||||
fn add(self, _: &mut Ui) -> WidgetRef<W> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static, F: FnOnce(&mut Ui) -> WidgetRef<W>>
|
||||
WidgetLike<IdFnTag> for F
|
||||
{
|
||||
type Widget = W;
|
||||
fn add(self, ui: &mut Ui) -> WidgetRef<W> {
|
||||
self(ui)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IdLike<W> {
|
||||
fn id(&self) -> Id;
|
||||
}
|
||||
|
||||
impl<W> IdLike<W> for WidgetRef<W> {
|
||||
fn id(&self) -> Id {
|
||||
self.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<WidgetRef<U>> for WidgetRef<W> {}
|
||||
79
core/src/layout/widget/like.rs
Normal file
79
core/src/layout/widget/like.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use super::*;
|
||||
use std::marker::Unsize;
|
||||
|
||||
pub trait WidgetLike<Tag> {
|
||||
type Widget: Widget + ?Sized + Unsize<dyn Widget> + 'static;
|
||||
|
||||
fn add(self, ui: &mut Ui) -> WidgetRef<Self::Widget>;
|
||||
|
||||
fn with_id<W2>(
|
||||
self,
|
||||
f: impl FnOnce(&mut Ui, WidgetRef<Self::Widget>) -> WidgetRef<W2>,
|
||||
) -> impl WidgetIdFn<W2>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
move |ui| {
|
||||
let id = self.add(ui);
|
||||
f(ui, id)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_root(self, ui: &mut Ui)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
ui.set_root(self);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WidgetArr<const LEN: usize> {
|
||||
pub arr: [WidgetRef; LEN],
|
||||
}
|
||||
|
||||
impl<const LEN: usize> WidgetArr<LEN> {
|
||||
pub fn new(arr: [WidgetRef; LEN]) -> Self {
|
||||
Self { arr }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WidgetArrLike<const LEN: usize, Tag> {
|
||||
fn ui(self, ui: &mut Ui) -> WidgetArr<LEN>;
|
||||
}
|
||||
|
||||
impl<const LEN: usize> WidgetArrLike<LEN, ArrTag> for WidgetArr<LEN> {
|
||||
fn ui(self, _: &mut Ui) -> WidgetArr<LEN> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// I hate this language it's so bad why do I even use it
|
||||
macro_rules! impl_widget_arr {
|
||||
($n:expr;$($W:ident)*) => {
|
||||
impl_widget_arr!($n;$($W)*;$(${concat($W,Tag)})*);
|
||||
};
|
||||
($n:expr;$($W:ident)*;$($Tag:ident)*) => {
|
||||
impl<$($W: WidgetLike<$Tag>,$Tag,)*> WidgetArrLike<$n, ($($Tag,)*)> for ($($W,)*) {
|
||||
fn ui(self, ui: &mut Ui) -> WidgetArr<$n> {
|
||||
#[allow(non_snake_case)]
|
||||
let ($($W,)*) = self;
|
||||
WidgetArr::new(
|
||||
[$($W.add(ui),)*],
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_widget_arr!(1;A);
|
||||
impl_widget_arr!(2;A B);
|
||||
impl_widget_arr!(3;A B C);
|
||||
impl_widget_arr!(4;A B C D);
|
||||
impl_widget_arr!(5;A B C D E);
|
||||
impl_widget_arr!(6;A B C D E F);
|
||||
impl_widget_arr!(7;A B C D E F G);
|
||||
impl_widget_arr!(8;A B C D E F G H);
|
||||
impl_widget_arr!(9;A B C D E F G H I);
|
||||
impl_widget_arr!(10;A B C D E F G H I J);
|
||||
impl_widget_arr!(11;A B C D E F G H I J K);
|
||||
impl_widget_arr!(12;A B C D E F G H I J K L);
|
||||
63
core/src/layout/widget/mod.rs
Normal file
63
core/src/layout/widget/mod.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
mod handle;
|
||||
mod like;
|
||||
mod tag;
|
||||
mod widgets;
|
||||
|
||||
pub use handle::*;
|
||||
pub use like::*;
|
||||
pub use tag::*;
|
||||
pub use widgets::*;
|
||||
|
||||
use crate::layout::{Len, Painter, SizeCtx, Ui};
|
||||
|
||||
pub trait Widget: 'static {
|
||||
fn draw(&mut self, painter: &mut Painter);
|
||||
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len;
|
||||
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len;
|
||||
}
|
||||
|
||||
impl Widget for () {
|
||||
fn draw(&mut self, _: &mut Painter) {}
|
||||
fn desired_width(&mut self, _: &mut SizeCtx) -> Len {
|
||||
Len::ZERO
|
||||
}
|
||||
fn desired_height(&mut self, _: &mut SizeCtx) -> Len {
|
||||
Len::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
/// A function that returns a widget given a UI.
|
||||
/// Useful for defining trait functions on widgets that create a parent widget so that the children
|
||||
/// don't need to be IDs yet
|
||||
pub trait WidgetFn<W: Widget + ?Sized>: FnOnce(&mut Ui) -> W {}
|
||||
impl<W: Widget, F: FnOnce(&mut Ui) -> W> WidgetFn<W> for F {}
|
||||
|
||||
impl<W: Widget, F: FnOnce(&mut Ui) -> W> WidgetLike<FnTag> for F {
|
||||
type Widget = W;
|
||||
fn add(self, ui: &mut Ui) -> WidgetRef<W> {
|
||||
self(ui).add(ui)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget> WidgetLike<WidgetTag> for W {
|
||||
type Widget = W;
|
||||
fn add(self, ui: &mut Ui) -> WidgetRef<W> {
|
||||
ui.add_widget(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WidgetOption {
|
||||
fn get(self, ui: &mut Ui) -> Option<WidgetRef>;
|
||||
}
|
||||
|
||||
impl WidgetOption for () {
|
||||
fn get(self, _: &mut Ui) -> Option<WidgetRef> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnOnce(&mut Ui) -> Option<WidgetRef>> WidgetOption for F {
|
||||
fn get(self, ui: &mut Ui) -> Option<WidgetRef> {
|
||||
self(ui)
|
||||
}
|
||||
}
|
||||
5
core/src/layout/widget/tag.rs
Normal file
5
core/src/layout/widget/tag.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub struct WidgetTag;
|
||||
pub struct FnTag;
|
||||
pub struct IdTag;
|
||||
pub struct IdFnTag;
|
||||
pub struct ArrTag;
|
||||
50
core/src/layout/widget/widgets.rs
Normal file
50
core/src/layout/widget/widgets.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use crate::{
|
||||
layout::{WeakWidgetRef, Widget, WidgetRef, WidgetUpdate},
|
||||
util::{HashMap, Id, IdTracker},
|
||||
};
|
||||
|
||||
pub struct Widgets {
|
||||
ids: IdTracker,
|
||||
map: HashMap<Id, WeakWidgetRef>,
|
||||
send: Sender<WidgetUpdate>,
|
||||
}
|
||||
|
||||
impl Widgets {
|
||||
pub fn new(send: Sender<WidgetUpdate>) -> Self {
|
||||
Self {
|
||||
ids: IdTracker::default(),
|
||||
map: HashMap::default(),
|
||||
send,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, id: Id) -> WidgetRef {
|
||||
self.map.get(&id).unwrap().expect_strong()
|
||||
}
|
||||
|
||||
pub fn insert<W: Widget>(&mut self, widget: W) -> WidgetRef<W> {
|
||||
let id = self.ids.next();
|
||||
let rf = WidgetRef::new(id, widget, self.send.clone());
|
||||
self.map.insert(id, rf.weak().any());
|
||||
rf
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, id: Id) {
|
||||
self.map.remove(&id);
|
||||
self.ids.free(id);
|
||||
}
|
||||
|
||||
pub fn reserve(&mut self) -> Id {
|
||||
self.ids.next()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.map.is_empty()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user