1 Commits

Author SHA1 Message Date
14435de6a5 bruh 2025-08-10 05:07:17 -04:00
8 changed files with 190 additions and 80 deletions

View File

@@ -49,18 +49,20 @@ impl Widget for Span {
} }
impl Span { impl Span {
pub fn proportioned<const LEN: usize>( pub fn proportioned(
axis: Axis, axis: Axis,
ratios: [impl UINum; LEN], elements: impl IntoIterator<Item = (WidgetId, impl UINum)>,
elements: [WidgetId; LEN],
) -> Self { ) -> Self {
let ratios = ratios.map(|r| r.to_f32()); // TODO: update
let total: f32 = ratios.iter().sum(); let elements = elements
.into_iter()
.map(|(w, r)| (w, r.to_f32()))
.collect::<Vec<_>>();
let total: f32 = elements.iter().map(|(_, r)| r).sum();
let mut start = 0.0; let mut start = 0.0;
Self { Self {
elements: elements elements: elements
.into_iter() .into_iter()
.zip(ratios)
.map(|(e, r)| { .map(|(e, r)| {
let end = start + r / total; let end = start + r / total;
let res = (start..end, e); let res = (start..end, e);
@@ -118,11 +120,11 @@ impl<T: UINum> From<T> for Padding {
} }
pub trait WidgetUtil<W> { pub trait WidgetUtil<W> {
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<W::Widget> 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.id(ui).erase_type(),
@@ -130,13 +132,13 @@ impl<W: Widget, WL: WidgetLike<W>> WidgetUtil<W> for WL {
} }
} }
pub trait WidgetArrUtil<const LEN: usize, Ws> { pub trait WidgetArrUtil<Wa: WidgetArrLike> {
fn span(self, axis: Axis, ratios: [impl UINum; LEN]) -> impl WidgetLike<Span>; fn span(self, axis: Axis, ratios: [impl UINum; Wa::LEN]) -> impl WidgetLike<Widget = Span>;
} }
impl<const LEN: usize, Ws, Wa: WidgetArrLike<LEN, Ws>> WidgetArrUtil<LEN, Ws> for Wa { impl<Wa: WidgetArrLike> WidgetArrUtil<Wa> for Wa {
fn span(self, axis: Axis, ratios: [impl UINum; LEN]) -> impl WidgetLike<Span> { fn span(self, axis: Axis, ratios: [impl UINum; Wa::LEN]) -> impl WidgetLike<Widget = Span> {
WidgetFn(move |ui| Span::proportioned(axis, ratios, self.ui(ui).arr)) WidgetFn(move |ui| Span::proportioned(axis, self.ui(ui).ids().into_iter().zip(ratios)))
} }
} }

View File

@@ -32,17 +32,17 @@ 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<WL: WidgetLike>(mut self, base: WL) -> UI {
let base = base.id(&mut self).erase_type(); let base = base.id(&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);

View File

@@ -3,7 +3,11 @@ use std::{
marker::PhantomData, marker::PhantomData,
}; };
use crate::{primitive::Painter, util::ID, UIBuilder}; use crate::{
primitive::Painter,
util::{IntoTupleList, TupleList, 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> Clone for WidgetId<W> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
ty: self.ty, ty: self.ty,
@@ -54,104 +58,129 @@ impl<W> WidgetId<W> {
} }
} }
pub trait WidgetLike<W> { pub trait WidgetLike {
fn id(self, ui: &mut UIBuilder) -> WidgetId<W>; type Widget;
fn id(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> {
type Widget = W;
fn id(self, ui: &mut UIBuilder) -> WidgetId<W> { fn id(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 {
type Widget = W;
fn id(self, ui: &mut UIBuilder) -> WidgetId<W> { fn id(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> WidgetLike for WidgetId<W> {
type Widget = W;
fn id(self, _: &mut UIBuilder) -> WidgetId<W> { fn id(self, _: &mut UIBuilder) -> WidgetId<W> {
self self
} }
} }
impl<W> WidgetLike<W> for WidgetArr<1, (W,)> { pub struct WidgetArr<Ws: WidgetList> {
fn id(self, _: &mut UIBuilder) -> WidgetId<W> {
let [id] = self.arr;
id.cast_type()
}
}
pub struct WidgetArr<const LEN: usize, Ws> {
pub ui: UIBuilder, pub ui: UIBuilder,
pub arr: [WidgetId<()>; LEN], pub arr: Ws::Ids,
_pd: PhantomData<Ws>,
} }
impl<const LEN: usize, Ws> WidgetArr<LEN, Ws> { impl<Ws: WidgetList> WidgetArr<Ws> {
pub fn new(ui: UIBuilder, arr: [WidgetId<()>; LEN]) -> Self { pub fn new(ui: UIBuilder, arr: Ws::Ids) -> Self {
Self { Self { ui, arr }
ui,
arr,
_pd: PhantomData,
} }
pub fn ids(self) -> Vec<WidgetId> {
self.arr.all()
} }
} }
pub type WidgetRef<W> = WidgetArr<1, (W,)>; pub type WidgetRef<W> = WidgetArr<(W, ())>;
impl<W> WidgetRef<W> { impl<W> WidgetRef<W> {
pub fn handle(&self) -> WidgetId<W> { pub fn handle(&self) -> WidgetId<W> {
let [id] = &self.arr; self.arr.head().clone()
id.clone().cast_type()
} }
pub fn to_id(self) -> WidgetId<W> { pub fn to_id(self) -> WidgetId<W> {
let [id] = self.arr; self.arr.into_head()
id.cast_type()
} }
} }
pub trait WidgetArrLike<const LEN: usize, Ws> { impl<W> WidgetLike for WidgetRef<W> {
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<LEN, Ws>; type Widget = W;
fn id(self, _: &mut UIBuilder) -> WidgetId<W> {
self.arr.into_head()
}
} }
impl<const LEN: usize, Ws> WidgetArrLike<LEN, Ws> for WidgetArr<LEN, Ws> { pub trait WidgetArrLike {
fn ui(self, _: &mut UIBuilder) -> WidgetArr<LEN, Ws> { const LEN: usize;
type Ws: WidgetList;
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<Self::Ws>;
}
impl<Ws: WidgetList> WidgetArrLike for WidgetArr<Ws> {
const LEN: usize = Ws::LEN;
type Ws = Ws;
fn ui(self, _: &mut UIBuilder) -> WidgetArr<Ws> {
self self
} }
} }
// I hate this language it's so bad why do I even use it impl<W: WidgetLike> WidgetArrLike for (W, ())
macro_rules! impl_node_arr { where
($n:expr;$($T:tt)*) => { Self: TupleList,
impl<$($T,${concat($T,$T)}: WidgetLike<$T>,)*> WidgetArrLike<$n, ($($T,)*)> for ($(${concat($T,$T)},)*) { {
#[allow(unused_variables)] const LEN: usize = <Self as TupleList>::LEN;
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<$n, ($($T,)*)> { type Ws = (W::Widget, ());
#[allow(non_snake_case)] fn ui(self, ui: &mut UIBuilder) -> WidgetArr<(W::Widget, ())> {
let ($($T,)*) = self; WidgetArr::new(ui.clone(), (self.0.id(ui), ()))
WidgetArr::new(
ui.clone(),
[$($T.id(ui).cast_type(),)*],
)
} }
}
impl<W: WidgetLike, T: WidgetArrLike> WidgetArrLike for (W, T)
where
Self: TupleList,
{
const LEN: usize = <Self as TupleList>::LEN;
type Ws = (W::Widget, T::Ws);
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<(W::Widget, T::Ws)> {
WidgetArr::new(ui.clone(), (self.0.id(ui), self.1.ui(ui).arr))
} }
};
} }
impl_node_arr!(1;A); pub trait WidgetList: TupleList {
impl_node_arr!(2;A B); type Ids: IdList;
impl_node_arr!(3;A B C); }
impl_node_arr!(4;A B C D); impl<H> WidgetList for (H, ()) {
impl_node_arr!(5;A B C D E); type Ids = (WidgetId<H>, ());
impl_node_arr!(6;A B C D E F); }
impl_node_arr!(7;A B C D E F G); impl<H, T: WidgetList> WidgetList for (H, T) {
impl_node_arr!(8;A B C D E F G H); type Ids = (WidgetId<H>, T::Ids);
impl_node_arr!(9;A B C D E F G H I); }
impl_node_arr!(10;A B C D E F G H I J);
impl_node_arr!(11;A B C D E F G H I J K); pub trait IdList: TupleList {
impl_node_arr!(12;A B C D E F G H I J K L); fn iter(self) -> impl Iterator<Item = WidgetId>;
fn all(self) -> Vec<WidgetId>
where
Self: Sized,
{
self.iter().collect::<Vec<_>>()
}
}
impl<H> IdList for (WidgetId<H>, ()) {
fn iter(self) -> impl Iterator<Item = WidgetId> {
core::iter::once(self.0.erase_type())
}
}
impl<H, T: IdList> IdList for (WidgetId<H>, T) {
fn iter(self) -> impl Iterator<Item = WidgetId> {
core::iter::once(self.0.erase_type()).chain(self.1.iter())
}
}

View File

@@ -3,10 +3,11 @@
#![feature(const_trait_impl)] #![feature(const_trait_impl)]
#![feature(const_from)] #![feature(const_from)]
#![feature(trait_alias)] #![feature(trait_alias)]
#![feature(generic_const_exprs)]
mod layout; mod layout;
mod render; mod render;
mod util; pub mod util;
mod base; mod base;
pub use layout::*; pub use layout::*;

View File

@@ -1,7 +1,8 @@
use gui::util::IntoTupleList;
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};
@@ -38,15 +39,19 @@ impl Client {
rect.color(UIColor::ORANGE), rect.color(UIColor::ORANGE),
rect.color(UIColor::LIME).pad(10.0), rect.color(UIColor::LIME).pad(10.0),
) )
.span(Axis::Y, [1, 1]), .into_list()
.span(Axis::Y, [1, 1].into()),
rect.color(UIColor::YELLOW), rect.color(UIColor::YELLOW),
) )
.into_list()
.span(Axis::X, [2, 2, 1]) .span(Axis::X, [2, 2, 1])
.pad(10), .pad(10),
) )
.into_list()
.span(Axis::X, [1, 3]), .span(Axis::X, [1, 3]),
rect.color(UIColor::GREEN), rect.color(UIColor::GREEN),
) )
.into_list()
.span(Axis::Y, [3, 1]) .span(Axis::Y, [3, 1])
.pad(10), .pad(10),
); );

View File

@@ -27,6 +27,7 @@ impl IDTracker {
id id
} }
#[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::*;

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

@@ -0,0 +1,70 @@
pub trait TupleList {
const LEN: usize;
type Head;
type Tail;
fn into_head(self) -> Self::Head;
fn head(&self) -> &Self::Head;
fn split(self) -> (Self::Head, Self::Tail);
}
impl TupleList for () {
const LEN: usize = 0;
type Head = ();
type Tail = ();
fn head(&self) -> &Self::Head {
&()
}
fn into_head(self) -> Self::Head {}
fn split(self) -> (Self::Head, Self::Tail) {
((), ())
}
}
impl<T, Rest: TupleList> TupleList for (T, Rest) {
const LEN: usize = Rest::LEN + 1;
type Head = T;
type Tail = Rest;
fn head(&self) -> &Self::Head {
&self.0
}
fn into_head(self) -> Self::Head {
self.0
}
fn split(self) -> (Self::Head, Self::Tail) {
(self.0, self.1)
}
}
pub trait IntoTupleList {
type List;
fn into_list(self) -> Self::List;
}
impl IntoTupleList for () {
type List = ();
fn into_list(self) -> Self::List {}
}
macro_rules! impl_tuple {
($H:tt $($T:tt)*) => {
impl<$H, $($T,)*> IntoTupleList for ($H, $($T,)*) {
type List = ($H, <($($T,)*) as IntoTupleList>::List);
fn into_list(self) -> Self::List {
#[allow(non_snake_case)]
let ($H, $($T,)*) = self;
($H, ($($T,)*).into_list())
}
}
};
}
impl_tuple!(A);
impl_tuple!(A B);
impl_tuple!(A B C);
impl_tuple!(A B C D);
impl_tuple!(A B C D E);
impl_tuple!(A B C D E F);
impl_tuple!(A B C D E F G);
impl_tuple!(A B C D E F G H);
impl_tuple!(A B C D E F G H I);
impl_tuple!(A B C D E F G H I J);
impl_tuple!(A B C D E F G H I J K);
impl_tuple!(A B C D E F G H I J K L);