diff --git a/src/base/frame.rs b/src/base/frame.rs new file mode 100644 index 0000000..eaf4dce --- /dev/null +++ b/src/base/frame.rs @@ -0,0 +1,45 @@ +use crate::{Painter, UINum, UIRegion, Widget, WidgetId}; + +pub struct Regioned { + pub region: UIRegion, + pub inner: WidgetId, +} + +impl Widget for Regioned { + fn draw(&self, painter: &mut Painter) { + painter.region.select(&self.region); + painter.draw(&self.inner); + } +} + +pub struct Padding { + left: f32, + right: f32, + top: f32, + bottom: f32, +} + +impl Padding { + pub fn uniform(amt: f32) -> Self { + Self { + left: amt, + right: amt, + top: amt, + bottom: amt, + } + } + pub fn region(&self) -> UIRegion { + let mut region = UIRegion::full(); + region.top_left.offset.x += self.left; + region.top_left.offset.y += self.top; + region.bot_right.offset.x -= self.right; + region.bot_right.offset.y -= self.bottom; + region + } +} + +impl From for Padding { + fn from(amt: T) -> Self { + Self::uniform(amt.to_f32()) + } +} diff --git a/src/base/mod.rs b/src/base/mod.rs index c14655d..647220b 100644 --- a/src/base/mod.rs +++ b/src/base/mod.rs @@ -1,171 +1,11 @@ -use std::ops::Range; +mod frame; +mod num; +mod rect; +mod span; +mod trait_fns; -use crate::{ - UIColor, Widget, WidgetArrLike, WidgetFn, WidgetId, WidgetLike, - primitive::{Axis, Painter, RoundedRectData, UIRegion, Vec2}, -}; - -#[derive(Clone, Copy)] -pub struct RoundedRect { - pub color: UIColor, - pub radius: f32, - pub thickness: f32, - pub inner_radius: f32, -} - -impl RoundedRect { - pub fn color(mut self, color: UIColor) -> Self { - self.color = color; - self - } -} - -impl Widget for RoundedRect { - fn draw(&self, painter: &mut Painter) { - painter.write(RoundedRectData { - color: self.color, - radius: self.radius, - thickness: self.thickness, - inner_radius: self.inner_radius, - }); - } -} - -pub struct Span { - pub elements: Vec<(Range, WidgetId)>, - pub axis: Axis, -} - -impl Widget for Span { - fn draw(&self, painter: &mut Painter) { - for (span, child) in &self.elements { - let mut sub_region = UIRegion::full(); - let view = sub_region.axis_mut(self.axis); - *view.top_left.anchor = span.start; - *view.bot_right.anchor = span.end; - painter.draw_within(child, sub_region); - } - } -} - -impl Span { - pub fn proportioned( - axis: Axis, - ratios: [impl UINum; LEN], - elements: [WidgetId; LEN], - ) -> Self { - let ratios = ratios.map(|r| r.to_f32()); - let total: f32 = ratios.iter().sum(); - let mut start = -1.0; - Self { - elements: elements - .into_iter() - .zip(ratios) - .map(|(e, r)| { - let end = start + (r / total) * 2.0; - let res = (start..end, e); - start = end; - res - }) - .collect(), - axis, - } - } -} - -pub struct Regioned { - region: UIRegion, - inner: WidgetId, -} - -impl Widget for Regioned { - fn draw(&self, painter: &mut Painter) { - painter.region.select(&self.region); - painter.draw(&self.inner); - } -} - -pub struct Padding { - left: f32, - right: f32, - top: f32, - bottom: f32, -} - -impl Padding { - pub fn uniform(amt: f32) -> Self { - Self { - left: amt, - right: amt, - top: amt, - bottom: amt, - } - } - pub fn region(&self) -> UIRegion { - let mut region = UIRegion::full(); - region.top_left.offset.x += self.left; - region.top_left.offset.y += self.top; - region.bot_right.offset.x -= self.right; - region.bot_right.offset.y -= self.bottom; - region - } -} - -impl From for Padding { - fn from(amt: T) -> Self { - Self::uniform(amt.to_f32()) - } -} - -pub trait WidgetUtil { - fn pad(self, padding: impl Into) -> impl WidgetLike; - fn center(self, size: impl Into) -> impl WidgetLike; -} - -impl WidgetUtil for W { - fn pad(self, padding: impl Into) -> impl WidgetLike { - WidgetFn(|ui| Regioned { - region: padding.into().region(), - inner: self.add(ui).erase_type(), - }) - } - - fn center(self, size: impl Into) -> impl WidgetLike { - WidgetFn(|ui| Regioned { - region: UIRegion::center(size.into()), - inner: self.add(ui).erase_type(), - }) - } -} - -pub trait WidgetArrUtil { - fn span(self, axis: Axis, ratios: [impl UINum; LEN]) -> impl WidgetLike; -} - -impl> WidgetArrUtil for Wa { - fn span(self, axis: Axis, ratios: [impl UINum; LEN]) -> impl WidgetLike { - WidgetFn(move |ui| Span::proportioned(axis, ratios, self.ui(ui).arr)) - } -} - -pub trait UINum { - fn to_f32(self) -> f32; -} - -impl UINum for f32 { - fn to_f32(self) -> f32 { - self - } -} - -impl UINum for u32 { - fn to_f32(self) -> f32 { - self as f32 - } -} - -impl UINum for i32 { - fn to_f32(self) -> f32 { - self as f32 - } -} +pub use frame::*; +pub use num::*; +pub use rect::*; +pub use span::*; +pub use trait_fns::*; diff --git a/src/base/num.rs b/src/base/num.rs new file mode 100644 index 0000000..788aa73 --- /dev/null +++ b/src/base/num.rs @@ -0,0 +1,50 @@ +pub trait UINum { + fn to_f32(self) -> f32; +} + +impl UINum for f32 { + fn to_f32(self) -> f32 { + self + } +} + +impl UINum for u32 { + fn to_f32(self) -> f32 { + self as f32 + } +} + +impl UINum for i32 { + fn to_f32(self) -> f32 { + self as f32 + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum Axis { + X, + Y, +} + +#[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, +} diff --git a/src/base/rect.rs b/src/base/rect.rs new file mode 100644 index 0000000..e7fb090 --- /dev/null +++ b/src/base/rect.rs @@ -0,0 +1,27 @@ +use crate::{Painter, UIColor, Widget, primitive::RoundedRectData}; + +#[derive(Clone, Copy)] +pub struct Rect { + pub color: UIColor, + pub radius: f32, + pub thickness: f32, + pub inner_radius: f32, +} + +impl Rect { + pub fn color(mut self, color: UIColor) -> Self { + self.color = color; + self + } +} + +impl Widget for Rect { + fn draw(&self, painter: &mut Painter) { + painter.write(RoundedRectData { + color: self.color, + radius: self.radius, + thickness: self.thickness, + inner_radius: self.inner_radius, + }); + } +} diff --git a/src/base/span.rs b/src/base/span.rs new file mode 100644 index 0000000..8c6e2fe --- /dev/null +++ b/src/base/span.rs @@ -0,0 +1,89 @@ +use crate::{Dir, Painter, UINum, UIRegion, UIScalar, Widget, WidgetId}; + +pub struct Span { + pub children: Vec<(WidgetId, SpanLen)>, + pub dir: Dir, +} + +impl Widget for Span { + fn draw(&self, painter: &mut Painter) { + let total = self.sums(); + let mut start = UIScalar::min(); + for (child, length) in &self.children { + let mut child_region = UIRegion::full(); + let mut axis = child_region.axis_mut(self.dir.axis); + axis.top_left.set(start); + match *length { + SpanLen::Fixed(offset) => { + start.offset += offset; + *axis.bot_right.offset = start.offset; + *axis.bot_right.anchor = *axis.top_left.anchor; + } + SpanLen::Ratio(ratio) => { + let offset = UIScalar::new(total.relative, total.fixed); + let rel_end = UIScalar::from_anchor(ratio / total.ratio); + start = rel_end.within(start, (UIScalar::max() + start) - offset); + axis.bot_right.set(start); + } + SpanLen::Relative(rel) => { + start.anchor += rel; + axis.bot_right.set(start); + } + } + painter.draw_within(child, child_region); + } + } +} + +#[derive(Default)] +pub struct SpanLenSums { + pub fixed: f32, + pub ratio: f32, + pub relative: f32, +} + +impl Span { + pub fn sums(&self) -> SpanLenSums { + self.lengths().fold(SpanLenSums::default(), |mut s, l| { + match l { + SpanLen::Fixed(v) => s.fixed += v, + SpanLen::Ratio(v) => s.ratio += v, + SpanLen::Relative(v) => s.relative += v, + } + s + }) + } + pub fn lengths(&self) -> impl ExactSizeIterator { + self.children.iter().map(|(_, s)| s) + } +} + +#[derive(Clone, Copy)] +pub enum SpanLen { + /// exact (non dynamic) size + Fixed(f32), + /// relative to remaining free space and other ratios + /// eg. 1 and 2 would take up 1/3 and 2/3 of the remaining space (after others) + Ratio(f32), + /// relative to the total space + /// eg. 0.5 means 1/2 of the total space + Relative(f32), +} + +pub fn fixed(x: N) -> SpanLen { + SpanLen::Fixed(x.to_f32()) +} + +pub fn ratio(x: N) -> SpanLen { + SpanLen::Ratio(x.to_f32()) +} + +pub fn rel(x: N) -> SpanLen { + SpanLen::Relative(x.to_f32()) +} + +impl From for SpanLen { + fn from(value: N) -> Self { + Self::Ratio(value.to_f32()) + } +} diff --git a/src/base/trait_fns.rs b/src/base/trait_fns.rs new file mode 100644 index 0000000..a1cde88 --- /dev/null +++ b/src/base/trait_fns.rs @@ -0,0 +1,37 @@ +use super::*; +use crate::{UIRegion, Vec2, WidgetArrLike, WidgetFn, WidgetLike}; + +pub trait WidgetUtil { + fn pad(self, padding: impl Into) -> impl WidgetLike; + fn center(self, size: impl Into) -> impl WidgetLike; +} + +impl WidgetUtil for W { + fn pad(self, padding: impl Into) -> impl WidgetLike { + WidgetFn(|ui| Regioned { + region: padding.into().region(), + inner: self.add(ui).erase_type(), + }) + } + + fn center(self, size: impl Into) -> impl WidgetLike { + WidgetFn(|ui| Regioned { + region: UIRegion::center(size.into()), + inner: self.add(ui).erase_type(), + }) + } +} + +pub trait WidgetArrUtil { + fn span(self, dir: Dir, lengths: [impl Into; LEN]) -> impl WidgetLike; +} + +impl> WidgetArrUtil for Wa { + fn span(self, dir: Dir, lengths: [impl Into; LEN]) -> impl WidgetLike { + let lengths = lengths.map(Into::into); + WidgetFn(move |ui| Span { + dir, + children: self.ui(ui).arr.into_iter().zip(lengths).collect(), + }) + } +} diff --git a/src/render/primitive/color.rs b/src/layout/color.rs similarity index 100% rename from src/render/primitive/color.rs rename to src/layout/color.rs diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 1d98bbf..ec38dda 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1,8 +1,15 @@ +mod color; +mod painter; +mod region; mod ui; +mod vec2; mod widget; +pub use color::*; +pub use region::*; pub use ui::*; +pub use vec2::*; pub use widget::*; +pub use painter::*; -use crate::primitive::Color; pub type UIColor = Color; diff --git a/src/render/primitive/mod.rs b/src/layout/painter.rs similarity index 72% rename from src/render/primitive/mod.rs rename to src/layout/painter.rs index 0cc037d..219fe2c 100644 --- a/src/render/primitive/mod.rs +++ b/src/layout/painter.rs @@ -1,21 +1,7 @@ -mod color; -mod def; -mod format; -mod vec2; - -pub use color::*; -pub use def::*; -pub use format::*; -pub use vec2::*; - -use crate::{render::data::PrimitiveInstance, WidgetId, Widgets}; -use bytemuck::Pod; - -#[derive(Default)] -pub struct Primitives { - pub instances: Vec, - pub data: Vec, -} +use crate::{ + UIRegion, WidgetId, Widgets, + primitive::{PrimitiveData, PrimitiveInstance, Primitives}, +}; pub struct Painter<'a> { nodes: &'a Widgets, @@ -23,11 +9,6 @@ pub struct Painter<'a> { pub region: UIRegion, } -/// NOTE: Self must have at least u32 alignment -pub trait PrimitiveData: Pod { - const DISCRIM: u32; -} - impl<'a> Painter<'a> { pub fn new(nodes: &'a Widgets) -> Self { Self { diff --git a/src/render/primitive/format.rs b/src/layout/region.rs similarity index 51% rename from src/render/primitive/format.rs rename to src/layout/region.rs index 9b5984e..a21e770 100644 --- a/src/render/primitive/format.rs +++ b/src/layout/region.rs @@ -1,4 +1,8 @@ -use crate::primitive::{Vec2, vec2::point}; +use crate::{ + Axis, Vec2, + layout::vec2, + util::{F32Util, impl_op}, +}; #[repr(C)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)] @@ -10,17 +14,17 @@ pub struct UIPos { impl UIPos { pub const fn anchor_offset(anchor_x: f32, anchor_y: f32, offset_x: f32, offset_y: f32) -> Self { Self { - anchor: point(anchor_x, anchor_y), - offset: point(offset_x, offset_y), + anchor: vec2(anchor_x, anchor_y), + offset: vec2(offset_x, offset_y), } } pub const fn center() -> Self { - Self::anchor_offset(0.0, 0.0, 0.0, 0.0) + Self::anchor_offset(0.5, 0.5, 0.0, 0.0) } pub const fn top_left() -> Self { - Self::anchor_offset(-1.0, -1.0, 0.0, 0.0) + Self::anchor_offset(0.0, 0.0, 0.0, 0.0) } pub const fn bottom_right() -> Self { @@ -33,23 +37,23 @@ impl UIPos { } pub const fn within(&self, region: &UIRegion) -> UIPos { - let lerp = self.anchor_01(); - let anchor = region.top_left.anchor.lerp(region.bot_right.anchor, lerp); - let offset = self.offset + region.top_left.offset.lerp(region.bot_right.offset, lerp); + let anchor = self + .anchor + .lerp(region.top_left.anchor, region.bot_right.anchor); + let offset = self.offset; + // + self + // .anchor + // .lerp(region.top_left.offset, region.bot_right.offset); UIPos { anchor, offset } } - pub const fn anchor_01(&self) -> Vec2 { - (self.anchor + 1.0) / 2.0 - } - - pub fn axis_mut(&mut self, axis: Axis) -> UIPosAxisView<'_> { + pub fn axis_mut(&mut self, axis: Axis) -> UIScalarView<'_> { match axis { - Axis::X => UIPosAxisView { + Axis::X => UIScalarView { anchor: &mut self.anchor.x, offset: &mut self.offset.x, }, - Axis::Y => UIPosAxisView { + Axis::Y => UIScalarView { anchor: &mut self.anchor.y, offset: &mut self.offset.y, }, @@ -57,13 +61,45 @@ impl UIPos { } } -pub struct UIPosAxisView<'a> { - pub anchor: &'a mut f32, - pub offset: &'a mut f32, +#[derive(Clone, Copy, Debug)] +pub struct UIScalar { + pub anchor: f32, + pub offset: f32, +} + +impl_op!(UIScalar Add add; anchor offset); +impl_op!(UIScalar Sub sub; anchor offset); + +impl UIScalar { + pub fn new(anchor: f32, offset: f32) -> Self { + Self { anchor, offset } + } + pub fn min() -> Self { + Self::new(0.0, 0.0) + } + + pub fn max() -> Self { + Self::new(1.0, 0.0) + } + + pub fn from_anchor(anchor: f32) -> Self { + Self::new(anchor, 0.0) + } + + pub fn offset(mut self, amt: f32) -> Self { + self.offset += amt; + self + } + + pub fn within(&self, start: UIScalar, end: UIScalar) -> Self { + let anchor = self.anchor.lerp(start.anchor, end.anchor); + let offset = self.offset + self.anchor.lerp(start.offset, end.offset); + Self { anchor, offset } + } } #[repr(C)] -#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] pub struct UIRegion { pub top_left: UIPos, pub bot_right: UIPos, @@ -100,12 +136,24 @@ impl UIRegion { } pub struct UIRegionAxisView<'a> { - pub top_left: UIPosAxisView<'a>, - pub bot_right: UIPosAxisView<'a>, + pub top_left: UIScalarView<'a>, + pub bot_right: UIScalarView<'a>, } -#[derive(Copy, Clone)] -pub enum Axis { - X, - Y, +pub struct UIScalarView<'a> { + pub anchor: &'a mut f32, + pub offset: &'a mut f32, +} + +impl UIScalarView<'_> { + pub fn set(&mut self, scalar: UIScalar) { + *self.anchor = scalar.anchor; + *self.offset = scalar.offset; + } + pub fn get(self) -> UIScalar { + UIScalar { + anchor: *self.anchor, + offset: *self.offset, + } + } } diff --git a/src/layout/ui.rs b/src/layout/ui.rs index 834c0ac..163e361 100644 --- a/src/layout/ui.rs +++ b/src/layout/ui.rs @@ -1,7 +1,7 @@ use crate::{ - primitive::{Painter, Primitives}, - util::{IDTracker, ID}, - HashMap, Widget, WidgetId, WidgetLike, WidgetRef, + HashMap, Painter, Widget, WidgetId, WidgetLike, WidgetRef, + primitive::Primitives, + util::{ID, IDTracker}, }; use std::{ any::{Any, TypeId}, diff --git a/src/layout/vec2.rs b/src/layout/vec2.rs new file mode 100644 index 0000000..f2b645c --- /dev/null +++ b/src/layout/vec2.rs @@ -0,0 +1,59 @@ +use crate::util::{F32Util, impl_op}; +use std::ops::*; + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Default, bytemuck::Pod, bytemuck::Zeroable)] +pub struct Vec2 { + pub x: f32, + pub y: f32, +} + +pub const fn vec2(x: f32, y: f32) -> Vec2 { + Vec2::new(x, y) +} + +impl Vec2 { + pub const ZERO: Self = Self::new(0.0, 0.0); + pub const ONE: Self = Self::new(1.0, 1.0); + + pub const fn new(x: f32, y: f32) -> Self { + Self { x, y } + } + + pub const fn lerp(self, from: impl const Into, to: impl const Into) -> Self { + let from = from.into(); + let to = to.into(); + Self { + x: self.x.lerp(from.x, to.x), + y: self.y.lerp(from.y, to.y), + } + } +} + +impl const From for Vec2 { + fn from(v: f32) -> Self { + Self { x: v, y: v } + } +} + +// this version looks kinda cool... is it more readable? more annoying to copy and change though +impl_op!(impl Add for Vec2: add x y); +impl_op!(Vec2 Sub sub; x y); +impl_op!(Vec2 Mul mul; x y); +impl_op!(Vec2 Div div; x y); + +impl Neg for Vec2 { + type Output = Self; + + fn neg(mut self) -> Self::Output { + self.x = -self.x; + self.y = -self.y; + self + } +} + +impl From<(f32, f32)> for Vec2 { + fn from((x, y): (f32, f32)) -> Self { + Self { x, y } + } +} diff --git a/src/layout/widget.rs b/src/layout/widget.rs index 417b115..1b38fce 100644 --- a/src/layout/widget.rs +++ b/src/layout/widget.rs @@ -3,7 +3,7 @@ use std::{ marker::PhantomData, }; -use crate::{primitive::Painter, util::ID, UIBuilder}; +use crate::{Painter, util::ID, UIBuilder}; pub trait Widget: 'static + Any { fn draw(&self, painter: &mut Painter); diff --git a/src/render/data.rs b/src/render/data.rs index 32cf6bb..c0e02b9 100644 --- a/src/render/data.rs +++ b/src/render/data.rs @@ -1,12 +1,6 @@ -use crate::primitive::UIRegion; use wgpu::VertexAttribute; -#[repr(C)] -#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -pub struct PrimitiveInstance { - pub region: UIRegion, - pub ptr: u32, -} +use crate::UIRegion; #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)] @@ -15,6 +9,13 @@ pub struct WindowUniform { pub height: f32, } +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +pub struct PrimitiveInstance { + pub region: UIRegion, + pub ptr: u32, +} + impl PrimitiveInstance { const ATTRIBS: [VertexAttribute; 5] = wgpu::vertex_attr_array![ 0 => Float32x2, diff --git a/src/render/mod.rs b/src/render/mod.rs index 1391ad8..ba1533b 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,7 +1,4 @@ -use crate::{ - render::{data::PrimitiveInstance, util::ArrBuf}, - UI, -}; +use crate::{UI, primitive::PrimitiveInstance, render::util::ArrBuf}; use data::WindowUniform; use wgpu::{ util::{BufferInitDescriptor, DeviceExt}, diff --git a/src/render/primitive.rs b/src/render/primitive.rs new file mode 100644 index 0000000..2ab1ba1 --- /dev/null +++ b/src/render/primitive.rs @@ -0,0 +1,27 @@ +use crate::Color; + +pub use super::data::PrimitiveInstance; + +#[derive(Default)] +pub struct Primitives { + pub instances: Vec, + pub data: Vec, +} + +/// NOTE: Self must have at least u32 alignment +pub trait PrimitiveData: bytemuck::Pod { + const DISCRIM: u32; +} + +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +pub struct RoundedRectData { + pub color: Color, + pub radius: f32, + pub thickness: f32, + pub inner_radius: f32, +} + +impl PrimitiveData for RoundedRectData { + const DISCRIM: u32 = 0; +} diff --git a/src/render/primitive/def.rs b/src/render/primitive/def.rs deleted file mode 100644 index b390d73..0000000 --- a/src/render/primitive/def.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::primitive::{Color, PrimitiveData}; - -#[repr(C)] -#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -pub struct RoundedRectData { - pub color: Color, - pub radius: f32, - pub thickness: f32, - pub inner_radius: f32, -} - -impl PrimitiveData for RoundedRectData { - const DISCRIM: u32 = 0; -} diff --git a/src/render/primitive/vec2.rs b/src/render/primitive/vec2.rs deleted file mode 100644 index c63709b..0000000 --- a/src/render/primitive/vec2.rs +++ /dev/null @@ -1,100 +0,0 @@ -use std::ops::*; - -#[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq, Default, bytemuck::Pod, bytemuck::Zeroable)] -pub struct Vec2 { - pub x: f32, - pub y: f32, -} - -pub const fn point(x: f32, y: f32) -> Vec2 { - Vec2::new(x, y) -} - -impl Vec2 { - pub const fn new(x: f32, y: f32) -> Self { - Self { x, y } - } - - pub const fn lerp(self, to: Self, amt: impl const Into) -> Self { - let amt = amt.into(); - Self { - x: lerp(self.x, to.x, amt.x), - y: lerp(self.y, to.y, amt.y), - } - } -} - -const fn lerp(x: f32, y: f32, amt: f32) -> f32 { - (1.0 - amt) * x + y * amt -} - -impl const From for Vec2 { - fn from(v: f32) -> Self { - Self { x: v, y: v } - } -} - -macro_rules! impl_op_inner { - ($op:ident $fn:ident $opa:ident $fna:ident) => { - impl const $op for Vec2 { - type Output = Self; - - fn $fn(self, rhs: Self) -> Self::Output { - Self { - x: self.x.$fn(rhs.x), - y: self.y.$fn(rhs.y), - } - } - } - impl $opa for Vec2 { - fn $fna(&mut self, rhs: Self) { - self.x.$fna(rhs.x); - self.y.$fna(rhs.y); - } - } - impl const $op for Vec2 { - type Output = Self; - - fn $fn(self, rhs: f32) -> Self::Output { - Self { - x: self.x.$fn(rhs), - y: self.y.$fn(rhs), - } - } - } - impl $opa for Vec2 { - fn $fna(&mut self, rhs: f32) { - self.x.$fna(rhs); - self.y.$fna(rhs); - } - } - }; -} - -macro_rules! impl_op { - ($op:ident $fn:ident) => { - impl_op_inner!($op $fn ${concat($op,Assign)} ${concat($fn,_assign)}); - }; -} - -impl_op!(Add add); -impl_op!(Sub sub); -impl_op!(Mul mul); -impl_op!(Div div); - -impl Neg for Vec2 { - type Output = Self; - - fn neg(mut self) -> Self::Output { - self.x = -self.x; - self.y = -self.y; - self - } -} - -impl From<(f32, f32)> for Vec2 { - fn from((x, y): (f32, f32)) -> Self { - Self { x, y } - } -} diff --git a/src/render/shader.wgsl b/src/render/shader.wgsl index f3eb10e..b48ff5a 100644 --- a/src/render/shader.wgsl +++ b/src/render/shader.wgsl @@ -42,8 +42,8 @@ fn vs_main( ) -> VertexOutput { var out: VertexOutput; - let top_left = (in.top_left_anchor + 1.0) / 2.0 * window.dim + in.top_left_offset; - let bot_right = (in.bottom_right_anchor + 1.0) / 2.0 * window.dim + in.bottom_right_offset; + let top_left = in.top_left_anchor * window.dim + in.top_left_offset; + let bot_right = in.bottom_right_anchor * window.dim + in.bottom_right_offset; let size = bot_right - top_left; var pos = top_left + vec2( diff --git a/src/testing/mod.rs b/src/testing/mod.rs index d5b896a..abf1dca 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use app::App; -use gui::{RoundedRect, UI, UIColor, WidgetArrUtil, WidgetUtil, primitive::Axis}; +use gui::{Dir, Rect, UI, UIColor, WidgetArrUtil, fixed, ratio, rel}; use render::Renderer; use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::Window}; @@ -19,38 +19,58 @@ pub struct Client { impl Client { pub fn new(window: Arc) -> Self { let mut renderer = Renderer::new(window); - let rect = RoundedRect { + let rect = Rect { color: UIColor::WHITE, - radius: 10.0, + radius: 50.0, thickness: 0.0, inner_radius: 0.0, }; let mut ui = UI::build(); let blue = ui.add(rect.color(UIColor::BLUE)); let handle = blue.handle(); + // let mut ui = ui.finish( + // ( + // ( + // blue, + // ( + // rect.color(UIColor::RED).center((100.0, 100.0)), + // ( + // rect.color(UIColor::ORANGE), + // rect.color(UIColor::LIME).pad(10.0), + // ) + // .span(Axis::Y, [1, 1]), + // rect.color(UIColor::YELLOW), + // ) + // .span(Axis::X, [2, 2, 1]) + // .pad(10), + // ) + // .span(Axis::X, [1, 3]), + // rect.color(UIColor::GREEN), + // ) + // .span(Axis::Y, [3, 1]) + // .pad(10), + // ); let mut ui = ui.finish( ( - ( - blue, - ( - rect.color(UIColor::RED).center((100.0, 100.0)), - ( - rect.color(UIColor::ORANGE), - rect.color(UIColor::LIME).pad(10.0), - ) - .span(Axis::Y, [1, 1]), - rect.color(UIColor::YELLOW), - ) - .span(Axis::X, [2, 2, 1]) - .pad(10), - ) - .span(Axis::X, [1, 3]), rect.color(UIColor::GREEN), + rect.color(UIColor::ORANGE), + rect.color(UIColor::CYAN), + rect.color(UIColor::BLUE), + rect.color(UIColor::MAGENTA), + rect.color(UIColor::RED), ) - .span(Axis::Y, [3, 1]) - .pad(10), + .span( + Dir::RIGHT, + [ + fixed(100), + ratio(1), + ratio(1), + rel(0.5), + fixed(100), + fixed(100), + ], + ), ); - // let mut ui = ui.finish((blue, rect.color(UIColor::RED)).span(Axis::X, [1, 1])); ui.widgets.get_mut(&handle).unwrap().color = UIColor::MAGENTA; renderer.update(&ui); Self { renderer } diff --git a/src/util/math.rs b/src/util/math.rs new file mode 100644 index 0000000..caeeb27 --- /dev/null +++ b/src/util/math.rs @@ -0,0 +1,58 @@ +use std::ops::*; + +#[const_trait] +pub trait F32Util { + fn lerp + const Add>(self, from: T, to: T) -> T; +} + +impl const F32Util for f32 { + fn lerp + const Add>(self, from: T, to: T) -> T { + from * (1.0 - self) + to * self + } +} + +macro_rules! impl_op { + ($T:ident $op:ident $fn:ident $opa:ident $fna:ident; $($field:ident)*) => { + mod ${concat(op_, $fn, _impl)} { + use super::*; + #[allow(unused_imports)] + use std::ops::*; + impl const $op for $T { + type Output = Self; + + fn $fn(self, rhs: Self) -> Self::Output { + Self { + $($field: self.$field.$fn(rhs.$field),)* + } + } + } + impl $opa for $T { + fn $fna(&mut self, rhs: Self) { + $(self.$field.$fna(rhs.$field);)* + } + } + impl const $op for $T { + type Output = Self; + + fn $fn(self, rhs: f32) -> Self::Output { + Self { + $($field: self.$field.$fn(rhs),)* + } + } + } + impl $opa for $T { + fn $fna(&mut self, rhs: f32) { + $(self.$field.$fna(rhs);)* + } + } + } + }; + ($T:ident $op:ident $fn:ident; $($field:ident)*) => { + impl_op!($T $op $fn ${concat($op,Assign)} ${concat($fn,_assign)}; $($field)*); + }; + (impl $op:ident for $T:ident: $fn:ident $($field:ident)*) => { + impl_op!($T $op $fn ${concat($op,Assign)} ${concat($fn,_assign)}; $($field)*); + }; +} + +pub(crate) use impl_op; diff --git a/src/util/mod.rs b/src/util/mod.rs index 3e76920..bd5aaa0 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,3 +1,5 @@ mod id; +mod math; pub use id::*; +pub use math::*;