3 Commits

Author SHA1 Message Date
a6b36fdcca basically failed (too much code, slower (macros), no new features) 2025-08-10 18:52:40 -04:00
577d62b1da small stuff 2025-08-10 14:35:01 -04:00
b6e43c157b assoc types 2025-08-10 05:19:00 -04:00
7 changed files with 160 additions and 77 deletions

View File

@@ -2,7 +2,8 @@ use std::ops::Range;
use crate::{ use crate::{
primitive::{Axis, Painter, RoundedRectData, UIRegion}, primitive::{Axis, Painter, RoundedRectData, UIRegion},
UIColor, Widget, WidgetArrLike, WidgetFn, WidgetId, WidgetLike, ToId, UIColor, Widget, WidgetArrLike, WidgetFn, WidgetId, WidgetIdLikeTuple, WidgetLike,
WidgetLikeTuple,
}; };
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@@ -117,26 +118,29 @@ impl<T: UINum> From<T> for Padding {
} }
} }
pub trait WidgetUtil<W> { pub trait WidgetUtil {
fn pad(self, padding: impl Into<Padding>) -> impl WidgetLike<Regioned>; fn pad(self, padding: impl Into<Padding>) -> impl WidgetLike<Widget = Regioned>;
} }
impl<W: Widget, WL: WidgetLike<W>> WidgetUtil<W> for WL { impl<W: WidgetLike> WidgetUtil for W {
fn pad(self, padding: impl Into<Padding>) -> impl WidgetLike<Regioned> { fn pad(self, padding: impl Into<Padding>) -> impl WidgetLike<Widget = Regioned> {
WidgetFn(|ui| Regioned { WidgetFn(|ui| Regioned {
region: padding.into().region(), region: padding.into().region(),
inner: self.id(ui).erase_type(), inner: self.add(ui).erase_type(),
}) })
} }
} }
pub trait WidgetArrUtil<const LEN: usize, Ws> { pub trait WidgetArrUtil<const LEN: usize> {
fn span(self, axis: Axis, ratios: [impl UINum; LEN]) -> impl WidgetLike<Span>; fn span(self, axis: Axis, ratios: [impl UINum; LEN]) -> impl WidgetLike<Widget = Span>;
} }
impl<const LEN: usize, Ws, Wa: WidgetArrLike<LEN, Ws>> WidgetArrUtil<LEN, Ws> for Wa { impl<const LEN: usize, Wa: WidgetArrLike<LEN>> WidgetArrUtil<LEN> for Wa
fn span(self, axis: Axis, ratios: [impl UINum; LEN]) -> impl WidgetLike<Span> { where
WidgetFn(move |ui| Span::proportioned(axis, ratios, self.ui(ui).arr)) <Wa::Ws as WidgetLikeTuple<LEN>>::Wrap<ToId>: WidgetIdLikeTuple<LEN>,
{
fn span(self, axis: Axis, ratios: [impl UINum; LEN]) -> impl WidgetLike<Widget = Span> {
WidgetFn(move |ui| Span::proportioned(axis, ratios, self.ui(ui).erase_types()))
} }
} }

View File

