diff --git a/hui-examples/examples/mom_downloader.rs b/hui-examples/examples/mom_downloader.rs index 82e861d..6855523 100644 --- a/hui-examples/examples/mom_downloader.rs +++ b/hui-examples/examples/mom_downloader.rs @@ -6,9 +6,14 @@ use winit::{ event_loop::{EventLoopBuilder, ControlFlow} }; use hui::{ - draw::CornerRadius, element::{ - container::{Alignment, Container, Sides}, progress_bar::ProgressBar, text::Text - }, elements, UiDirection, UiInstance, UiSize + UiInstance, elements, + layout::{Alignment, UiDirection, UiSize}, + rectangle::{Corners, Sides}, + element::{ + container::Container, + progress_bar::ProgressBar, + text::Text, + }, }; use hui_glium::GliumUiRenderer; @@ -52,7 +57,7 @@ fn main() { align: (Alignment::Begin, Alignment::Begin), size: (UiSize::Static(450.), UiSize::Auto), background: Some(vec4(0.2, 0.2, 0.5, 1.)), - corner_radius: Some(CornerRadius::all(8.)), + corner_radius: Some(Corners::all(8.)), elements: elements(|el| { if instant.elapsed().as_secs_f32() < 5. { el.add(Text { diff --git a/hui-examples/examples/rounded.rs b/hui-examples/examples/rounded.rs index a4911c7..c8de0df 100644 --- a/hui-examples/examples/rounded.rs +++ b/hui-examples/examples/rounded.rs @@ -5,10 +5,13 @@ use winit::{ event_loop::{EventLoopBuilder, ControlFlow} }; use hui::{ - draw::CornerRadius, element::{ - container::{Alignment, Container, Sides}, + UiInstance, + layout::{Alignment, UiSize, UiDirection}, + rectangle::{Corners, Sides}, + element::{ + container::Container, text::Text - }, UiDirection, UiInstance, UiSize + }, }; use hui_glium::GliumUiRenderer; @@ -41,7 +44,7 @@ fn main() { align: (Alignment::Center, Alignment::Center), size: (UiSize::Fraction(0.5), UiSize::Fraction(0.5)), background: Some(vec4(1., 0., 0., 1.)), - corner_radius: Some(CornerRadius { + corner_radius: Some(Corners { top_left: 10., top_right: 20., bottom_left: 50., @@ -54,7 +57,7 @@ fn main() { align: (Alignment::Center, Alignment::Center), size: (UiSize::Auto, UiSize::Auto), background: Some(vec4(0.1, 0.1, 0.1, 0.5)), - corner_radius: Some(CornerRadius::all(8.)), + corner_radius: Some(Corners::all(8.)), elements: vec![ Box::new(Text { text: "Corners".into(), diff --git a/hui-examples/examples/stress.rs b/hui-examples/examples/stress.rs index 7ff53d3..342ce4d 100644 --- a/hui-examples/examples/stress.rs +++ b/hui-examples/examples/stress.rs @@ -7,12 +7,13 @@ use winit::{ }; use hui::{ UiInstance, + layout::UiSize, + rectangle::Sides, element::{ - UiElement, + container::Container, progress_bar::ProgressBar, - container::{Container, Sides} + UiElement }, - UiSize }; use hui_glium::GliumUiRenderer; diff --git a/hui-examples/examples/test.rs b/hui-examples/examples/test.rs index b784490..0b55397 100644 --- a/hui-examples/examples/test.rs +++ b/hui-examples/examples/test.rs @@ -6,11 +6,13 @@ use winit::{ event_loop::{EventLoopBuilder, ControlFlow} }; use hui::{ - UiInstance, UiSize, UiDirection, + UiInstance, + layout::{Alignment, UiSize, UiDirection}, + rectangle::{Sides, Corners}, element::{ UiElement, progress_bar::ProgressBar, - container::{Container, Sides, Alignment}, + container::Container, rect::Rect }, }; @@ -116,6 +118,12 @@ fn main() { left: 30., right: 40., }, + corner_radius: Some(Corners { + top_left: 0., + top_right: 30., + bottom_left: 0., + bottom_right: 0., + }), elements: vec![ Box::new(Rect { size: (UiSize::Static(50.), UiSize::Static(50.)), diff --git a/hui-examples/examples/test2.rs b/hui-examples/examples/test2.rs index da9b2e2..421c904 100644 --- a/hui-examples/examples/test2.rs +++ b/hui-examples/examples/test2.rs @@ -5,9 +5,11 @@ use winit::{ event_loop::{EventLoopBuilder, ControlFlow} }; use hui::{ - UiInstance, UiSize, + UiInstance, + rectangle::Sides, + layout::{UiSize, Alignment}, element::{ - container::{Alignment, Container, Sides}, + container::Container, text::Text, } }; diff --git a/hui-examples/examples/text_weird.rs b/hui-examples/examples/text_weird.rs index b02b890..f3ecae5 100644 --- a/hui-examples/examples/text_weird.rs +++ b/hui-examples/examples/text_weird.rs @@ -6,13 +6,12 @@ use winit::{ event_loop::{EventLoopBuilder, ControlFlow} }; use hui::{ - UiInstance, + UiInstance, elements, + layout::UiSize, element::{ container::Container, text::Text, rect::Rect, spacer::Spacer }, - UiSize, - elements, }; use hui_glium::GliumUiRenderer; diff --git a/hui/Cargo.toml b/hui/Cargo.toml index f0518aa..0e82277 100644 --- a/hui/Cargo.toml +++ b/hui/Cargo.toml @@ -26,14 +26,18 @@ nz = "0.3" document-features = "0.2" [features] -default = ["builtin_elements", "builtin_font", "pixel_perfect"] +default = ["builtin_elements", "builtin_font", "pixel_perfect_text"] ## Enable the built-in font (ProggyTiny, adds 35kb to the executable) builtin_font = [] ## Enable the built-in elements (`Container`, `ProgressBar`, etc.)\ builtin_elements = ["builtin_container"] ## Enable only the `Container` component (which is essential for laying out other components) builtin_container = [] -## Round vertex positions to nearest integer coordinates (fixes blurry text) -pixel_perfect = [] - -#parallel = ["dep:rayon", "fontdue/parallel"] +## Round all vertex positions to nearest integer coordinates (not recommended) +pixel_perfect = ["pixel_perfect_text"] +## Apply pixel-perfect rendering hack to text (fixes blurry text rendering) +pixel_perfect_text = [] +#! Make sure to disable the `pixel_perfect` feature if you are rendering UI in 3D space\ +#! or using DPI (or any other form of) scaling while passing the virtual resolution to the ui +# ## Enable multi-threading support (currently only affects some 3rd-party libraries) +# parallel = ["fontdue/parallel"] diff --git a/hui/src/draw.rs b/hui/src/draw.rs index 5132954..5c13283 100644 --- a/hui/src/draw.rs +++ b/hui/src/draw.rs @@ -2,7 +2,7 @@ use crate::{IfModified, text::{TextRenderer, FontHandle}}; mod corner_radius; -pub use corner_radius::{CornerRadius, RoundedCorners}; +pub use corner_radius::RoundedCorners; use std::borrow::Cow; use fontdue::layout::{Layout, CoordinateSystem, TextStyle}; use glam::{Vec2, Vec4, vec2}; @@ -157,7 +157,7 @@ impl UiDrawPlan { match command { UiDrawCommand::Rectangle { position, size, color, rounded_corners } => { let vidx = swapper.current().vertices.len() as u32; - if let Some(corner) = rounded_corners.filter(|x| x.radius.max() > 0.0) { + if let Some(corner) = rounded_corners.filter(|x| x.radius.max_f32() > 0.0) { //this code is stupid as fuck //Random vert in the center for no reason @@ -271,6 +271,10 @@ impl UiDrawPlan { todo!("circle draw command not implemented yet") }, UiDrawCommand::Text { position, size, color, text, font } => { + if text.is_empty() { + continue + } + //XXX: should we be doing this every time? let mut layout = Layout::new(CoordinateSystem::PositiveYDown); layout.append( @@ -314,12 +318,20 @@ impl UiDrawPlan { uv: vec2(p0x, p1y), }, ]); + #[cfg(all( + feature = "pixel_perfect_text", + not(feature = "pixel_perfect") + ))] { + for vtx in &mut swapper.current_mut().vertices[(vidx as usize)..] { + vtx.position = vtx.position.round() + } + } } } } #[cfg(feature = "pixel_perfect")] swapper.current_mut().vertices.iter_mut().for_each(|v| { - v.position = v.position.floor() + v.position = v.position.round() }); prev_command = Some(command); } diff --git a/hui/src/draw/corner_radius.rs b/hui/src/draw/corner_radius.rs index f9f58f0..bf2b02c 100644 --- a/hui/src/draw/corner_radius.rs +++ b/hui/src/draw/corner_radius.rs @@ -1,90 +1,25 @@ use std::num::NonZeroU16; +use crate::Corners; -#[derive(Clone, Copy, Debug, PartialEq, Default)] -pub struct CornerRadius { - pub top_left: f32, - pub top_right: f32, - pub bottom_left: f32, - pub bottom_right: f32, -} - -impl CornerRadius { - pub const fn all(radius: f32) -> Self { - Self { - top_left: radius, - top_right: radius, - bottom_left: radius, - bottom_right: radius, - } - } - - pub const fn none() -> Self { - Self::all(0.0) - } - - pub const fn top_bottom(top: f32, bottom: f32) -> Self { - Self { - top_left: top, - top_right: top, - bottom_left: bottom, - bottom_right: bottom, - } - } - - pub const fn left_right(left: f32, right: f32) -> Self { - Self { - top_left: left, - top_right: right, - bottom_left: left, - bottom_right: right, - } - } - - //XXX: should these be public? (don't see any reason to NOT expose them) - pub fn max(&self) -> f32 { - self.top_left - .max(self.top_right) - .max(self.bottom_left) - .max(self.bottom_right) - } - - pub fn point_count(&self) -> NonZeroU16 { - //Increase for higher quality - const VTX_PER_CORER_RADIUS_PIXEL: f32 = 0.5; - NonZeroU16::new( - (self.max() * VTX_PER_CORER_RADIUS_PIXEL).round() as u16 + 2 - ).unwrap() - } -} - -impl From for CornerRadius { - fn from(radius: f32) -> Self { - Self::all(radius) - } -} - -impl From<(f32, f32, f32, f32)> for CornerRadius { - fn from((top_left, top_right, bottom_left, bottom_right): (f32, f32, f32, f32)) -> Self { - Self { - top_left, - top_right, - bottom_left, - bottom_right, - } - } +fn point_count(corners: Corners) -> NonZeroU16 { + //Increase for higher quality + const VTX_PER_CORER_RADIUS_PIXEL: f32 = 0.5; + NonZeroU16::new( + (corners.max_f32() * VTX_PER_CORER_RADIUS_PIXEL).round() as u16 + 2 + ).unwrap() } #[derive(Clone, Copy, Debug, PartialEq)] pub struct RoundedCorners { - pub radius: CornerRadius, + pub radius: Corners, pub point_count: NonZeroU16, } impl RoundedCorners { - pub fn from_radius(radius: CornerRadius) -> Self { + pub fn from_radius(radius: Corners) -> Self { Self { radius, - point_count: radius.point_count(), + point_count: point_count(radius), } } } @@ -92,7 +27,7 @@ impl RoundedCorners { impl Default for RoundedCorners { fn default() -> Self { Self { - radius: CornerRadius::default(), + radius: Corners::default(), point_count: NonZeroU16::new(8).unwrap(), } } diff --git a/hui/src/element/builtin/container.rs b/hui/src/element/builtin/container.rs index 8570059..16d2d69 100644 --- a/hui/src/element/builtin/container.rs +++ b/hui/src/element/builtin/container.rs @@ -1,53 +1,17 @@ use glam::{Vec2, vec2, Vec4}; use crate::{ - draw::{CornerRadius, RoundedCorners, UiDrawCommand}, + layout::{Alignment, LayoutInfo, UiDirection, UiSize}, + rectangle::{Corners, Sides}, + draw::{RoundedCorners, UiDrawCommand}, element::{MeasureContext, ProcessContext, UiElement}, measure::{Hints, Response}, - LayoutInfo, UiDirection, UiSize }; -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum Alignment { - Begin, - Center, - End, -} - pub struct Border { pub color: Vec4, pub width: f32, } -#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)] -pub struct Sides { - pub top: T, - pub bottom: T, - pub left: T, - pub right: T, -} - -impl Sides { - #[inline] - pub fn all(value: T) -> Self { - Self { - top: value.clone(), - bottom: value.clone(), - left: value.clone(), - right: value, - } - } - - #[inline] - pub fn horizontal_vertical(horizontal: T, vertical: T) -> Self { - Self { - top: vertical.clone(), - bottom: vertical, - left: horizontal.clone(), - right: horizontal, - } - } -} - pub struct Container { // pub min_size: (UiSize, UiSize), // pub max_size: (UiSize, UiSize), @@ -62,7 +26,7 @@ pub struct Container { pub borders: Sides>, //pub clip: bool, //TODO clip children pub elements: Vec>, - pub corner_radius: Option, + pub corner_radius: Option>, } impl Default for Container { diff --git a/hui/src/interaction.rs b/hui/src/element/builtin/interactable.rs similarity index 100% rename from hui/src/interaction.rs rename to hui/src/element/builtin/interactable.rs diff --git a/hui/src/layout.rs b/hui/src/layout.rs new file mode 100644 index 0000000..4492e9d --- /dev/null +++ b/hui/src/layout.rs @@ -0,0 +1,33 @@ +//! Layout related types and functions + +use glam::Vec2; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, PartialOrd, Ord)] +pub enum Alignment { + #[default] + Begin = 0, + Center = 1, + End = 2, +} + +#[derive(Default, Debug, Clone, Copy)] +pub enum UiSize { + #[default] + Auto, + Fraction(f32), + Static(f32), +} + +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum UiDirection { + #[default] + Vertical, + Horizontal, +} + +pub struct LayoutInfo { + ///Not availabe during measuring step + pub position: Vec2, + pub max_size: Vec2, + pub direction: UiDirection, +} diff --git a/hui/src/lib.rs b/hui/src/lib.rs index ce124ee..22f2961 100644 --- a/hui/src/lib.rs +++ b/hui/src/lib.rs @@ -10,6 +10,8 @@ use std::collections::VecDeque; +pub mod layout; +pub mod rectangle; pub mod element; pub mod event; pub mod input; @@ -17,8 +19,9 @@ pub mod draw; pub mod measure; pub mod state; pub mod text; -pub mod interaction; +use layout::{UiDirection, UiSize, LayoutInfo}; +use rectangle::{Corners, Sides}; use element::{MeasureContext, ProcessContext, UiElement}; use event::UiEvent; use state::StateRepo; @@ -122,35 +125,20 @@ impl Default for UiInstance { } } -#[derive(Default, Debug, Clone, Copy)] -pub enum UiSize { - #[default] - Auto, - Fraction(f32), - Static(f32), -} - -#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum UiDirection { - #[default] - Vertical, - Horizontal, -} - -pub struct LayoutInfo { - ///Not availabe during measuring step - pub position: Vec2, - pub max_size: Vec2, - pub direction: UiDirection, -} - +#[allow(deprecated)] +#[deprecated(since = "0.1.0-alpha.3", note = "will be removed in the next release")] pub struct ElementList(Vec>); +#[allow(deprecated)] +#[deprecated(since = "0.1.0-alpha.3", note = "will be removed in the next release")] impl ElementList { pub fn add(&mut self, element: impl UiElement + 'static) { self.0.push(Box::new(element)); } } + +#[allow(deprecated)] +#[deprecated(since = "0.1.0-alpha.3", note = "will be removed in the next release")] pub fn elements(f: impl FnOnce(&mut ElementList)) -> Vec> { let mut elements = ElementList(Vec::new()); f(&mut elements); diff --git a/hui/src/rectangle.rs b/hui/src/rectangle.rs new file mode 100644 index 0000000..5f80ece --- /dev/null +++ b/hui/src/rectangle.rs @@ -0,0 +1,119 @@ +//! This module contains the definitions of the `Sides` and `Corners` structs, +//! which represent the sides and corners of a rectangular shape. + +/// Represents 4 sides of a rectangular shape. +#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)] +pub struct Sides { + pub top: T, + pub bottom: T, + pub left: T, + pub right: T, +} + +impl Sides { + #[inline] + pub fn all(value: T) -> Self { + Self { + top: value.clone(), + bottom: value.clone(), + left: value.clone(), + right: value, + } + } + + #[inline] + pub fn horizontal_vertical(horizontal: T, vertical: T) -> Self { + Self { + top: vertical.clone(), + bottom: vertical, + left: horizontal.clone(), + right: horizontal, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +pub struct Corners { + pub top_left: T, + pub top_right: T, + pub bottom_left: T, + pub bottom_right: T, +} + +impl Corners { + #[inline] + pub fn all(value: T) -> Self { + Self { + top_left: value.clone(), + top_right: value.clone(), + bottom_left: value.clone(), + bottom_right: value, + } + } + + #[inline] + pub fn top_bottom(top: T, bottom: T) -> Self { + Self { + top_left: top.clone(), + top_right: top, + bottom_left: bottom.clone(), + bottom_right: bottom, + } + } + + #[inline] + pub fn left_right(left: T, right: T) -> Self { + Self { + top_left: left.clone(), + top_right: right.clone(), + bottom_left: left, + bottom_right: right, + } + } +} + +impl Corners { + pub fn max(&self) -> T { + self.top_left.clone() + .max(self.top_right.clone()) + .max(self.bottom_left.clone()) + .max(self.bottom_right.clone()) + .clone() + } +} + +/// Represents 4 corners of a rectangular shape. +impl Corners { + pub fn max_f32(&self) -> f32 { + self.top_left + .max(self.top_right) + .max(self.bottom_left) + .max(self.bottom_right) + } +} + +impl Corners { + pub fn max_f64(&self) -> f64 { + self.top_left + .max(self.top_right) + .max(self.bottom_left) + .max(self.bottom_right) + } +} + +impl From for Corners { + fn from(value: T) -> Self { + Self::all(value) + } +} + +impl From<(T, T, T, T)> for Corners { + fn from((top_left, top_right, bottom_left, bottom_right): (T, T, T, T)) -> Self { + Self { + top_left, + top_right, + bottom_left, + bottom_right, + } + } +}