use std::{ any::{Any, TypeId}, marker::PhantomData, }; use crate::{Painter, Ui, util::Id}; pub trait Widget: Any { fn draw(&self, painter: &mut Painter); } pub struct AnyWidget; /// An identifier for a widget. /// Can index a UI to get the associated widget. /// /// 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)] #[derive(Eq, Hash, PartialEq)] pub struct WidgetId { pub(super) ty: TypeId, pub(super) id: Id, _pd: PhantomData, } impl std::fmt::Debug for WidgetId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.id.fmt(f) } } // TODO: temp impl Clone for WidgetId { fn clone(&self) -> Self { Self::new(self.id.duplicate(), self.ty) } } impl WidgetId { pub(super) fn new(id: Id, ty: TypeId) -> Self { Self { ty, id, _pd: PhantomData, } } pub fn erase_type(self) -> WidgetId { self.cast_type() } pub fn as_any(&self) -> &WidgetId { // safety: self is repr(C) and generic only used for phantom data unsafe { std::mem::transmute(self) } } fn cast_type(self) -> WidgetId { WidgetId { ty: self.ty, id: self.id, _pd: PhantomData, } } } pub struct WidgetTag; pub struct FnTag; pub struct IdTag; pub trait WidgetLike { type Widget: 'static; fn add(self, ui: &mut Ui) -> WidgetId; fn with_id( self, f: impl FnOnce(&mut Ui, WidgetId) -> WidgetId, ) -> impl WidgetIdFn where Self: Sized, { move |ui| { let id = self.add(ui); f(ui, id) } } } /// 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, Ctx> = FnOnce(&mut Ui) -> W; pub trait WidgetIdFn = FnOnce(&mut Ui) -> WidgetId; pub trait Idable { type Widget: Widget; fn set(self, ui: &mut Ui, id: &WidgetId); fn id<'a>( self, id: &WidgetId, ) -> impl WidgetIdFn + use<'a, Self, Ctx, Tag> where Self: Sized, { let id = id.clone(); move |ui| { self.set(ui, &id); id } } } impl, Ctx> Idable for W { type Widget = W; fn set(self, ui: &mut Ui, id: &WidgetId) { ui.set(id, self); } } impl) -> W, W: Widget, Ctx> Idable for F { type Widget = W; fn set(self, ui: &mut Ui, id: &WidgetId) { let w = self(ui); ui.set(id, w); } } impl, Ctx, F: FnOnce(&mut Ui) -> W> WidgetLike for F { type Widget = W; fn add(self, ui: &mut Ui) -> WidgetId { let w = self(ui); ui.add(w) } } impl) -> WidgetId, Ctx> WidgetLike for F { type Widget = W; fn add(self, ui: &mut Ui) -> WidgetId { self(ui) } } impl, Ctx> WidgetLike for W { type Widget = W; fn add(self, ui: &mut Ui) -> WidgetId { ui.add_widget(self) } } impl WidgetLike for WidgetId { type Widget = W; fn add(self, _: &mut Ui) -> WidgetId { self } } pub struct WidgetArr { pub arr: [WidgetId; LEN], _pd: PhantomData, } impl WidgetArr { pub fn new(arr: [WidgetId; LEN]) -> Self { Self { arr, _pd: PhantomData, } } } pub struct ArrTag; pub trait WidgetArrLike { type Ws; fn ui(self, ui: &mut Ui) -> WidgetArr; } impl WidgetArrLike for WidgetArr { type Ws = Ws; fn ui(self, _: &mut Ui) -> WidgetArr { self } } impl, Ctx> WidgetArrLike<1, Ctx, WidgetTag> for W { type Ws = (W::Widget,); fn ui(self, ui: &mut Ui) -> WidgetArr<1, (W::Widget,)> { WidgetArr::new([self.add(ui).erase_type()]) } } // 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,)* Ctx> WidgetArrLike<$n, Ctx, ($($Tag,)*)> for ($($W,)*) { type Ws = ($($W::Widget,)*); fn ui(self, ui: &mut Ui) -> WidgetArr<$n, ($($W::Widget,)*)> { #[allow(non_snake_case)] let ($($W,)*) = self; WidgetArr::new( [$($W.add(ui).cast_type(),)*], ) } } }; } 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);