@@ -32,18 +32,18 @@ impl From<UI> for UIBuilder {
impl UIBuilder { impl UIBuilder {
pub fn add<W: Widget>(&mut self, w: W) -> WidgetRef<W> { pub fn add<W: Widget>(&mut self, w: W) -> WidgetRef<W> {
WidgetRef::new(self.clone(), [self.push(w)]) WidgetRef::new(self.clone(), (self.push(w),))
} }
pub fn push<W: Widget>(&mut self, w: W) -> WidgetId { pub fn push<W: Widget>(&mut self, w: W) -> WidgetId<W> {
let mut ui = self.ui.borrow_mut(); let mut ui = self.ui.borrow_mut();
let id = ui.ids.next(); let id = ui.ids.next();
ui.widgets.insert(id.duplicate(), w); ui.widgets.insert(id.duplicate(), w);
WidgetId::new(id, TypeId::of::<W>()) WidgetId::new(id, TypeId::of::<W>())
} }
pub fn finish<WL: WidgetLike<W>, W>(mut self, base: WL) -> UI { pub fn finish<W: WidgetLike>(mut self, base: W) -> UI {
let base = base.id(&mut self).erase_type(); let base = base.add(&mut self).erase_type();
let mut ui = Rc::into_inner(self.ui).unwrap().into_inner(); let mut ui = Rc::into_inner(self.ui).unwrap().into_inner();
ui.base = Some(base); ui.base = Some(base);
ui ui

View File

@@ -3,7 +3,11 @@ use std::{
marker::PhantomData, marker::PhantomData,
}; };
use crate::{primitive::Painter, util::ID, UIBuilder}; use crate::{
primitive::Painter,
util::{impl_tuple, ID},
UIBuilder,
};
pub trait Widget: 'static + Any { pub trait Widget: 'static + Any {
fn draw(&self, painter: &mut Painter); fn draw(&self, painter: &mut Painter);
@@ -23,7 +27,7 @@ pub struct WidgetId<W = ()> {
} }
// TODO: temp // TODO: temp
impl Clone for WidgetId { impl<W: Widget> Clone for WidgetId<W> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
ty: self.ty, ty: self.ty,
@@ -54,104 +58,121 @@ impl<W> WidgetId<W> {
} }
} }
pub trait WidgetLike<W> { pub trait WidgetLike {
fn id(self, ui: &mut UIBuilder) -> WidgetId<W>; type Widget: Widget;
fn add(self, ui: &mut UIBuilder) -> WidgetId<Self::Widget>;
} }
/// wouldn't be needed if negative trait bounds & disjoint impls existed /// wouldn't be needed if negative trait bounds & disjoint impls existed
pub struct WidgetFn<F: FnOnce(&mut UIBuilder) -> W, W>(pub F); pub struct WidgetFn<F: FnOnce(&mut UIBuilder) -> W, W>(pub F);
impl<W: Widget, F: FnOnce(&mut UIBuilder) -> W> WidgetLike<W> for WidgetFn<F, W> { impl<W: Widget, F: FnOnce(&mut UIBuilder) -> W> WidgetLike for WidgetFn<F, W> {
fn id(self, ui: &mut UIBuilder) -> WidgetId<W> { type Widget = W;
fn add(self, ui: &mut UIBuilder) -> WidgetId<W> {
let w = (self.0)(ui); let w = (self.0)(ui);
ui.add(w).to_id() ui.add(w).to_id()
} }
} }
impl<W: Widget> WidgetLike<W> for W { impl<W: Widget> WidgetLike for W {
fn id(self, ui: &mut UIBuilder) -> WidgetId<W> { type Widget = W;
fn add(self, ui: &mut UIBuilder) -> WidgetId<W> {
ui.add(self).to_id() ui.add(self).to_id()
} }
} }
impl<W> WidgetLike<W> for WidgetId<W> { impl<W: Widget> WidgetLike for WidgetId<W> {
fn id(self, _: &mut UIBuilder) -> WidgetId<W> { type Widget = W;
fn add(self, _: &mut UIBuilder) -> WidgetId<W> {
self self
} }
} }
impl<W> WidgetLike<W> for WidgetArr<1, (W,)> { impl<W: Widget> WidgetLike for WidgetArr<1, (W,)> {
fn id(self, _: &mut UIBuilder) -> WidgetId<W> { type Widget = W;
let [id] = self.arr; fn add(self, _: &mut UIBuilder) -> WidgetId<W> {
id.cast_type() self.arr.0
} }
} }
pub struct WidgetArr<const LEN: usize, Ws> { pub struct WidgetArr<const LEN: usize, Ws: WidgetLikeTuple<LEN>> {
pub ui: UIBuilder, pub ui: UIBuilder,
pub arr: [WidgetId<()>; LEN], pub arr: Ws::Wrap<ToId>,
_pd: PhantomData<Ws>,
} }
impl<const LEN: usize, Ws> WidgetArr<LEN, Ws> { impl<const LEN: usize, Ws: WidgetLikeTuple<LEN>> WidgetArr<LEN, Ws>
pub fn new(ui: UIBuilder, arr: [WidgetId<()>; LEN]) -> Self { where
Self { Ws::Wrap<ToId>: WidgetIdLikeTuple<LEN>,
ui, {
arr, pub fn new(ui: UIBuilder, arr: Ws::Wrap<ToId>) -> Self {
_pd: PhantomData, Self { ui, arr }
} }
pub fn erase_types(self) -> [WidgetId; LEN] {
self.arr.map::<EraseId>(&mut ())
} }
} }
pub type WidgetRef<W> = WidgetArr<1, (W,)>; pub type WidgetRef<W> = WidgetArr<1, (W,)>;
impl<W> WidgetRef<W> { impl<W: WidgetLike> WidgetRef<W> {
pub fn handle(&self) -> WidgetId<W> { pub fn handle(&self) -> WidgetId<W::Widget> {
let [id] = &self.arr; self.arr.0.clone()
id.clone().cast_type()
} }
pub fn to_id(self) -> WidgetId<W> { pub fn to_id(self) -> WidgetId<W::Widget> {
let [id] = self.arr; self.arr.0
id.cast_type()
} }
} }
pub trait WidgetArrLike<const LEN: usize, Ws> { pub trait WidgetArrLike<const LEN: usize> {
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<LEN, Ws>; type Ws: WidgetLikeTuple<LEN>;
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<LEN, Self::Ws>;
} }
impl<const LEN: usize, Ws> WidgetArrLike<LEN, Ws> for WidgetArr<LEN, Ws> { impl<const LEN: usize, Ws: WidgetLikeTuple<LEN>> WidgetArrLike<LEN> for WidgetArr<LEN, Ws> {
type Ws = Ws;
fn ui(self, _: &mut UIBuilder) -> WidgetArr<LEN, Ws> { fn ui(self, _: &mut UIBuilder) -> WidgetArr<LEN, Ws> {
self self
} }
} }
// I hate this language it's so bad why do I even use it impl_tuple!(Widget);
macro_rules! impl_node_arr { impl_tuple!(WidgetLike);
($n:expr;$($T:tt)*) => { impl_tuple!(WidgetIdLike);
impl<$($T,${concat($T,$T)}: WidgetLike<$T>,)*> WidgetArrLike<$n, ($($T,)*)> for ($(${concat($T,$T)},)*) {
#[allow(unused_variables)] pub trait WidgetIdLike {
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<$n, ($($T,)*)> { fn erase_type(self) -> WidgetId;
#[allow(non_snake_case)]
let ($($T,)*) = self;
WidgetArr::new(
ui.clone(),
[$($T.id(ui).cast_type(),)*],
)
}
}
};
} }
impl_node_arr!(1;A); impl<W> WidgetIdLike for WidgetId<W> {
impl_node_arr!(2;A B); fn erase_type(self) -> WidgetId {
impl_node_arr!(3;A B C); self.erase_type()
impl_node_arr!(4;A B C D); }
impl_node_arr!(5;A B C D E); }
impl_node_arr!(6;A B C D E F);
impl_node_arr!(7;A B C D E F G); pub struct ToId;
impl_node_arr!(8;A B C D E F G H); impl WidgetLikeWrapper for ToId {
impl_node_arr!(9;A B C D E F G H I); type Wrap<T: WidgetLike> = WidgetId<T::Widget>;
impl_node_arr!(10;A B C D E F G H I J); type Ctx = UIBuilder;
impl_node_arr!(11;A B C D E F G H I J K); fn wrap<T: WidgetLike>(t: T, ctx: &mut Self::Ctx) -> Self::Wrap<T> {
impl_node_arr!(12;A B C D E F G H I J K L); t.add(ctx)
}
}
struct EraseId;
impl WidgetIdLikeMapper for EraseId {
type Map = WidgetId<()>;
type Ctx = ();
fn map<Id: WidgetIdLike>(t: Id, _: &mut Self::Ctx) -> Self::Map {
t.erase_type()
}
}
impl<T: WidgetLikeTuple<LEN>, const LEN: usize> WidgetArrLike<LEN> for T
where
T::Wrap<ToId>: WidgetIdLikeTuple<LEN>,
{
type Ws = T;
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<LEN, T> {
WidgetArr::new(ui.clone(), self.wrap::<ToId>(ui))
}
}

View File

@@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use app::App; use app::App;
use gui::{primitive::Axis, RoundedRect, UIColor, WidgetArrLike, WidgetArrUtil, WidgetUtil, UI}; use gui::{primitive::Axis, RoundedRect, UIColor, WidgetArrUtil, WidgetUtil, UI};
use render::Renderer; use render::Renderer;
use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::Window}; use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::Window};

