From 6251c23d3753e4e7d3064b62fe2840744734649d Mon Sep 17 00:00:00 2001 From: shadow cat Date: Thu, 20 Nov 2025 15:44:39 -0500 Subject: [PATCH] the great orientation refactor (move to x & y UiScalars/Spans) + don't call full size in align --- src/core/position/align.rs | 28 +- src/core/position/offset.rs | 2 +- src/core/position/pad.rs | 18 +- src/core/position/sized.rs | 10 +- src/core/position/span.rs | 40 +- src/core/text/build.rs | 4 +- src/core/text/edit.rs | 12 +- src/core/text/mod.rs | 10 +- src/core/trait_fns.rs | 8 +- src/layout/mod.rs | 2 - src/layout/orientation/align.rs | 196 ++++++++ src/layout/orientation/axis.rs | 70 +++ .../{orientation.rs => orientation/len.rs} | 111 +---- src/layout/orientation/mod.rs | 11 + src/layout/orientation/pos.rs | 449 ++++++++++++++++++ src/layout/painter.rs | 12 +- src/layout/pos.rs | 382 --------------- src/layout/text.rs | 6 +- src/render/shader.wgsl | 17 +- src/testing/mod.rs | 22 +- 20 files changed, 832 insertions(+), 578 deletions(-) create mode 100644 src/layout/orientation/align.rs create mode 100644 src/layout/orientation/axis.rs rename src/layout/{orientation.rs => orientation/len.rs} (60%) create mode 100644 src/layout/orientation/mod.rs create mode 100644 src/layout/orientation/pos.rs delete mode 100644 src/layout/pos.rs diff --git a/src/core/position/align.rs b/src/core/position/align.rs index 28ed7df..13fe98c 100644 --- a/src/core/position/align.rs +++ b/src/core/position/align.rs @@ -7,8 +7,32 @@ pub struct Aligned { impl Widget for Aligned { fn draw(&mut self, painter: &mut Painter) { - let region = - UiRegion::from_ui_size_align(painter.size(&self.inner).to_uivec2(), self.align); + let region = match self.align { + Align { + x: Some(x), + y: Some(y), + } => { + painter + .size(&self.inner) + .to_uivec2() + .align(RegionAlign { x, y }) + } + Align { + x: Some(x), + y: None, + } => { + let x = painter.size_ctx().width(&self.inner).apply_rest().align(x); + UiRegion::new(x, UiSpan::FULL) + } + Align { + x: None, + y: Some(y), + } => { + let y = painter.size_ctx().height(&self.inner).apply_rest().align(y); + UiRegion::new(UiSpan::FULL, y) + } + Align { x: None, y: None } => UiRegion::FULL, + }; painter.widget_within(&self.inner, region); } diff --git a/src/core/position/offset.rs b/src/core/position/offset.rs index 32286f2..6b49f83 100644 --- a/src/core/position/offset.rs +++ b/src/core/position/offset.rs @@ -7,7 +7,7 @@ pub struct Offset { impl Widget for Offset { fn draw(&mut self, painter: &mut Painter) { - let region = UiRegion::full().offset(self.amt); + let region = UiRegion::FULL.offset(self.amt); painter.widget_within(&self.inner, region); } diff --git a/src/core/position/pad.rs b/src/core/position/pad.rs index 276c4df..1b921fd 100644 --- a/src/core/position/pad.rs +++ b/src/core/position/pad.rs @@ -13,8 +13,8 @@ impl Widget for Pad { fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { let width = self.padding.left + self.padding.right; let height = self.padding.top + self.padding.bottom; - ctx.outer.abs.x -= width; - ctx.outer.abs.y -= height; + ctx.outer.x.abs -= width; + ctx.outer.y.abs -= height; let mut size = ctx.width(&self.inner); size.abs += width; size @@ -23,8 +23,8 @@ impl Widget for Pad { fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { let width = self.padding.left + self.padding.right; let height = self.padding.top + self.padding.bottom; - ctx.outer.abs.x -= width; - ctx.outer.abs.y -= height; + ctx.outer.x.abs -= width; + ctx.outer.y.abs -= height; let mut size = ctx.height(&self.inner); size.abs += height; size @@ -49,11 +49,11 @@ impl Padding { } } pub fn region(&self) -> UiRegion { - let mut region = UiRegion::full(); - region.top_left.abs.x += self.left; - region.top_left.abs.y += self.top; - region.bot_right.abs.x -= self.right; - region.bot_right.abs.y -= self.bottom; + let mut region = UiRegion::FULL; + region.x.start.abs += self.left; + region.y.start.abs += self.top; + region.x.end.abs -= self.right; + region.y.end.abs -= self.bottom; region } pub fn x(amt: impl UiNum) -> Self { diff --git a/src/core/position/sized.rs b/src/core/position/sized.rs index 706c8a5..567178f 100644 --- a/src/core/position/sized.rs +++ b/src/core/position/sized.rs @@ -9,16 +9,10 @@ pub struct Sized { impl Sized { fn apply_to_outer(&self, ctx: &mut SizeCtx) { if let Some(x) = self.x { - let outer = ctx.outer.axis(Axis::X); - ctx.outer - .axis_mut(Axis::X) - .set(x.apply_rest().within_len(outer)); + ctx.outer.x.select_len(x.apply_rest()); } if let Some(y) = self.y { - let outer = ctx.outer.axis(Axis::Y); - ctx.outer - .axis_mut(Axis::Y) - .set(y.apply_rest().within_len(outer)); + ctx.outer.y.select_len(y.apply_rest()); } } } diff --git a/src/core/position/span.rs b/src/core/position/span.rs index 535db00..eb3de80 100644 --- a/src/core/position/span.rs +++ b/src/core/position/span.rs @@ -1,6 +1,5 @@ -use std::marker::PhantomData; - use crate::prelude::*; +use std::marker::PhantomData; pub struct Span { pub children: Vec, @@ -13,18 +12,19 @@ impl Widget for Span { let total = self.len_sum(&mut painter.size_ctx()); let mut start = UiScalar::rel_min(); for child in &self.children { - let mut child_region = UiRegion::full(); - let mut axis = child_region.axis_mut(self.dir.axis); - axis.top_left.set(start); + let mut span = UiSpan::FULL; + span.start = start; let len = painter.len_axis(child, self.dir.axis); if len.rest > 0.0 { let offset = UiScalar::new(total.rel, total.abs); - let rel_end = UiScalar::from_anchor(len.rest / total.rest); - start = rel_end.within(start, (UiScalar::rel_max() + start) - offset); + let rel_end = UiScalar::rel(len.rest / total.rest); + let end = (UiScalar::rel_max() + start) - offset; + start = rel_end.within(&start.to(end)); } start.abs += len.abs; start.rel += len.rel; - axis.bot_right.set(start); + span.end = start; + let mut child_region = UiRegion::from_axis(self.dir.axis, span, UiSpan::FULL); if self.dir.sign == Sign::Neg { child_region.flip(self.dir.axis); } @@ -89,33 +89,29 @@ impl Span { fn desired_ortho(&mut self, ctx: &mut SizeCtx) -> Len { // this is an awful hack to get text wrapping to work properly when in a downward span + let outer = ctx.outer.axis(self.dir.axis); if self.dir.axis == Axis::X { // so....... this literally copies draw so that the lengths are correctly set in the // context, which makes this slow and not cool let total = self.len_sum(ctx); let mut start = UiScalar::rel_min(); - let outer = ctx.outer.axis(self.dir.axis); let mut ortho_len = Len::ZERO; for child in &self.children { - let mut child_region = UiRegion::full(); - let mut axis = child_region.axis_mut(self.dir.axis); - axis.top_left.set(start); + let mut span = UiSpan::FULL; + span.start = start; let len = ctx.len_axis(child, self.dir.axis); if len.rest > 0.0 { let offset = UiScalar::new(total.rel, total.abs); - let rel_end = UiScalar::from_anchor(len.rest / total.rest); - start = rel_end.within(start, (UiScalar::rel_max() + start) - offset); + let rel_end = UiScalar::rel(len.rest / total.rest); + let end = (UiScalar::rel_max() + start) - offset; + start = rel_end.within(&start.to(end)); } start.abs += len.abs; start.rel += len.rel; - axis.bot_right.set(start); - // if self.dir.sign == Sign::Neg { - // child_region.flip(self.dir.axis); - // } - let scalar = child_region.size().axis(self.dir.axis); - ctx.outer - .axis_mut(self.dir.axis) - .set(scalar.within_len(outer)); + span.end = start; + + let scalar = span.len(); + *ctx.outer.axis_mut(self.dir.axis) = outer.select_len(scalar); let ortho = ctx.len_axis(child, !self.dir.axis); // TODO: rel shouldn't do this, but no easy way before actually calculating pixels if ortho.rel > 0.0 || ortho.rest > 0.0 { diff --git a/src/core/text/build.rs b/src/core/text/build.rs index 9c8726a..778494a 100644 --- a/src/core/text/build.rs +++ b/src/core/text/build.rs @@ -32,8 +32,8 @@ impl TextBuilder { self.attrs.line_height = height; self } - pub fn text_align(mut self, align: Align) -> Self { - self.attrs.align = align; + pub fn text_align(mut self, align: impl Into) -> Self { + self.attrs.align = align.into(); self } pub fn wrap(mut self, wrap: bool) -> Self { diff --git a/src/core/text/edit.rs b/src/core/text/edit.rs index 8a28c8c..340f7e5 100644 --- a/src/core/text/edit.rs +++ b/src/core/text/edit.rs @@ -15,10 +15,10 @@ pub struct TextEdit { impl TextEdit { pub fn region(&self) -> UiRegion { - UiRegion::from_size_align( - self.tex().map(|t| t.size()).unwrap_or(Vec2::ZERO), - self.align, - ) + self.tex() + .map(|t| t.size()) + .unwrap_or(Vec2::ZERO) + .align(self.align) } pub fn content(&self) -> String { @@ -44,7 +44,7 @@ impl Widget for TextEdit { let size = vec2(1, self.attrs.line_height); painter.primitive_within( RectPrimitive::color(Color::WHITE), - UiRegion::from_size_align(size, Align::TopLeft) + size.align(Align::TOP_LEFT) .offset(offset) .within(®ion), ); @@ -205,7 +205,7 @@ impl<'a> TextEditCtx<'a> { } pub fn select(&mut self, pos: Vec2, size: Vec2) { - let pos = pos - self.text.region().top_left.to_abs(size); + let pos = pos - self.text.region().top_left().to_abs(size); self.text.cursor = self.text.buf.hit(pos.x, pos.y); } diff --git a/src/core/text/mod.rs b/src/core/text/mod.rs index 13711a8..168fcc8 100644 --- a/src/core/text/mod.rs +++ b/src/core/text/mod.rs @@ -100,11 +100,13 @@ impl Widget for Text { } } -pub fn text_region(tex: &TextTexture, align: Align) -> UiRegion { +pub fn text_region(tex: &TextTexture, align: RegionAlign) -> UiRegion { let tex_dims = tex.handle.size(); - let mut region = UiRegion::from_size_align(tex.size(), align); - region.top_left.abs += tex.top_left; - region.bot_right.abs = region.top_left.abs + tex_dims; + let mut region = tex.size().align(align); + region.x.start.abs += tex.top_left.x; + region.y.start.abs += tex.top_left.y; + region.x.end.abs = region.x.start.abs + tex_dims.x; + region.y.end.abs = region.y.start.abs + tex_dims.y; region } diff --git a/src/core/trait_fns.rs b/src/core/trait_fns.rs index a75e6ac..517f4d4 100644 --- a/src/core/trait_fns.rs +++ b/src/core/trait_fns.rs @@ -3,7 +3,7 @@ use crate::prelude::*; pub trait CoreWidget { fn pad(self, padding: impl Into) -> impl WidgetFn; - fn align(self, align: Align) -> impl WidgetFn; + fn align(self, align: impl Into) -> impl WidgetFn; fn center(self) -> impl WidgetFn; fn label(self, label: impl Into) -> impl WidgetIdFn; fn sized(self, size: impl Into) -> impl WidgetFn; @@ -24,15 +24,15 @@ impl, Tag> CoreWidget for W { } } - fn align(self, align: Align) -> impl WidgetFn { + fn align(self, align: impl Into) -> impl WidgetFn { move |ui| Aligned { inner: self.add(ui).any(), - align, + align: align.into(), } } fn center(self) -> impl WidgetFn { - self.align(Align::Center) + self.align(Align::CENTER) } fn label(self, label: impl Into) -> impl WidgetIdFn { diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 16f63fb..652ece3 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -6,7 +6,6 @@ mod module; mod num; mod orientation; mod painter; -mod pos; mod text; mod texture; mod ui; @@ -22,7 +21,6 @@ pub use module::*; pub use num::*; pub use orientation::*; pub use painter::*; -pub use pos::*; pub use text::*; pub use texture::*; pub use ui::*; diff --git a/src/layout/orientation/align.rs b/src/layout/orientation/align.rs new file mode 100644 index 0000000..adc7fc4 --- /dev/null +++ b/src/layout/orientation/align.rs @@ -0,0 +1,196 @@ +use crate::layout::vec2; + +use super::*; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct Align { + pub x: Option, + pub y: Option, +} + +impl Align { + pub const TOP_LEFT: RegionAlign = RegionAlign::TOP_LEFT; + pub const TOP_CENTER: RegionAlign = RegionAlign::TOP_CENTER; + pub const TOP_RIGHT: RegionAlign = RegionAlign::TOP_RIGHT; + pub const CENTER_LEFT: RegionAlign = RegionAlign::CENTER_LEFT; + pub const CENTER: RegionAlign = RegionAlign::CENTER; + pub const CENTER_RIGHT: RegionAlign = RegionAlign::CENTER_RIGHT; + pub const BOT_LEFT: RegionAlign = RegionAlign::BOT_LEFT; + pub const BOT_CENTER: RegionAlign = RegionAlign::BOT_CENTER; + pub const BOT_RIGHT: RegionAlign = RegionAlign::BOT_RIGHT; + pub const LEFT: CardinalAlign = CardinalAlign::LEFT; + pub const H_CENTER: CardinalAlign = CardinalAlign::H_CENTER; + pub const RIGHT: CardinalAlign = CardinalAlign::RIGHT; + pub const TOP: CardinalAlign = CardinalAlign::TOP; + pub const V_CENTER: CardinalAlign = CardinalAlign::V_CENTER; + pub const BOT: CardinalAlign = CardinalAlign::BOT; +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum AxisAlign { + Neg, + Center, + Pos, +} + +impl AxisAlign { + pub const fn rel(&self) -> f32 { + match self { + Self::Neg => 0.0, + Self::Center => 0.5, + Self::Pos => 1.0, + } + } +} + +pub struct CardinalAlign { + axis: Axis, + align: AxisAlign, +} + +impl CardinalAlign { + pub const LEFT: Self = Self::new(Axis::X, AxisAlign::Neg); + pub const H_CENTER: Self = Self::new(Axis::X, AxisAlign::Center); + pub const RIGHT: Self = Self::new(Axis::X, AxisAlign::Pos); + pub const TOP: Self = Self::new(Axis::Y, AxisAlign::Neg); + pub const V_CENTER: Self = Self::new(Axis::Y, AxisAlign::Center); + pub const BOT: Self = Self::new(Axis::Y, AxisAlign::Pos); + + pub const fn new(axis: Axis, align: AxisAlign) -> Self { + Self { axis, align } + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct RegionAlign { + pub x: AxisAlign, + pub y: AxisAlign, +} + +impl RegionAlign { + pub const TOP_LEFT: Self = Self::new(AxisAlign::Neg, AxisAlign::Neg); + pub const TOP_CENTER: Self = Self::new(AxisAlign::Center, AxisAlign::Neg); + pub const TOP_RIGHT: Self = Self::new(AxisAlign::Pos, AxisAlign::Neg); + pub const CENTER_LEFT: Self = Self::new(AxisAlign::Neg, AxisAlign::Center); + pub const CENTER: Self = Self::new(AxisAlign::Center, AxisAlign::Center); + pub const CENTER_RIGHT: Self = Self::new(AxisAlign::Pos, AxisAlign::Center); + pub const BOT_LEFT: Self = Self::new(AxisAlign::Neg, AxisAlign::Pos); + pub const BOT_CENTER: Self = Self::new(AxisAlign::Center, AxisAlign::Pos); + pub const BOT_RIGHT: Self = Self::new(AxisAlign::Pos, AxisAlign::Pos); + + pub const fn new(x: AxisAlign, y: AxisAlign) -> Self { + Self { x, y } + } + pub const fn rel(&self) -> Vec2 { + vec2(self.x.rel(), self.y.rel()) + } +} + +impl UiVec2 { + pub fn partial_align(&self, align: Align) -> UiRegion { + UiRegion { + x: if let Some(align) = align.x { + self.x.align(align) + } else { + UiSpan::FULL + }, + y: if let Some(align) = align.y { + self.y.align(align) + } else { + UiSpan::FULL + }, + } + } + + pub fn align(&self, align: RegionAlign) -> UiRegion { + UiRegion { + x: self.x.align(align.x), + y: self.y.align(align.y), + } + } +} + +impl Vec2 { + pub fn partial_align(&self, align: Align) -> UiRegion { + let s = UiVec2::from(*self); + UiRegion { + x: if let Some(align) = align.x { + s.x.align(align) + } else { + UiSpan::FULL + }, + y: if let Some(align) = align.y { + s.y.align(align) + } else { + UiSpan::FULL + }, + } + } + + pub fn align(&self, align: RegionAlign) -> UiRegion { + let s = UiVec2::from(*self); + UiRegion { + x: s.x.align(align.x), + y: s.y.align(align.y), + } + } +} + +impl UiScalar { + pub const fn align(&self, align: AxisAlign) -> UiSpan { + let rel = align.rel(); + let mut start = UiScalar::rel(rel); + start.abs -= self.abs * rel; + start.rel -= self.rel * rel; + let mut end = UiScalar::rel(rel); + end.abs += self.abs * (1.0 - rel); + end.rel += self.rel * (1.0 - rel); + UiSpan { start, end } + } +} + +impl From for Align { + fn from(region: RegionAlign) -> Self { + Self { + x: Some(region.x), + y: Some(region.y), + } + } +} + +impl From for RegionAlign { + fn from(align: Align) -> Self { + Self { + x: align.x.unwrap_or(AxisAlign::Center), + y: align.y.unwrap_or(AxisAlign::Center), + } + } +} + +impl From for RegionAlign { + fn from(align: CardinalAlign) -> Self { + Align::from(align).into() + } +} + +impl From for Align { + fn from(cardinal: CardinalAlign) -> Self { + let align = Some(cardinal.align); + match cardinal.axis { + Axis::X => Self { x: align, y: None }, + Axis::Y => Self { x: None, y: align }, + } + } +} + +impl const From for UiVec2 { + fn from(align: RegionAlign) -> Self { + Self::rel(align.rel()) + } +} + +impl RegionAlign { + pub const fn pos(self) -> UiVec2 { + UiVec2::from(self) + } +} diff --git a/src/layout/orientation/axis.rs b/src/layout/orientation/axis.rs new file mode 100644 index 0000000..5b58a7e --- /dev/null +++ b/src/layout/orientation/axis.rs @@ -0,0 +1,70 @@ +use super::*; + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum Axis { + X, + Y, +} + +impl std::ops::Not for Axis { + type Output = Self; + + fn not(self) -> Self::Output { + match self { + Self::X => Self::Y, + Self::Y => Self::X, + } + } +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Dir { + pub axis: Axis, + pub sign: Sign, +} + +impl Dir { + pub const fn new(axis: Axis, dir: Sign) -> Self { + Self { axis, sign: dir } + } + + pub const LEFT: Self = Self::new(Axis::X, Sign::Neg); + pub const RIGHT: Self = Self::new(Axis::X, Sign::Pos); + pub const UP: Self = Self::new(Axis::Y, Sign::Neg); + pub const DOWN: Self = Self::new(Axis::Y, Sign::Pos); +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Sign { + Neg, + Pos, +} + +impl Vec2 { + pub fn axis(&self, axis: Axis) -> f32 { + match axis { + Axis::X => self.x, + Axis::Y => self.y, + } + } + + pub fn axis_mut(&mut self, axis: Axis) -> &mut f32 { + match axis { + Axis::X => &mut self.x, + Axis::Y => &mut self.y, + } + } + + pub const fn from_axis(axis: Axis, aligned: f32, ortho: f32) -> Self { + Self { + x: match axis { + Axis::X => aligned, + Axis::Y => ortho, + }, + y: match axis { + Axis::Y => aligned, + Axis::X => ortho, + }, + } + } +} diff --git a/src/layout/orientation.rs b/src/layout/orientation/len.rs similarity index 60% rename from src/layout/orientation.rs rename to src/layout/orientation/len.rs index 1c819d3..094e4c8 100644 --- a/src/layout/orientation.rs +++ b/src/layout/orientation/len.rs @@ -1,107 +1,5 @@ -use std::ops::Not; - -use crate::{ - layout::{UiNum, UiScalar, UiVec2, Vec2, vec2}, - util::impl_op, -}; - -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum Axis { - X, - Y, -} - -impl Not for Axis { - type Output = Self; - - fn not(self) -> Self::Output { - match self { - Self::X => Self::Y, - Self::Y => Self::X, - } - } -} - -#[derive(Clone, Copy, Eq, PartialEq)] -pub struct Dir { - pub axis: Axis, - pub sign: Sign, -} - -impl Dir { - pub const fn new(axis: Axis, dir: Sign) -> Self { - Self { axis, sign: dir } - } - - pub const LEFT: Self = Self::new(Axis::X, Sign::Neg); - pub const RIGHT: Self = Self::new(Axis::X, Sign::Pos); - pub const UP: Self = Self::new(Axis::Y, Sign::Neg); - pub const DOWN: Self = Self::new(Axis::Y, Sign::Pos); -} - -#[derive(Clone, Copy, Eq, PartialEq)] -pub enum Sign { - Neg, - Pos, -} - -impl Vec2 { - pub fn axis(&self, axis: Axis) -> f32 { - match axis { - Axis::X => self.x, - Axis::Y => self.y, - } - } - - pub fn axis_mut(&mut self, axis: Axis) -> &mut f32 { - match axis { - Axis::X => &mut self.x, - Axis::Y => &mut self.y, - } - } - - pub const fn from_axis(axis: Axis, aligned: f32, ortho: f32) -> Self { - Self { - x: match axis { - Axis::X => aligned, - Axis::Y => ortho, - }, - y: match axis { - Axis::Y => aligned, - Axis::X => ortho, - }, - } - } -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum Align { - TopLeft, - Top, - TopRight, - Left, - Center, - Right, - BotLeft, - Bot, - BotRight, -} - -impl Align { - pub const fn rel(&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), - } - } -} +use super::*; +use crate::{layout::UiNum, util::impl_op}; #[derive(Debug, Default, Clone, Copy, PartialEq)] pub struct Size { @@ -159,7 +57,10 @@ impl Size { } pub fn to_uivec2(self) -> UiVec2 { - UiVec2::from_scalars(self.x.apply_rest(), self.y.apply_rest()) + UiVec2 { + x: self.x.apply_rest(), + y: self.y.apply_rest(), + } } pub fn from_axis(axis: Axis, aligned: Len, ortho: Len) -> Self { diff --git a/src/layout/orientation/mod.rs b/src/layout/orientation/mod.rs new file mode 100644 index 0000000..f6d2963 --- /dev/null +++ b/src/layout/orientation/mod.rs @@ -0,0 +1,11 @@ +mod align; +mod axis; +mod len; +mod pos; + +use super::vec2::*; + +pub use align::*; +pub use axis::*; +pub use len::*; +pub use pos::*; diff --git a/src/layout/orientation/pos.rs b/src/layout/orientation/pos.rs new file mode 100644 index 0000000..c0d49bc --- /dev/null +++ b/src/layout/orientation/pos.rs @@ -0,0 +1,449 @@ +use std::{fmt::Display, hash::Hash, marker::Destruct}; + +use super::*; +use crate::{ + layout::UiNum, + util::{LerpUtil, impl_op}, +}; + +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, bytemuck::Pod, bytemuck::Zeroable, Default)] +pub struct UiVec2 { + pub x: UiScalar, + pub y: UiScalar, +} + +impl UiVec2 { + pub const ZERO: Self = Self { + x: UiScalar::ZERO, + y: UiScalar::ZERO, + }; + + pub const fn new(x: UiScalar, y: UiScalar) -> Self { + Self { x, y } + } + + pub const fn abs(abs: impl const Into) -> Self { + let abs = abs.into(); + Self { + x: UiScalar::abs(abs.x), + y: UiScalar::abs(abs.y), + } + } + + pub const fn rel(rel: impl const Into) -> Self { + let rel = rel.into(); + Self { + x: UiScalar::rel(rel.x), + y: UiScalar::rel(rel.y), + } + } + + pub const fn shift(&mut self, offset: impl const Into) { + let offset = offset.into(); + *self += offset; + } + + pub const fn offset(mut self, offset: impl const Into) -> Self { + self.shift(offset); + self + } + + pub const fn within(&self, region: &UiRegion) -> UiVec2 { + UiVec2 { + x: self.x.within(®ion.x), + y: self.y.within(®ion.y), + } + } + + pub const fn outside(&self, region: &UiRegion) -> UiVec2 { + UiVec2 { + x: self.x.outside(®ion.x), + y: self.y.outside(®ion.y), + } + } + + pub fn axis_mut(&mut self, axis: Axis) -> &mut UiScalar { + match axis { + Axis::X => &mut self.x, + Axis::Y => &mut self.y, + } + } + + pub fn axis(&self, axis: Axis) -> UiScalar { + match axis { + Axis::X => self.x, + Axis::Y => self.y, + } + } + + pub fn to_abs(&self, rel: Vec2) -> Vec2 { + self.get_rel() * rel + self.get_abs() + } + + pub const FULL_SIZE: Self = Self::rel(Vec2::ONE); + + pub const fn from_axis(axis: Axis, aligned: UiScalar, ortho: UiScalar) -> Self { + match axis { + Axis::X => Self { + x: aligned, + y: ortho, + }, + Axis::Y => Self { + x: ortho, + y: aligned, + }, + } + } + + pub fn get_abs(&self) -> Vec2 { + (self.x.abs, self.y.abs).into() + } + + pub fn get_rel(&self) -> Vec2 { + (self.x.rel, self.y.rel).into() + } + + pub fn abs_mut(&mut self) -> Vec2View<'_> { + Vec2View { + x: &mut self.x.abs, + y: &mut self.y.abs, + } + } +} + +impl Display for UiVec2 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "rel{};abs{}", self.get_rel(), self.get_abs()) + } +} + +impl_op!(UiVec2 Add add; x y); +impl_op!(UiVec2 Sub sub; x y); + +impl const From for UiVec2 { + fn from(abs: Vec2) -> Self { + Self::abs(abs) + } +} + +impl const From<(T, U)> for UiVec2 +where + (T, U): const Destruct, +{ + fn from(abs: (T, U)) -> Self { + Self::abs(abs) + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, Default, bytemuck::Zeroable)] +pub struct UiScalar { + pub rel: f32, + pub abs: f32, +} + +impl Eq for UiScalar {} +impl Hash for UiScalar { + fn hash(&self, state: &mut H) { + state.write_u32(self.rel.to_bits()); + state.write_u32(self.abs.to_bits()); + } +} + +impl_op!(UiScalar Add add; rel abs); +impl_op!(UiScalar Sub sub; rel abs); + +impl UiScalar { + pub const ZERO: Self = Self { rel: 0.0, abs: 0.0 }; + pub const FULL: Self = Self { rel: 1.0, abs: 0.0 }; + + pub const fn new(rel: f32, abs: f32) -> Self { + Self { rel, abs } + } + + pub const fn rel(rel: f32) -> Self { + Self { rel, abs: 0.0 } + } + + pub const fn abs(abs: f32) -> Self { + Self { rel: 0.0, abs } + } + + pub const fn rel_min() -> Self { + Self::new(0.0, 0.0) + } + + pub const fn rel_max() -> Self { + Self::new(1.0, 0.0) + } + + pub const fn max(&self, other: Self) -> Self { + Self { + rel: self.rel.max(other.rel), + abs: self.abs.max(other.abs), + } + } + + pub const fn min(&self, other: Self) -> Self { + Self { + rel: self.rel.min(other.rel), + abs: self.abs.min(other.abs), + } + } + + pub const fn offset(mut self, amt: f32) -> Self { + self.abs += amt; + self + } + + pub const fn within(&self, span: &UiSpan) -> Self { + let anchor = self.rel.lerp(span.start.rel, span.end.rel); + let offset = self.abs + self.rel.lerp(span.start.abs, span.end.abs); + Self { + rel: anchor, + abs: offset, + } + } + + pub const fn outside(&self, span: &UiSpan) -> Self { + let rel = self.rel.lerp_inv(span.start.rel, span.end.rel); + let abs = self.abs - rel.lerp(span.start.abs, span.end.abs); + Self { rel, abs } + } + + pub fn within_len(&self, len: UiScalar) -> Self { + self.within(&UiSpan { + start: UiScalar::ZERO, + end: len, + }) + } + + pub fn select_len(&self, len: UiScalar) -> Self { + len.within_len(*self) + } + + pub const fn flip(&mut self) { + self.rel = 1.0 - self.rel; + self.abs = -self.abs; + } + + pub const fn to(&self, end: Self) -> UiSpan { + UiSpan { start: *self, end } + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] +pub struct UiSpan { + pub start: UiScalar, + pub end: UiScalar, +} + +impl UiSpan { + pub const FULL: Self = Self { + start: UiScalar::ZERO, + end: UiScalar::FULL, + }; + + pub const fn rel(rel: f32) -> Self { + Self { + start: UiScalar::rel(rel), + end: UiScalar::rel(rel), + } + } + + pub const fn new(start: UiScalar, end: UiScalar) -> Self { + Self { start, end } + } + + pub const fn flip(&mut self) { + self.start.flip(); + self.end.flip(); + std::mem::swap(&mut self.start.rel, &mut self.end.rel); + std::mem::swap(&mut self.start.abs, &mut self.end.abs); + } + + pub const fn shift(&mut self, offset: UiScalar) { + self.start += offset; + self.end += offset; + } + + pub const fn within(&self, parent: &Self) -> Self { + Self { + start: self.start.within(parent), + end: self.end.within(parent), + } + } + + pub const fn outside(&self, parent: &Self) -> Self { + Self { + start: self.start.outside(parent), + end: self.end.outside(parent), + } + } + + pub const fn len(&self) -> UiScalar { + self.end - self.start + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] +pub struct UiRegion { + pub x: UiSpan, + pub y: UiSpan, +} + +impl UiRegion { + pub const FULL: Self = Self { + x: UiSpan::FULL, + y: UiSpan::FULL, + }; + + pub const fn new(x: UiSpan, y: UiSpan) -> Self { + Self { x, y } + } + + pub const fn rel(rel: Vec2) -> Self { + Self { + x: UiSpan::rel(rel.x), + y: UiSpan::rel(rel.y), + } + } + pub const fn within(&self, parent: &Self) -> Self { + Self { + x: self.x.within(&parent.x), + y: self.y.within(&parent.y), + } + } + pub const fn outside(&self, parent: &Self) -> Self { + Self { + x: self.x.outside(&parent.x), + y: self.y.outside(&parent.y), + } + } + pub const fn axis_mut(&mut self, axis: Axis) -> &mut UiSpan { + match axis { + Axis::X => &mut self.x, + Axis::Y => &mut self.y, + } + } + + pub const fn flip(&mut self, axis: Axis) { + match axis { + Axis::X => self.x.flip(), + Axis::Y => self.y.flip(), + } + } + + pub fn shift(&mut self, offset: impl Into) { + let offset = offset.into(); + self.x.shift(offset.x); + self.y.shift(offset.y); + } + + pub fn offset(mut self, offset: impl Into) -> Self { + self.shift(offset); + self + } + + pub fn to_px(&self, size: Vec2) -> PixelRegion { + PixelRegion { + top_left: self.top_left().get_rel() * size + self.top_left().get_abs(), + bot_right: self.bot_right().get_rel() * size + self.bot_right().get_abs(), + } + } + + pub const fn center(&self) -> UiVec2 { + Align::CENTER.pos().within(self) + } + + pub const fn size(&self) -> UiVec2 { + UiVec2 { + x: self.x.len(), + y: self.y.len(), + } + } + + pub const fn top_left(&self) -> UiVec2 { + UiVec2 { + x: self.x.start, + y: self.y.start, + } + } + + pub const fn bot_right(&self) -> UiVec2 { + UiVec2 { + x: self.x.end, + y: self.y.end, + } + } + + pub const fn from_axis(axis: Axis, aligned: UiSpan, ortho: UiSpan) -> Self { + Self { + x: match axis { + Axis::X => aligned, + Axis::Y => ortho, + }, + y: match axis { + Axis::X => ortho, + Axis::Y => aligned, + }, + } + } +} + +impl Display for UiRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} -> {} (size: {})", + self.top_left(), + self.bot_right(), + self.size() + ) + } +} + +#[derive(Debug)] +pub struct PixelRegion { + pub top_left: Vec2, + pub bot_right: Vec2, +} + +impl PixelRegion { + pub fn contains(&self, pos: Vec2) -> bool { + pos.x >= self.top_left.x + && pos.x <= self.bot_right.x + && pos.y >= self.top_left.y + && pos.y <= self.bot_right.y + } + + pub fn size(&self) -> Vec2 { + self.bot_right - self.top_left + } +} + +impl Display for PixelRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} -> {}", self.top_left, self.bot_right) + } +} + +pub struct Vec2View<'a> { + pub x: &'a mut f32, + pub y: &'a mut f32, +} + +impl Vec2View<'_> { + pub fn set(&mut self, other: Vec2) { + *self.x = other.x; + *self.y = other.y; + } + + pub fn add(&mut self, other: Vec2) { + *self.x += other.x; + *self.y += other.y; + } +} diff --git a/src/layout/painter.rs b/src/layout/painter.rs index fc680fa..ddc479a 100644 --- a/src/layout/painter.rs +++ b/src/layout/painter.rs @@ -31,7 +31,6 @@ pub struct PainterCtx<'a> { pub text: &'a mut TextData, pub screen_size: Vec2, pub modules: &'a mut Modules, - pub px_dependent: &'a mut HashSet, pub cache_width: HashMap, pub cache_height: HashMap, draw_started: HashSet, @@ -78,7 +77,6 @@ impl<'a> PainterCtx<'a> { text: &mut data.text, screen_size: data.output_size, modules: &mut data.modules, - px_dependent: &mut data.px_dependent, masks: &mut data.masks, cache_width: Default::default(), cache_height: Default::default(), @@ -107,7 +105,6 @@ impl<'a> PainterCtx<'a> { output_size: self.screen_size, checked_width: &mut Default::default(), checked_height: &mut Default::default(), - px_dependent: &mut Default::default(), id, } .width_inner(id); @@ -134,7 +131,6 @@ impl<'a> PainterCtx<'a> { output_size: self.screen_size, checked_width: &mut Default::default(), checked_height: &mut Default::default(), - px_dependent: &mut Default::default(), id, } .height_inner(id); @@ -164,7 +160,7 @@ impl<'a> PainterCtx<'a> { pub fn draw(&mut self, id: Id) { self.draw_started.clear(); self.layers.clear(); - self.draw_inner(0, id, UiRegion::full(), None, MaskIdx::NONE, None); + self.draw_inner(0, id, UiRegion::FULL, None, MaskIdx::NONE, None); } fn draw_inner( @@ -300,7 +296,6 @@ impl<'a> PainterCtx<'a> { m.on_undraw(inst); } } - self.px_dependent.remove(&id); inst } @@ -405,7 +400,6 @@ impl<'a, 'c> Painter<'a, 'c> { textures: self.ctx.textures, widgets: self.ctx.widgets, output_size: self.ctx.screen_size, - px_dependent: self.ctx.px_dependent, checked_width: &mut self.children_width, checked_height: &mut self.children_height, cache_width: &mut self.ctx.cache_width, @@ -417,7 +411,6 @@ impl<'a, 'c> Painter<'a, 'c> { } pub fn px_size(&mut self) -> Vec2 { - self.ctx.px_dependent.insert(self.id); self.region.size().to_abs(self.ctx.screen_size) } @@ -443,7 +436,6 @@ pub struct SizeCtx<'a> { pub textures: &'a mut Textures, source: Id, widgets: &'a Widgets, - px_dependent: &'a mut HashSet, cache_width: &'a mut HashMap, cache_height: &'a mut HashMap, checked_width: &'a mut HashMap, @@ -523,8 +515,6 @@ impl SizeCtx<'_> { } pub fn px_size(&mut self) -> Vec2 { - // WARNING IF UNCOMMENT: self.id is no longer valid - // self.px_dependent.insert(self.id); self.outer.to_abs(self.output_size) } diff --git a/src/layout/pos.rs b/src/layout/pos.rs deleted file mode 100644 index 53bf20b..0000000 --- a/src/layout/pos.rs +++ /dev/null @@ -1,382 +0,0 @@ -use std::{fmt::Display, marker::Destruct}; - -use crate::{ - layout::{Align, Axis, UiNum, Vec2}, - util::{LerpUtil, impl_op}, -}; - -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, bytemuck::Pod, bytemuck::Zeroable, Default)] -pub struct UiVec2 { - pub rel: Vec2, - pub abs: Vec2, -} - -impl UiVec2 { - pub const ZERO: Self = Self { - rel: Vec2::ZERO, - abs: Vec2::ZERO, - }; - - /// expands this position into a sized region centered at self - pub fn expand(&self, size: impl Into) -> UiRegion { - let size = size.into(); - UiRegion { - top_left: self.offset(-size / 2.0), - bot_right: self.offset(size / 2.0), - } - } - - pub const fn abs(abs: impl const Into) -> Self { - Self { - rel: Vec2::ZERO, - abs: abs.into(), - } - } - - pub const fn rel(rel: impl const Into) -> Self { - Self { - rel: rel.into(), - abs: Vec2::ZERO, - } - } - - pub const fn shift(&mut self, offset: impl const Into) { - let offset = offset.into(); - *self += offset; - } - - pub const fn offset(mut self, offset: impl const Into) -> Self { - self.shift(offset); - self - } - - pub const fn within(&self, region: &UiRegion) -> UiVec2 { - let rel = self.rel.lerp(region.top_left.rel, region.bot_right.rel); - let abs = self.abs + self.rel.lerp(region.top_left.abs, region.bot_right.abs); - UiVec2 { rel, abs } - } - - pub const fn outside(&self, region: &UiRegion) -> UiVec2 { - let rel = self.rel.lerp_inv(region.top_left.rel, region.bot_right.rel); - let abs = self.abs - rel.lerp(region.top_left.abs, region.bot_right.abs); - UiVec2 { rel, abs } - } - - pub fn axis_mut(&mut self, axis: Axis) -> UiScalarView<'_> { - match axis { - Axis::X => UiScalarView { - rel: &mut self.rel.x, - abs: &mut self.abs.x, - }, - Axis::Y => UiScalarView { - rel: &mut self.rel.y, - abs: &mut self.abs.y, - }, - } - } - - pub fn axis(&self, axis: Axis) -> UiScalar { - match axis { - Axis::X => UiScalar { - rel: self.rel.x, - abs: self.abs.x, - }, - Axis::Y => UiScalar { - rel: self.rel.y, - abs: self.abs.y, - }, - } - } - - /// reflection about an axis - pub fn flip(&mut self, axis: Axis) { - *self.rel.axis_mut(axis) = 1.0 - self.rel.axis(axis); - *self.abs.axis_mut(axis) = -self.abs.axis(axis); - } - - pub fn flipped(mut self, axis: Axis) -> Self { - self.flip(axis); - self - } - - pub fn to_abs(&self, rel: Vec2) -> Vec2 { - self.rel * rel + self.abs - } - - pub const FULL_SIZE: Self = Self::rel(Vec2::ONE); - - pub const fn from_axis(axis: Axis, aligned: UiScalar, ortho: UiScalar) -> Self { - Self { - rel: Vec2::from_axis(axis, aligned.rel, ortho.rel), - abs: Vec2::from_axis(axis, aligned.abs, ortho.abs), - } - } - - pub const fn from_scalars(x: UiScalar, y: UiScalar) -> Self { - Self { - rel: Vec2 { x: x.rel, y: y.rel }, - abs: Vec2 { x: x.abs, y: y.abs }, - } - } -} - -impl Display for UiVec2 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "rel{};abs{}", self.rel, self.abs) - } -} - -impl_op!(UiVec2 Add add; rel abs); -impl_op!(UiVec2 Sub sub; rel abs); - -impl const From for UiVec2 { - fn from(align: Align) -> Self { - Self::rel(align.rel()) - } -} - -impl Align { - pub fn pos(self) -> UiVec2 { - UiVec2::from(self) - } -} - -impl const From for UiVec2 { - fn from(abs: Vec2) -> Self { - Self::abs(abs) - } -} - -impl const From<(T, U)> for UiVec2 -where - (T, U): const Destruct, -{ - fn from(abs: (T, U)) -> Self { - Self::abs(abs) - } -} - -#[derive(Clone, Copy, Debug, Default)] -pub struct UiScalar { - pub rel: f32, - pub abs: f32, -} - -impl_op!(UiScalar Add add; rel abs); -impl_op!(UiScalar Sub sub; rel abs); - -impl UiScalar { - pub const ZERO: Self = Self { rel: 0.0, abs: 0.0 }; - pub const FULL: Self = Self { rel: 1.0, abs: 0.0 }; - - pub fn new(rel: f32, abs: f32) -> Self { - Self { rel, abs } - } - pub fn rel_min() -> Self { - Self::new(0.0, 0.0) - } - - pub fn rel_max() -> Self { - Self::new(1.0, 0.0) - } - - pub fn max(&self, other: Self) -> Self { - Self { - rel: self.rel.max(other.rel), - abs: self.abs.max(other.abs), - } - } - - pub fn min(&self, other: Self) -> Self { - Self { - rel: self.rel.min(other.rel), - abs: self.abs.min(other.abs), - } - } - - pub fn from_anchor(anchor: f32) -> Self { - Self::new(anchor, 0.0) - } - - pub fn offset(mut self, amt: f32) -> Self { - self.abs += amt; - self - } - - pub fn within(&self, start: UiScalar, end: UiScalar) -> Self { - let anchor = self.rel.lerp(start.rel, end.rel); - let offset = self.abs + self.rel.lerp(start.abs, end.abs); - Self { - rel: anchor, - abs: offset, - } - } - - pub fn within_len(&self, len: UiScalar) -> Self { - self.within(UiScalar::ZERO, len) - } -} - -#[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] -pub struct UiRegion { - pub top_left: UiVec2, - pub bot_right: UiVec2, -} - -impl UiRegion { - pub const fn full() -> Self { - Self { - top_left: Align::TopLeft.into(), - bot_right: Align::BotRight.into(), - } - } - pub fn rel(anchor: Vec2) -> Self { - Self { - top_left: UiVec2::rel(anchor), - bot_right: UiVec2::rel(anchor), - } - } - pub fn within(&self, parent: &Self) -> Self { - Self { - top_left: self.top_left.within(parent), - bot_right: self.bot_right.within(parent), - } - } - pub fn outside(&self, parent: &Self) -> Self { - Self { - top_left: self.top_left.outside(parent), - bot_right: self.bot_right.outside(parent), - } - } - pub fn axis_mut(&mut self, axis: Axis) -> UIRegionAxisView<'_> { - UIRegionAxisView { - top_left: self.top_left.axis_mut(axis), - bot_right: self.bot_right.axis_mut(axis), - } - } - - pub fn flip(&mut self, axis: Axis) { - self.top_left.flip(axis); - self.bot_right.flip(axis); - let tl = self.top_left.axis_mut(axis); - let br = self.bot_right.axis_mut(axis); - std::mem::swap(tl.rel, br.rel); - std::mem::swap(tl.abs, br.abs); - } - - pub fn shift(&mut self, offset: impl Into) { - let offset = offset.into(); - self.top_left.shift(offset); - self.bot_right.shift(offset); - } - - pub fn offset(mut self, offset: impl Into) -> Self { - self.shift(offset); - self - } - - pub fn to_px(&self, size: Vec2) -> PixelRegion { - PixelRegion { - top_left: self.top_left.rel * size + self.top_left.abs, - bot_right: self.bot_right.rel * size + self.bot_right.abs, - } - } - - pub fn center(&self) -> UiVec2 { - Align::Center.pos().within(self) - } - - pub fn size(&self) -> UiVec2 { - self.bot_right - self.top_left - } - - 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 = UiVec2::from(align); - top_left.abs -= size * align.rel(); - let mut bot_right = UiVec2::from(align); - bot_right.abs += size * (Vec2::ONE - align.rel()); - Self { - top_left, - bot_right, - } - } - - pub fn from_ui_size_align(size: UiVec2, align: Align) -> Self { - let mut top_left = UiVec2::from(align); - top_left.abs -= size.abs * align.rel(); - top_left.rel -= size.rel * align.rel(); - let mut bot_right = UiVec2::from(align); - bot_right.abs += size.abs * (Vec2::ONE - align.rel()); - bot_right.rel += size.rel * (Vec2::ONE - align.rel()); - Self { - top_left, - bot_right, - } - } -} - -impl Display for UiRegion { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{} -> {} (size: {})", - self.top_left, - self.bot_right, - self.size() - ) - } -} - -#[derive(Debug)] -pub struct PixelRegion { - pub top_left: Vec2, - pub bot_right: Vec2, -} - -impl PixelRegion { - pub fn contains(&self, pos: Vec2) -> bool { - pos.x >= self.top_left.x - && pos.x <= self.bot_right.x - && pos.y >= self.top_left.y - && pos.y <= self.bot_right.y - } - - pub fn size(&self) -> Vec2 { - self.bot_right - self.top_left - } -} - -impl Display for PixelRegion { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{} -> {}", self.top_left, self.bot_right) - } -} - -pub struct UIRegionAxisView<'a> { - pub top_left: UiScalarView<'a>, - pub bot_right: UiScalarView<'a>, -} - -pub struct UiScalarView<'a> { - pub rel: &'a mut f32, - pub abs: &'a mut f32, -} - -impl UiScalarView<'_> { - pub fn set(&mut self, scalar: UiScalar) { - *self.rel = scalar.rel; - *self.abs = scalar.abs; - } - pub fn get(self) -> UiScalar { - UiScalar { - rel: *self.rel, - abs: *self.abs, - } - } -} diff --git a/src/layout/text.rs b/src/layout/text.rs index 63c018d..843bc03 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -4,7 +4,7 @@ use cosmic_text::{ use image::{Rgba, RgbaImage}; use crate::{ - layout::{Align, TextureHandle, Textures, UiColor, Vec2}, + layout::{Align, RegionAlign, TextureHandle, Textures, UiColor, Vec2}, util::HashMap, }; @@ -35,7 +35,7 @@ pub struct TextAttrs { pub family: Family<'static>, pub wrap: bool, /// inner alignment of text region (within where its drawn) - pub align: Align, + pub align: RegionAlign, } impl TextAttrs { @@ -65,7 +65,7 @@ impl Default for TextAttrs { line_height: size * 1.2, family: Family::SansSerif, wrap: false, - align: Align::Center, + align: Align::CENTER, } } } diff --git a/src/render/shader.wgsl b/src/render/shader.wgsl index 0861d6b..238b601 100644 --- a/src/render/shader.wgsl +++ b/src/render/shader.wgsl @@ -42,10 +42,10 @@ struct WindowUniform { }; struct InstanceInput { - @location(0) top_left_anchor: vec2, - @location(1) top_left_offset: vec2, - @location(2) bottom_right_anchor: vec2, - @location(3) bottom_right_offset: vec2, + @location(0) x_start: vec2, + @location(1) x_end: vec2, + @location(2) y_start: vec2, + @location(3) y_end: vec2, @location(4) binding: u32, @location(5) idx: u32, @location(6) mask_idx: u32, @@ -75,8 +75,13 @@ fn vs_main( ) -> VertexOutput { var out: VertexOutput; - let top_left = floor(in.top_left_anchor * window.dim) + floor(in.top_left_offset); - let bot_right = floor(in.bottom_right_anchor * window.dim) + floor(in.bottom_right_offset); + let top_left_rel = vec2(in.x_start.x, in.y_start.x); + let top_left_abs = vec2(in.x_start.y, in.y_start.y); + let bot_right_rel = vec2(in.x_end.x, in.y_end.x); + let bot_right_abs = vec2(in.x_end.y, in.y_end.y); + + let top_left = floor(top_left_rel * window.dim) + floor(top_left_abs); + let bot_right = floor(bot_right_rel * window.dim) + floor(bot_right_abs); let size = bot_right - top_left; let uv = vec2( diff --git a/src/testing/mod.rs b/src/testing/mod.rs index f051774..b00af23 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -86,7 +86,7 @@ impl Client { ctx.ui[span_add].children.push(child); }) .sized((150, 150)) - .align(Align::BotRight); + .align(Align::BOT_RIGHT); let del_button = rect(Color::RED) .radius(30) @@ -94,7 +94,7 @@ impl Client { ctx.ui[span_add].children.pop(); }) .sized((150, 150)) - .align(Align::BotLeft); + .align(Align::BOT_LEFT); let span_add_test = (span_add, add_button, del_button) .stack() @@ -105,18 +105,18 @@ impl Client { let btext = |content| text(content).size(30); let text_test = ( - btext("this is a").align(Align::Left), - btext("teeeeeeeest").align(Align::Right), - btext("okkk\nokkkkkk!").align(Align::Left), + btext("this is a").align(Align::LEFT), + btext("teeeeeeeest").align(Align::RIGHT), + btext("okkk\nokkkkkk!").align(Align::LEFT), btext("hmm"), btext("a"), ( - btext("'").family(Family::Monospace).align(Align::Top), + btext("'").family(Family::Monospace).align(Align::TOP), btext("'").family(Family::Monospace), btext(":gamer mode").family(Family::Monospace), rect(Color::CYAN).sized((10, 10)).center(), rect(Color::RED).sized((100, 100)).center(), - rect(Color::PURPLE).sized((50, 50)).align(Align::Top), + rect(Color::PURPLE).sized((50, 50)).align(Align::TOP), ) .span(Dir::RIGHT) .center(), @@ -129,7 +129,7 @@ impl Client { let msg_area = (Rect::new(Color::SKY), texts.scroll().masked()).stack(); let add_text = text("add") .editable() - .text_align(Align::Left) + .text_align(Align::LEFT) .size(30) .id_on(CursorSense::click(), |id, client: &mut Client, ctx| { client.ui.text(id).select(ctx.cursor, ctx.size); @@ -140,7 +140,7 @@ impl Client { let text = text(content) .editable() .size(30) - .text_align(Align::Left) + .text_align(Align::LEFT) .wrap(true) .id_on(CursorSense::click(), |id, client: &mut Client, ctx| { client.ui.text(id).select(ctx.cursor, ctx.size); @@ -171,7 +171,7 @@ impl Client { .stack() .size(StackSize::Child(1)) .offset_layer(1) - .align(Align::Bot), + .align(Align::BOT), ) .span(Dir::DOWN) .add_static(&mut ui); @@ -208,7 +208,7 @@ impl Client { .span(Dir::RIGHT); let info = text("").add(&mut ui); - let info_sect = info.clone().pad(10).align(Align::Right); + let info_sect = info.clone().pad(10).align(Align::RIGHT); ((tabs.height(40), main).span(Dir::DOWN), info_sect) .stack()