diff --git a/hui-examples/examples/mom_downloader.rs b/hui-examples/examples/mom_downloader.rs index c1ad7e8..82e861d 100644 --- a/hui-examples/examples/mom_downloader.rs +++ b/hui-examples/examples/mom_downloader.rs @@ -6,7 +6,7 @@ use winit::{ event_loop::{EventLoopBuilder, ControlFlow} }; use hui::{ - element::{ + draw::CornerRadius, element::{ container::{Alignment, Container, Sides}, progress_bar::ProgressBar, text::Text }, elements, UiDirection, UiInstance, UiSize }; @@ -52,7 +52,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(8.), + corner_radius: Some(CornerRadius::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 033bf2c..a4911c7 100644 --- a/hui-examples/examples/rounded.rs +++ b/hui-examples/examples/rounded.rs @@ -5,11 +5,10 @@ use winit::{ event_loop::{EventLoopBuilder, ControlFlow} }; use hui::{ - UiDirection, UiInstance, UiSize, - element::{ + draw::CornerRadius, element::{ container::{Alignment, Container, Sides}, text::Text - }, + }, UiDirection, UiInstance, UiSize }; use hui_glium::GliumUiRenderer; @@ -42,7 +41,12 @@ 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(30.), + corner_radius: Some(CornerRadius { + top_left: 10., + top_right: 20., + bottom_left: 50., + bottom_right: 80. + }), elements: vec![ Box::new(Container { padding: Sides::all(20.), @@ -50,10 +54,10 @@ 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(30.), + corner_radius: Some(CornerRadius::all(8.)), elements: vec![ Box::new(Text { - text: "You tried".into(), + text: "Corners".into(), text_size: 50, color: vec4(1., 1., 1., 1.), ..Default::default() diff --git a/hui/Cargo.toml b/hui/Cargo.toml index da58f75..f3daad4 100644 --- a/hui/Cargo.toml +++ b/hui/Cargo.toml @@ -22,6 +22,7 @@ glam = "0.25" fontdue = "0.8" rect_packer = "0.2" log = "0.4" +nz = "0.3" [features] default = ["builtin_elements", "builtin_font"] diff --git a/hui/src/draw.rs b/hui/src/draw.rs index ea1de9e..bea316a 100644 --- a/hui/src/draw.rs +++ b/hui/src/draw.rs @@ -1,5 +1,8 @@ use crate::{IfModified, text::{TextRenderer, FontHandle}}; +mod corner_radius; + +pub use corner_radius::{CornerRadius, RoundedCorners}; use std::borrow::Cow; use fontdue::layout::{Layout, CoordinateSystem, TextStyle}; use glam::{Vec2, Vec4, vec2}; @@ -14,9 +17,8 @@ pub enum UiDrawCommand { size: Vec2, ///Color (RGBA) color: Vec4, - //TODO: rounded corners per side ///Rounded corners - corner_radius: Option, + rounded_corners: Option, }, Text { ///Position in pixels @@ -132,10 +134,9 @@ impl UiDrawPlan { } match command { - UiDrawCommand::Rectangle { position, size, color, corner_radius } => { - let corner_radius = corner_radius.unwrap_or(0.0); + UiDrawCommand::Rectangle { position, size, color, rounded_corners } => { let vidx = swapper.current().vertices.len() as u32; - if corner_radius > 0.0 { + if let Some(corner) = rounded_corners.filter(|x| x.radius.max() > 0.0) { //this code is stupid as fuck //Random vert in the center for no reason @@ -146,9 +147,8 @@ impl UiDrawPlan { uv: vec2(0., 0.), }); - //TODO: make this configurable or compute dynamically //TODO: fix some corners tris being invisible (close enough lol) - let rounded_corner_verts = 8; + let rounded_corner_verts = corner.point_count.get() as u32; for i in 0..rounded_corner_verts { let cratio = i as f32 / rounded_corner_verts as f32; let angle = cratio * std::f32::consts::PI * 0.5; @@ -156,25 +156,25 @@ impl UiDrawPlan { let y = angle.cos(); //Top-right corner swapper.current_mut().vertices.push(UiVertex { - position: *position + vec2(x, 1. - y) * corner_radius + vec2(size.x - corner_radius, 0.), + position: *position + vec2(x, 1. - y) * corner.radius.top_right + vec2(size.x - corner.radius.top_right, 0.), color: *color, uv: vec2(0.0, 0.0), }); //Bottom-right corner swapper.current_mut().vertices.push(UiVertex { - position: *position + vec2(x - 1., y) * corner_radius + vec2(size.x, size.y - corner_radius), + position: *position + vec2(x - 1., y) * corner.radius.bottom_right + vec2(size.x, size.y - corner.radius.bottom_right), color: *color, uv: vec2(0.0, 0.0), }); //Bottom-left corner swapper.current_mut().vertices.push(UiVertex { - position: *position + vec2(1. - x, y) * corner_radius + vec2(0., size.y - corner_radius), + position: *position + vec2(1. - x, y) * corner.radius.bottom_left + vec2(0., size.y - corner.radius.bottom_left), color: *color, uv: vec2(0.0, 0.0), }); //Top-left corner swapper.current_mut().vertices.push(UiVertex { - position: *position + vec2(1. - x, 1. - y) * corner_radius, + position: *position + vec2(1. - x, 1. - y) * corner.radius.top_left, color: *color, uv: vec2(0.0, 0.0), }); diff --git a/hui/src/draw/corner_radius.rs b/hui/src/draw/corner_radius.rs new file mode 100644 index 0000000..f9f58f0 --- /dev/null +++ b/hui/src/draw/corner_radius.rs @@ -0,0 +1,99 @@ +use std::num::NonZeroU16; + +#[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, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct RoundedCorners { + pub radius: CornerRadius, + pub point_count: NonZeroU16, +} + +impl RoundedCorners { + pub fn from_radius(radius: CornerRadius) -> Self { + Self { + radius, + point_count: radius.point_count(), + } + } +} + +impl Default for RoundedCorners { + fn default() -> Self { + Self { + radius: CornerRadius::default(), + point_count: NonZeroU16::new(8).unwrap(), + } + } +} diff --git a/hui/src/element/builtin/container.rs b/hui/src/element/builtin/container.rs index 61bccf6..8570059 100644 --- a/hui/src/element/builtin/container.rs +++ b/hui/src/element/builtin/container.rs @@ -1,6 +1,6 @@ use glam::{Vec2, vec2, Vec4}; use crate::{ - draw::UiDrawCommand, + draw::{CornerRadius, RoundedCorners, UiDrawCommand}, element::{MeasureContext, ProcessContext, UiElement}, measure::{Hints, Response}, LayoutInfo, UiDirection, UiSize @@ -62,7 +62,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 { @@ -170,7 +170,7 @@ impl UiElement for Container { position, size: ctx.measure.size, color, - corner_radius: self.corner_radius, + rounded_corners: self.corner_radius.map(RoundedCorners::from_radius), }); } diff --git a/hui/src/element/builtin/progress_bar.rs b/hui/src/element/builtin/progress_bar.rs index 06d5eea..550b4cb 100644 --- a/hui/src/element/builtin/progress_bar.rs +++ b/hui/src/element/builtin/progress_bar.rs @@ -56,7 +56,7 @@ impl UiElement for ProgressBar { position: ctx.layout.position, size: ctx.measure.size, color: self.color_background, - corner_radius: None, + rounded_corners: None, }); } if value > 0. { @@ -64,7 +64,7 @@ impl UiElement for ProgressBar { position: ctx.layout.position, size: ctx.measure.size * vec2(value, 1.0), color: self.color_foreground, - corner_radius: None, + rounded_corners: None, }); } } diff --git a/hui/src/element/builtin/rect.rs b/hui/src/element/builtin/rect.rs index 30f6846..bae669c 100644 --- a/hui/src/element/builtin/rect.rs +++ b/hui/src/element/builtin/rect.rs @@ -46,7 +46,7 @@ impl UiElement for Rect { position: ctx.layout.position, size: ctx.measure.size, color, - corner_radius: None, + rounded_corners: None, }); } } diff --git a/hui/src/input.rs b/hui/src/input.rs new file mode 100644 index 0000000..3246304 --- /dev/null +++ b/hui/src/input.rs @@ -0,0 +1,3 @@ +pub(crate) struct UiInputState { + +} diff --git a/hui/src/lib.rs b/hui/src/lib.rs index 3e7e366..db697e7 100644 --- a/hui/src/lib.rs +++ b/hui/src/lib.rs @@ -5,8 +5,11 @@ //! Simple UI library for games and other interactive applications //! +use std::collections::VecDeque; + pub mod element; pub mod event; +pub mod input; pub mod draw; pub mod measure; pub mod state; @@ -14,6 +17,7 @@ pub mod text; pub mod interaction; use element::{MeasureContext, ProcessContext, UiElement}; +use event::UiEvent; use state::StateRepo; use draw::{UiDrawCommands, UiDrawPlan}; use text::{TextRenderer, FontTextureInfo, FontHandle}; @@ -37,6 +41,7 @@ pub struct UiInstance { draw_plan: UiDrawPlan, draw_plan_modified: bool, text_renderer: TextRenderer, + events: VecDeque, } impl UiInstance { @@ -52,6 +57,7 @@ impl UiInstance { draw_plan_modified: false, // ftm: FontTextureManager::default(), text_renderer: TextRenderer::new(), + events: VecDeque::new(), } } @@ -101,6 +107,10 @@ impl UiInstance { pub fn font_texture(&self) -> FontTextureInfo { self.text_renderer.font_texture() } + + pub fn push_event(&mut self, event: UiEvent) { + self.events.push_back(event); + } } impl Default for UiInstance {