View File

@@ -27,6 +27,8 @@ impl IDTracker {
id id
} }
// TODO: use this
#[allow(dead_code)]
pub fn free(&mut self, id: ID) { pub fn free(&mut self, id: ID) {
self.free.push(id); self.free.push(id);
} }

View File

@@ -1,3 +1,5 @@
mod id; mod id;
mod tuple;
pub use id::*; pub use id::*;
pub use tuple::*;

54
src/util/tuple.rs Normal file
View File

@@ -0,0 +1,54 @@
macro_rules! impl_tuple {
($Tuple:ident $Bound:ident $Wrapper:ident $Mapper:ident $n:expr;$TL:tt $($T:tt)*) => {
#[allow(non_snake_case)]
impl<$($T: $Bound,)* $TL: $Bound> $Tuple<$n> for ($($T,)* $TL,) {
type Wrap<W: $Wrapper> = ($(W::Wrap<$T>,)* W::Wrap<$TL>,);
type Map<M: $Mapper> = [M::Map; $n];
fn wrap<W: $Wrapper>(self, ctx: &mut W::Ctx) -> ($(W::Wrap<$T>,)* W::Wrap<$TL>,) {
let ($($T,)* $TL,) = self;
($(W::wrap($T, ctx),)* W::wrap($TL, ctx),)
}
fn map<M: $Mapper>(self, ctx: &mut M::Ctx) -> [M::Map; $n] {
let ($($T,)* $TL,) = self;
[$(M::map($T, ctx),)* M::map($TL, ctx)]
}
}
};
($Tuple:ident, $Bound:ident, $Wrapper:ident, $Mapper:ident) => {
pub trait $Wrapper {
type Wrap<T: $Bound>;
type Ctx;
fn wrap<T: $Bound>(t: T, ctx: &mut Self::Ctx) -> Self::Wrap<T>;
}
pub trait $Mapper {
type Map;
type Ctx;
fn map<T: $Bound>(t: T, ctx: &mut Self::Ctx) -> Self::Map;
}
pub trait $Tuple<const LEN: usize> {
type Wrap<W: $Wrapper>;
type Map<M: $Mapper>;
fn map<M: $Mapper>(self, ctx: &mut M::Ctx) -> [M::Map; LEN];
fn wrap<W: $Wrapper>(self, ctx: &mut W::Ctx) -> Self::Wrap<W>;
}
impl_tuple!($Tuple $Bound $Wrapper $Mapper 1;A);
impl_tuple!($Tuple $Bound $Wrapper $Mapper 2;A B);
impl_tuple!($Tuple $Bound $Wrapper $Mapper 3;A B C);
impl_tuple!($Tuple $Bound $Wrapper $Mapper 4;A B C D);
impl_tuple!($Tuple $Bound $Wrapper $Mapper 5;A B C D E);
impl_tuple!($Tuple $Bound $Wrapper $Mapper 6;A B C D E F);
// impl_tuple!($Tuple $Bound $Wrapper $Mapper 7;A B C D E F G);
// impl_tuple!($Tuple $Bound $Wrapper $Mapper 8;A B C D E F G H);
// impl_tuple!($Tuple $Bound $Wrapper $Mapper 9;A B C D E F G H I);
// impl_tuple!($Tuple $Bound $Wrapper $Mapper 10;A B C D E F G H I J);
// impl_tuple!($Tuple $Bound $Wrapper $Mapper 11;A B C D E F G H I J K);
// impl_tuple!($Tuple $Bound $Wrapper $Mapper 12;A B C D E F G H I J K L);
};
($Bound:ident) => {
impl_tuple!(${concat($Bound, Tuple)}, $Bound, ${concat($Bound, Wrapper)}, ${concat($Bound, Mapper)});
}
}
pub(crate) use impl_tuple;