From 915b99c3a593704ed2f9cd519d97902c981ae6fe Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Sat, 23 Mar 2024 01:57:07 +0100 Subject: [PATCH] frame stuff --- hui/Cargo.toml | 1 + hui/src/frame.rs | 211 +++++------------------------------------ hui/src/frame/layer.rs | 90 ++++++++++++++++++ hui/src/frame/point.rs | 198 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 314 insertions(+), 186 deletions(-) create mode 100644 hui/src/frame/layer.rs create mode 100644 hui/src/frame/point.rs diff --git a/hui/Cargo.toml b/hui/Cargo.toml index 62b8bbb..b326864 100644 --- a/hui/Cargo.toml +++ b/hui/Cargo.toml @@ -26,6 +26,7 @@ document-features = "0.2" derive_setters = "0.1" derive_more = "0.99" tinyset = "0.4" +enum_dispatch = "0.3" [features] default = ["builtin_elements", "builtin_font", "pixel_perfect_text"] diff --git a/hui/src/frame.rs b/hui/src/frame.rs index ee8fb66..7c4410a 100644 --- a/hui/src/frame.rs +++ b/hui/src/frame.rs @@ -1,195 +1,34 @@ -use glam::{Vec2, vec2}; -use derive_more::{Add, AddAssign, Sub, SubAssign}; -use crate::{ - draw::ImageHandle, - element::fill_rect::FillRect, - layout::{Size, Size2d} -}; +use crate::rect::FillColor; -//TODO finish dis, slider component would be a great place to test it +pub mod point; +pub mod layer; -/// Point inside a frame +use layer::{FrameLayer, RectLayer}; + +///XXX: this is not used yet, and also kinda a mess, simplify? +///Maybe limit to a single layer? (aka `Frame` will be just one of the options) +///aka Frame::Rectangle, Frame::NinePatch, ... + +/// A frame, which can contain multiple layers /// -/// Can be absolute, relative, or a combination of both\ -/// Final coordinate is calculated as `absolute + relative * parent_size` -#[derive(Clone, Copy, Debug, Default, Add, AddAssign, Sub, SubAssign)] -pub struct FramePoint { - /// Absolute positioning - pub absolute: f32, - - /// Relative positioning - pub relative: f32, -} - -impl From for FramePoint { - fn from(value: f32) -> Self { - Self::absolute(value) - } -} - -impl From for FramePoint { - /// Convert a `Size` into a `FramePoint` - /// - /// This function behaves just as you would expect, but `Auto` is always treated as `BEGIN` - fn from(size: Size) -> Self { - match size { - Size::Auto => Self::BEGIN, - Size::Relative(value) => Self::relative(value), - Size::Absolute(value) => Self::absolute(value), - } - } -} - -impl FramePoint { - pub const BEGIN: Self = Self { - absolute: 0.0, - relative: 0.0, - }; - - pub const CENTER: Self = Self { - absolute: 0.5, - relative: 0.0, - }; - - pub const END: Self = Self { - absolute: 1.0, - relative: 0.0, - }; - - pub const fn absolute(value: f32) -> Self { - Self { - absolute: value, - relative: 0.0, - } - } - - pub const fn relative(value: f32) -> Self { - Self { - absolute: 0.0, - relative: value, - } - } - - pub const fn relative_absolute(relative: f32, absolute: f32) -> Self { - Self { - absolute, - relative, - } - } - - fn resolve(&self, parent_size: f32) -> f32 { - self.absolute + self.relative * parent_size - } -} - -#[derive(Clone, Copy, Debug, Default, Add, AddAssign, Sub, SubAssign)] -pub struct FramePoint2d { - pub x: FramePoint, - pub y: FramePoint, -} - -impl From<(FramePoint, FramePoint)> for FramePoint2d { - fn from((x, y): (FramePoint, FramePoint)) -> Self { - Self { x, y } - } -} - -impl From for FramePoint2d { - fn from(size: Size) -> Self { - Self { - x: size.into(), - y: size.into(), - } - } -} - -impl From for FramePoint2d { - fn from(size: Size2d) -> Self { - Self { - x: size.width.into(), - y: size.height.into(), - } - } -} - -impl From<(f32, f32)> for FramePoint2d { - fn from((x, y): (f32, f32)) -> Self { - Self { - x: FramePoint::absolute(x), - y: FramePoint::absolute(y), - } - } -} - -impl From for FramePoint2d { - fn from(vec: Vec2) -> Self { - Self { - x: FramePoint::absolute(vec.x), - y: FramePoint::absolute(vec.y), - } - } -} - -impl FramePoint2d { - pub const TOP_LEFT: Self = Self { - x: FramePoint::BEGIN, - y: FramePoint::BEGIN, - }; - pub const TOP_CENTER: Self = Self { - x: FramePoint::CENTER, - y: FramePoint::BEGIN, - }; - pub const TOP_RIGHT: Self = Self { - x: FramePoint::END, - y: FramePoint::BEGIN, - }; - pub const CENTER_LEFT: Self = Self { - x: FramePoint::BEGIN, - y: FramePoint::CENTER, - }; - pub const CENTER: Self = Self { - x: FramePoint::CENTER, - y: FramePoint::CENTER, - }; - pub const CENTER_RIGHT: Self = Self { - x: FramePoint::END, - y: FramePoint::CENTER, - }; - pub const BOTTOM_LEFT: Self = Self { - x: FramePoint::BEGIN, - y: FramePoint::END, - }; - pub const BOTTOM_CENTER: Self = Self { - x: FramePoint::CENTER, - y: FramePoint::END, - }; - pub const BOTTOM_RIGHT: Self = Self { - x: FramePoint::END, - y: FramePoint::END, - }; - - pub fn resolve(&self, parent_size: Vec2) -> Vec2 { - let x = self.x.resolve(parent_size.x); - let y = self.y.resolve(parent_size.y); - vec2(x, y) - } -} - -enum FrameLayer { - Rect { - color: FillRect, - image: Option, - top_left: FramePoint2d, - bottom_right: FramePoint2d, - } -} - +/// Use these to construct complex backgrounds +#[derive(Default, Clone)] pub struct Frame { + /// Layers of the frame layers: Vec } -impl> From for Frame { - fn from(value: T) -> Self { - todo!() +impl> From for Frame { + fn from(color: T) -> Self { + let mut frame = Self::default(); + frame.add(RectLayer::from_color(color)); + frame + } +} + +impl Frame { + pub fn add(&mut self, layer: impl Into) -> &mut Self { + self.layers.push(layer.into()); + self } } diff --git a/hui/src/frame/layer.rs b/hui/src/frame/layer.rs new file mode 100644 index 0000000..ce76648 --- /dev/null +++ b/hui/src/frame/layer.rs @@ -0,0 +1,90 @@ +use glam::Vec2; +use enum_dispatch::enum_dispatch; +use crate::{ + color, + draw::{ImageHandle, RoundedCorners, UiDrawCommand, UiDrawCommandList}, + rect::{Corners, FillColor}, +}; +use super::point::FramePoint2d; + +#[enum_dispatch] +pub(crate) trait FrameLayerImpl { + fn draw(&self, draw: &mut UiDrawCommandList, parent_size: Vec2); +} + +#[derive(Clone, Copy)] +#[enum_dispatch(FrameLayerImpl)] +pub enum FrameLayer { + Rect(RectLayer), +} + +#[derive(Clone, Copy)] +pub struct RectLayer { + color: FillColor, + image: Option, + top_left: FramePoint2d, + bottom_right: FramePoint2d, + corner_radius: Corners, +} + +impl RectLayer { + pub fn from_color(color: impl Into) -> Self { + Self { + color: color.into(), + ..Self::default() + } + } + + pub fn from_color_rounded(color: impl Into, corner_radius: impl Into>) -> Self { + Self { + color: color.into(), + corner_radius: corner_radius.into(), + ..Self::default() + } + } + + pub fn from_image(image: ImageHandle) -> Self { + Self { + color: color::WHITE.into(), + image: Some(image), + ..Self::default() + } + } + + pub fn from_color_image(color: impl Into, image: ImageHandle) -> Self { + Self { + color: color.into(), + image: Some(image), + ..Self::default() + } + } +} + +impl Default for RectLayer { + fn default() -> Self { + Self { + color: FillColor::default(), + image: None, + top_left: FramePoint2d::default(), + bottom_right: FramePoint2d::default(), + corner_radius: Corners::default(), + } + } +} + +impl FrameLayerImpl for RectLayer { + fn draw(&self, draw: &mut UiDrawCommandList, parent_size: Vec2) { + //TODO: handle bottom_right < top_left + let top_left = self.top_left.resolve(parent_size); + let bottom_right = self.bottom_right.resolve(parent_size); + draw.add(UiDrawCommand::Rectangle { + position: top_left, + size: bottom_right - top_left, + color: self.color.corners(), + texture: self.image, + rounded_corners: (self.corner_radius.max_f32() > 0.).then_some( + RoundedCorners::from_radius(self.corner_radius) + ), + }); + } +} diff --git a/hui/src/frame/point.rs b/hui/src/frame/point.rs new file mode 100644 index 0000000..cd7ce04 --- /dev/null +++ b/hui/src/frame/point.rs @@ -0,0 +1,198 @@ +use glam::{Vec2, vec2}; +use derive_more::{Add, AddAssign, Sub, SubAssign}; +use crate::layout::{Size, Size2d}; + +/// Point inside a frame +/// +/// Can be absolute, relative, or a combination of both\ +/// Final coordinate is calculated as `absolute + relative * parent_size` +#[derive(Clone, Copy, Debug, Default, Add, AddAssign, Sub, SubAssign)] +pub struct FramePoint { + /// Absolute positioning + pub absolute: f32, + + /// Relative positioning + pub relative: f32, +} + +impl From for FramePoint { + fn from(value: f32) -> Self { + Self::absolute(value) + } +} + +impl From for FramePoint { + /// Convert a `Size` into a `FramePoint` + /// + /// This function behaves just as you would expect, but `Auto` is always treated as `BEGIN` + fn from(size: Size) -> Self { + match size { + Size::Auto => Self::BEGIN, + Size::Relative(value) => Self::relative(value), + Size::Absolute(value) => Self::absolute(value), + } + } +} + +impl FramePoint { + /// Beginning of the frame axis + pub const BEGIN: Self = Self { + absolute: 0.0, + relative: 0.0, + }; + + /// Center of the frame axis + pub const CENTER: Self = Self { + absolute: 0.5, + relative: 0.0, + }; + + /// End of the frame axis + pub const END: Self = Self { + absolute: 1.0, + relative: 0.0, + }; + + /// Create a new absolutely positioned `FramePoint` + pub const fn absolute(value: f32) -> Self { + Self { + absolute: value, + relative: 0.0, + } + } + + /// Create a new relatively positioned `FramePoint` + pub const fn relative(value: f32) -> Self { + Self { + absolute: 0.0, + relative: value, + } + } + + /// Create a new `FramePoint` with both absolute and relative positioning + pub const fn relative_absolute(relative: f32, absolute: f32) -> Self { + Self { + absolute, + relative, + } + } + + /// Resolve the `FramePoint` into an absolute coordinate + pub(crate) fn resolve(&self, parent_size: f32) -> f32 { + self.absolute + self.relative * parent_size + } +} + +/// A 2-dimensional [`FramePoint`] +#[derive(Clone, Copy, Debug, Default, Add, AddAssign, Sub, SubAssign)] +pub struct FramePoint2d { + pub x: FramePoint, + pub y: FramePoint, +} + +impl From<(FramePoint, FramePoint)> for FramePoint2d { + fn from((x, y): (FramePoint, FramePoint)) -> Self { + Self { x, y } + } +} + +impl From for FramePoint2d { + fn from(size: Size) -> Self { + Self { + x: size.into(), + y: size.into(), + } + } +} + +impl From for FramePoint2d { + fn from(size: Size2d) -> Self { + Self { + x: size.width.into(), + y: size.height.into(), + } + } +} + +impl From<(f32, f32)> for FramePoint2d { + fn from((x, y): (f32, f32)) -> Self { + Self { + x: FramePoint::absolute(x), + y: FramePoint::absolute(y), + } + } +} + +impl From for FramePoint2d { + fn from(vec: Vec2) -> Self { + Self { + x: FramePoint::absolute(vec.x), + y: FramePoint::absolute(vec.y), + } + } +} + +impl FramePoint2d { + /// Top left corner of the frame + pub const TOP_LEFT: Self = Self { + x: FramePoint::BEGIN, + y: FramePoint::BEGIN, + }; + + /// Top center of the frame + pub const TOP_CENTER: Self = Self { + x: FramePoint::CENTER, + y: FramePoint::BEGIN, + }; + + /// Top right corner of the frame + pub const TOP_RIGHT: Self = Self { + x: FramePoint::END, + y: FramePoint::BEGIN, + }; + + /// Center left of the frame + pub const CENTER_LEFT: Self = Self { + x: FramePoint::BEGIN, + y: FramePoint::CENTER, + }; + + /// Center of the frame + pub const CENTER: Self = Self { + x: FramePoint::CENTER, + y: FramePoint::CENTER, + }; + + /// Center right of the frame + pub const CENTER_RIGHT: Self = Self { + x: FramePoint::END, + y: FramePoint::CENTER, + }; + + /// Bottom left corner of the frame + pub const BOTTOM_LEFT: Self = Self { + x: FramePoint::BEGIN, + y: FramePoint::END, + }; + + /// Bottom center of the frame + pub const BOTTOM_CENTER: Self = Self { + x: FramePoint::CENTER, + y: FramePoint::END, + }; + + /// Bottom right corner of the frame + pub const BOTTOM_RIGHT: Self = Self { + x: FramePoint::END, + y: FramePoint::END, + }; + + /// Resolve the `FramePoint2d` into an absolute coordinate within the frame's coordinate system\ + /// + /// (point with absolute cordinates, relative to the frame's top-left corner) + pub(crate) fn resolve(&self, parent_size: Vec2) -> Vec2 { + let x = self.x.resolve(parent_size.x); + let y = self.y.resolve(parent_size.y); + vec2(x, y) + } +}