From 1204e3728e140cb664457fde8abda2f031bba08a Mon Sep 17 00:00:00 2001 From: Shadow Cat Date: Thu, 28 Aug 2025 21:55:34 -0400 Subject: [PATCH] alignment!!! --- src/core/align.rs | 17 ++++++++++ src/core/mod.rs | 4 +++ src/core/sized.rs | 16 ++++++++++ src/core/span.rs | 4 +-- src/core/trait_fns.rs | 67 ++++++++++++++++++++++++++++++++------- src/layout/num.rs | 7 ++-- src/layout/orientation.rs | 49 +++++++++++++++++----------- src/layout/painter.rs | 2 +- src/layout/pos.rs | 46 ++++++++++++++++++--------- src/layout/vec2.rs | 9 ++++-- src/testing/mod.rs | 55 ++++++++++++++------------------ 11 files changed, 190 insertions(+), 86 deletions(-) create mode 100644 src/core/align.rs create mode 100644 src/core/sized.rs diff --git a/src/core/align.rs b/src/core/align.rs new file mode 100644 index 0000000..de1ecd0 --- /dev/null +++ b/src/core/align.rs @@ -0,0 +1,17 @@ +use crate::prelude::*; + +pub struct Aligned { + pub inner: WidgetId, + pub align: Align, +} + +impl Widget for Aligned { + fn draw(&mut self, painter: &mut Painter) { + let region = UiRegion::from_size_align(painter.size(&self.inner), self.align); + painter.draw_within(&self.inner, region); + } + + fn size(&mut self, ctx: &mut SizeCtx) -> Vec2 { + ctx.size(&self.inner) + } +} diff --git a/src/core/mod.rs b/src/core/mod.rs index b301b6c..b0f9fc2 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,16 +1,20 @@ +mod align; mod frame; mod image; mod rect; mod sense; +mod sized; mod span; mod stack; mod text; mod trait_fns; +pub use align::*; pub use frame::*; pub use image::*; pub use rect::*; pub use sense::*; +pub use sized::*; pub use span::*; pub use stack::*; pub use text::*; diff --git a/src/core/sized.rs b/src/core/sized.rs new file mode 100644 index 0000000..2663285 --- /dev/null +++ b/src/core/sized.rs @@ -0,0 +1,16 @@ +use crate::prelude::*; + +pub struct Sized { + pub inner: WidgetId, + pub size: Vec2, +} + +impl Widget for Sized { + fn draw(&mut self, painter: &mut Painter) { + painter.draw(&self.inner); + } + + fn size(&mut self, _: &mut SizeCtx) -> Vec2 { + self.size + } +} diff --git a/src/core/span.rs b/src/core/span.rs index 827ab20..159b0d9 100644 --- a/src/core/span.rs +++ b/src/core/span.rs @@ -33,9 +33,7 @@ impl Widget for Span { let offset = size.axis(self.dir.axis); start.offset += offset; *axis.bot_right.offset = start.offset; - child_region.bot_right.anchor = child_region.top_left.anchor; - let opposite = !self.dir.axis; - *child_region.bot_right.offset.axis_mut(opposite) += size.axis(opposite); + *axis.bot_right.anchor = *axis.top_left.anchor; } } if self.dir.sign == Sign::Neg { diff --git a/src/core/trait_fns.rs b/src/core/trait_fns.rs index 984f907..a17126d 100644 --- a/src/core/trait_fns.rs +++ b/src/core/trait_fns.rs @@ -1,13 +1,13 @@ use super::*; -use crate::layout::{ - Dir, UiPos, UiRegion, Vec2, WidgetArrLike, WidgetFnRet, WidgetIdFnRet, WidgetLike, -}; +use crate::prelude::*; pub trait CoreWidget { fn pad(self, padding: impl Into) -> WidgetFnRet!(Regioned); - fn center(self, size: impl Into) -> WidgetFnRet!(Regioned); + fn align(self, align: Align) -> WidgetFnRet!(Aligned); + fn center(self) -> WidgetFnRet!(Aligned); fn region(self, region: UiRegion) -> WidgetFnRet!(Regioned); fn label(self, label: impl Into) -> WidgetIdFnRet!(W); + fn sized(self, size: impl Into) -> WidgetFnRet!(Sized); } impl, Tag> CoreWidget for W { @@ -18,13 +18,17 @@ impl, Tag> CoreWidget for W { } } - fn center(self, size: impl Into) -> WidgetFnRet!(Regioned) { - |ui| Regioned { - region: UiPos::center().expand(size.into()), + fn align(self, align: Align) -> WidgetFnRet!(Aligned) { + move |ui| Aligned { inner: self.add(ui).erase_type(), + align, } } + fn center(self) -> WidgetFnRet!(Aligned) { + self.align(Align::Center) + } + fn region(self, region: UiRegion) -> WidgetFnRet!(Regioned) { move |ui| Regioned { region, @@ -39,19 +43,26 @@ impl, Tag> CoreWidget for W { id } } + + fn sized(self, size: impl Into) -> WidgetFnRet!(Sized) { + move |ui| Sized { + inner: self.add(ui).erase_type(), + size: size.into(), + } + } } pub trait CoreWidgetArr { - fn span(self, dir: Dir, lengths: [impl Into; LEN]) -> WidgetFnRet!(Span); + fn span(self, dir: Dir, lengths: impl IntoSpanLens) -> WidgetFnRet!(Span); fn stack(self) -> WidgetFnRet!(Stack); } impl, Tag> CoreWidgetArr for Wa { - fn span(self, dir: Dir, lengths: [impl Into; LEN]) -> WidgetFnRet!(Span) { - let lengths = lengths.map(Into::into); + fn span(self, dir: Dir, lengths: impl IntoSpanLens) -> WidgetFnRet!(Span) { + let lengths = lengths.into_lens(); move |ui| Span { - dir, children: self.ui(ui).arr.into_iter().zip(lengths).collect(), + dir, } } fn stack(self) -> WidgetFnRet!(Stack) { @@ -60,3 +71,37 @@ impl, Tag> CoreWidgetArr } } } + +// pub struct SpanBuilder> { +// children: Wa, +// dir: Dir, +// align: Align, +// } +// +// impl WidgetLike for SpanBuilder { +// type Widget = Span; +// +// fn add(self, ui: &mut Ui) -> WidgetId { +// ui.add_widget(Span { +// children: self.children, +// dir: self.dir, +// align: self.align, +// }) +// } +// } + +pub trait IntoSpanLens { + fn into_lens(self) -> [SpanLen; LEN]; +} + +impl> IntoSpanLens for [T; LEN] { + fn into_lens(self) -> [SpanLen; LEN] { + self.map(Into::into) + } +} + +impl IntoSpanLens for SpanLen { + fn into_lens(self) -> [SpanLen; LEN] { + [self; LEN] + } +} diff --git a/src/layout/num.rs b/src/layout/num.rs index d45f701..571f728 100644 --- a/src/layout/num.rs +++ b/src/layout/num.rs @@ -1,20 +1,21 @@ +#[const_trait] pub trait UiNum { fn to_f32(self) -> f32; } -impl UiNum for f32 { +impl const UiNum for f32 { fn to_f32(self) -> f32 { self } } -impl UiNum for u32 { +impl const UiNum for u32 { fn to_f32(self) -> f32 { self as f32 } } -impl UiNum for i32 { +impl const UiNum for i32 { fn to_f32(self) -> f32 { self as f32 } diff --git a/src/layout/orientation.rs b/src/layout/orientation.rs index 363be19..993c29e 100644 --- a/src/layout/orientation.rs +++ b/src/layout/orientation.rs @@ -2,25 +2,6 @@ use std::ops::Not; use crate::layout::{Vec2, vec2}; -#[derive(Clone, Copy, Debug)] -pub enum Corner { - TopLeft, - TopRight, - BotLeft, - BotRight, -} - -impl Corner { - pub const fn anchor(&self) -> Vec2 { - match self { - Corner::TopLeft => vec2(0.0, 0.0), - Corner::TopRight => vec2(1.0, 0.0), - Corner::BotLeft => vec2(0.0, 1.0), - Corner::BotRight => vec2(1.0, 1.0), - } - } -} - #[derive(Copy, Clone, Eq, PartialEq)] pub enum Axis { X, @@ -89,3 +70,33 @@ impl Vec2 { } } } + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Align { + TopLeft, + Top, + TopRight, + Left, + Center, + Right, + BotLeft, + Bot, + BotRight, +} + +impl Align { + pub const fn anchor(&self) -> Vec2 { + match self { + Self::TopLeft => vec2(0.0, 0.0), + Self::Top => vec2(0.5, 0.0), + Self::TopRight => vec2(1.0, 0.0), + Self::Left => vec2(0.0, 0.5), + Self::Center => vec2(0.5, 0.5), + Self::Right => vec2(1.0, 0.5), + Self::BotLeft => vec2(0.0, 1.0), + Self::Bot => vec2(0.5, 1.0), + Self::BotRight => vec2(1.0, 1.0), + } + } +} + diff --git a/src/layout/painter.rs b/src/layout/painter.rs index 52b2399..608cc06 100644 --- a/src/layout/painter.rs +++ b/src/layout/painter.rs @@ -156,7 +156,7 @@ impl<'a> Painter<'a> { .size(&mut self.size_ctx()) } - pub fn size_ctx(&mut self) -> SizeCtx { + pub fn size_ctx(&mut self) -> SizeCtx<'_> { SizeCtx { size: self.region().in_size(self.screen_size), text: self.text, diff --git a/src/layout/pos.rs b/src/layout/pos.rs index 916c07a..fa15270 100644 --- a/src/layout/pos.rs +++ b/src/layout/pos.rs @@ -1,5 +1,5 @@ use crate::{ - layout::{Axis, Corner, Vec2, vec2}, + layout::{Align, Axis, Vec2}, util::{F32Util, impl_op}, }; @@ -20,10 +20,6 @@ impl UiPos { } } - pub const fn center() -> Self { - Self::anchor(vec2(0.5, 0.5)) - } - pub const fn anchor(anchor: Vec2) -> Self { Self { anchor, @@ -38,10 +34,6 @@ impl UiPos { } } - pub const fn corner(corner: Corner) -> Self { - Self::anchor(corner.anchor()) - } - pub const fn shift(&mut self, offset: impl const Into) { self.offset += offset.into(); } @@ -90,6 +82,18 @@ impl UiPos { } } +impl const From for UiPos { + fn from(align: Align) -> Self { + Self::anchor(align.anchor()) + } +} + +impl Align { + pub fn pos(self) -> UiPos { + UiPos::from(self) + } +} + #[derive(Clone, Copy, Debug)] pub struct UIScalar { pub anchor: f32, @@ -137,8 +141,8 @@ pub struct UiRegion { impl UiRegion { pub const fn full() -> Self { Self { - top_left: UiPos::corner(Corner::TopLeft), - bot_right: UiPos::corner(Corner::BotRight), + top_left: Align::TopLeft.into(), + bot_right: Align::BotRight.into(), } } pub fn anchor(anchor: Vec2) -> Self { @@ -153,9 +157,6 @@ impl UiRegion { bot_right: self.bot_right.within(parent), } } - pub fn select(&mut self, inner: &Self) { - *self = inner.within(self); - } pub fn axis_mut(&mut self, axis: Axis) -> UIRegionAxisView<'_> { UIRegionAxisView { top_left: self.top_left.axis_mut(axis), @@ -188,12 +189,27 @@ impl UiRegion { } pub fn center(&self) -> UiPos { - UiPos::center().within(self) + Align::Center.pos().within(self) } pub fn in_size(&self, size: Vec2) -> Vec2 { self.bot_right.to_size(size) - self.top_left.to_size(size) } + + pub fn select_aligned(&self, size: Vec2, align: Align) -> Self { + Self::from_size_align(size, align).within(self) + } + + pub fn from_size_align(size: Vec2, align: Align) -> Self { + let mut top_left = UiPos::from(align); + top_left.offset -= size * align.anchor(); + let mut bot_right = UiPos::from(align); + bot_right.offset += size * (Vec2::ONE - align.anchor()); + Self { + top_left, + bot_right, + } + } } #[derive(Debug)] diff --git a/src/layout/vec2.rs b/src/layout/vec2.rs index f51b96e..82ea190 100644 --- a/src/layout/vec2.rs +++ b/src/layout/vec2.rs @@ -54,9 +54,12 @@ impl Vec2 { } } -impl const From for Vec2 { - fn from(v: f32) -> Self { - Self { x: v, y: v } +impl const From for Vec2 { + fn from(v: T) -> Self { + Self { + x: v.to_f32(), + y: v.to_f32(), + } } } diff --git a/src/testing/mod.rs b/src/testing/mod.rs index 1eb052d..2f26cab 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -39,7 +39,7 @@ impl Client { ( rect.color(Color::BLUE), ( - rect.color(Color::RED).center((100.0, 100.0)), + rect.color(Color::RED).sized((100.0, 100.0)), (rect.color(Color::ORANGE), rect.color(Color::LIME).pad(10.0)) .span(Dir::RIGHT, [1, 1]), rect.color(Color::YELLOW), @@ -91,62 +91,55 @@ impl Client { let text_test = ui.add_static( ( - text("this is a").size(30), - text("teeeeeeeest").size(30), - text("okkk\nokkkkkk!").size(30), + text("this is a").size(30).align(Align::Left), + text("teeeeeeeest").size(30).align(Align::Left), + text("okkk\nokkkkkk!").size(30).align(Align::Left), text("hmm").size(30), text("a").size(30), ( text("'").size(30).family(Family::Monospace), text("'").size(30).family(Family::Monospace), text(":gamer mode").size(30).family(Family::Monospace), + Rect::new(Color::BLUE).sized(100), ) - .span(Dir::RIGHT, [sized(); _]), + .span(Dir::RIGHT, sized()) + .center(), text("pretty cool right?").size(30), ) - .span(Dir::DOWN, [sized(); _]), + .span(Dir::DOWN, sized()), ); - let tabs = ui.add( - ( - switch_button(Color::RED, pad_test, "pad test"), - switch_button(Color::GREEN, span_test, "span test"), - switch_button(Color::BLUE, span_add, "span add test"), - switch_button(Color::MAGENTA, text_test, "text test"), - ) - .span(Dir::RIGHT, [1; _]), - ); + let tabs = ( + switch_button(Color::RED, pad_test, "pad test"), + switch_button(Color::GREEN, span_test, "span test"), + switch_button(Color::BLUE, span_add, "span add test"), + switch_button(Color::MAGENTA, text_test, "text test"), + ) + .span(Dir::RIGHT, ratio(1)); + let add_button = Rect::new(Color::LIME) .radius(30) .on(PRESS_START, move |ui| { let child = ui - .add(image(include_bytes!("assets/sungals.png"))) + .add(image(include_bytes!("assets/sungals.png")).center()) .erase_type(); ui[span_add].children.push((child, sized())); }) - .region( - UiPos::corner(Corner::BotRight) - .expand((150, 150)) - .shifted((-75, -75)), - ); + .sized(150) + .align(Align::BotRight); let del_button = Rect::new(Color::RED) .radius(30) .on(PRESS_START, move |ui| { ui[span_add].children.pop(); }) - .region( - UiPos::corner(Corner::BotLeft) - .expand((150, 150)) - .shifted((75, -75)), - ); + .sized(150) + .align(Align::BotLeft); let info = ui.add(text("")); - let info_sect = info.clone().region( - UiPos::corner(Corner::TopRight) - .expand((150, 150)) - .shifted((-75, 0)), - ); + let info_sect = info + .clone() + .region(Align::TopRight.pos().expand((150, 150)).shifted((-75, 0))); ui.set_base( ( tabs.label("tabs"),