update rounded corner api

This commit is contained in:
griffi-gh 2024-02-19 19:40:18 +01:00
parent c72a005ff6
commit 83c00e8c13
10 changed files with 142 additions and 25 deletions

View file

@ -6,7 +6,7 @@ use winit::{
event_loop::{EventLoopBuilder, ControlFlow} event_loop::{EventLoopBuilder, ControlFlow}
}; };
use hui::{ use hui::{
element::{ draw::CornerRadius, element::{
container::{Alignment, Container, Sides}, progress_bar::ProgressBar, text::Text container::{Alignment, Container, Sides}, progress_bar::ProgressBar, text::Text
}, elements, UiDirection, UiInstance, UiSize }, elements, UiDirection, UiInstance, UiSize
}; };
@ -52,7 +52,7 @@ fn main() {
align: (Alignment::Begin, Alignment::Begin), align: (Alignment::Begin, Alignment::Begin),
size: (UiSize::Static(450.), UiSize::Auto), size: (UiSize::Static(450.), UiSize::Auto),
background: Some(vec4(0.2, 0.2, 0.5, 1.)), background: Some(vec4(0.2, 0.2, 0.5, 1.)),
corner_radius: Some(8.), corner_radius: Some(CornerRadius::all(8.)),
elements: elements(|el| { elements: elements(|el| {
if instant.elapsed().as_secs_f32() < 5. { if instant.elapsed().as_secs_f32() < 5. {
el.add(Text { el.add(Text {

View file

@ -5,11 +5,10 @@ use winit::{
event_loop::{EventLoopBuilder, ControlFlow} event_loop::{EventLoopBuilder, ControlFlow}
}; };
use hui::{ use hui::{
UiDirection, UiInstance, UiSize, draw::CornerRadius, element::{
element::{
container::{Alignment, Container, Sides}, container::{Alignment, Container, Sides},
text::Text text::Text
}, }, UiDirection, UiInstance, UiSize
}; };
use hui_glium::GliumUiRenderer; use hui_glium::GliumUiRenderer;
@ -42,7 +41,12 @@ fn main() {
align: (Alignment::Center, Alignment::Center), align: (Alignment::Center, Alignment::Center),
size: (UiSize::Fraction(0.5), UiSize::Fraction(0.5)), size: (UiSize::Fraction(0.5), UiSize::Fraction(0.5)),
background: Some(vec4(1., 0., 0., 1.)), 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![ elements: vec![
Box::new(Container { Box::new(Container {
padding: Sides::all(20.), padding: Sides::all(20.),
@ -50,10 +54,10 @@ fn main() {
align: (Alignment::Center, Alignment::Center), align: (Alignment::Center, Alignment::Center),
size: (UiSize::Auto, UiSize::Auto), size: (UiSize::Auto, UiSize::Auto),
background: Some(vec4(0.1, 0.1, 0.1, 0.5)), background: Some(vec4(0.1, 0.1, 0.1, 0.5)),
corner_radius: Some(30.), corner_radius: Some(CornerRadius::all(8.)),
elements: vec![ elements: vec![
Box::new(Text { Box::new(Text {
text: "You tried".into(), text: "Corners".into(),
text_size: 50, text_size: 50,
color: vec4(1., 1., 1., 1.), color: vec4(1., 1., 1., 1.),
..Default::default() ..Default::default()

View file

@ -22,6 +22,7 @@ glam = "0.25"
fontdue = "0.8" fontdue = "0.8"
rect_packer = "0.2" rect_packer = "0.2"
log = "0.4" log = "0.4"
nz = "0.3"
[features] [features]
default = ["builtin_elements", "builtin_font"] default = ["builtin_elements", "builtin_font"]

View file

@ -1,5 +1,8 @@
use crate::{IfModified, text::{TextRenderer, FontHandle}}; use crate::{IfModified, text::{TextRenderer, FontHandle}};
mod corner_radius;
pub use corner_radius::{CornerRadius, RoundedCorners};
use std::borrow::Cow; use std::borrow::Cow;
use fontdue::layout::{Layout, CoordinateSystem, TextStyle}; use fontdue::layout::{Layout, CoordinateSystem, TextStyle};
use glam::{Vec2, Vec4, vec2}; use glam::{Vec2, Vec4, vec2};
@ -14,9 +17,8 @@ pub enum UiDrawCommand {
size: Vec2, size: Vec2,
///Color (RGBA) ///Color (RGBA)
color: Vec4, color: Vec4,
//TODO: rounded corners per side
///Rounded corners ///Rounded corners
corner_radius: Option<f32>, rounded_corners: Option<RoundedCorners>,
}, },
Text { Text {
///Position in pixels ///Position in pixels
@ -132,10 +134,9 @@ impl UiDrawPlan {
} }
match command { match command {
UiDrawCommand::Rectangle { position, size, color, corner_radius } => { UiDrawCommand::Rectangle { position, size, color, rounded_corners } => {
let corner_radius = corner_radius.unwrap_or(0.0);
let vidx = swapper.current().vertices.len() as u32; 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 //this code is stupid as fuck
//Random vert in the center for no reason //Random vert in the center for no reason
@ -146,9 +147,8 @@ impl UiDrawPlan {
uv: vec2(0., 0.), uv: vec2(0., 0.),
}); });
//TODO: make this configurable or compute dynamically
//TODO: fix some corners tris being invisible (close enough lol) //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 { for i in 0..rounded_corner_verts {
let cratio = i as f32 / rounded_corner_verts as f32; let cratio = i as f32 / rounded_corner_verts as f32;
let angle = cratio * std::f32::consts::PI * 0.5; let angle = cratio * std::f32::consts::PI * 0.5;
@ -156,25 +156,25 @@ impl UiDrawPlan {
let y = angle.cos(); let y = angle.cos();
//Top-right corner //Top-right corner
swapper.current_mut().vertices.push(UiVertex { 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, color: *color,
uv: vec2(0.0, 0.0), uv: vec2(0.0, 0.0),
}); });
//Bottom-right corner //Bottom-right corner
swapper.current_mut().vertices.push(UiVertex { 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, color: *color,
uv: vec2(0.0, 0.0), uv: vec2(0.0, 0.0),
}); });
//Bottom-left corner //Bottom-left corner
swapper.current_mut().vertices.push(UiVertex { 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, color: *color,
uv: vec2(0.0, 0.0), uv: vec2(0.0, 0.0),
}); });
//Top-left corner //Top-left corner
swapper.current_mut().vertices.push(UiVertex { 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, color: *color,
uv: vec2(0.0, 0.0), uv: vec2(0.0, 0.0),
}); });

View file

@ -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<f32> 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(),
}
}
}

View file

@ -1,6 +1,6 @@
use glam::{Vec2, vec2, Vec4}; use glam::{Vec2, vec2, Vec4};
use crate::{ use crate::{
draw::UiDrawCommand, draw::{CornerRadius, RoundedCorners, UiDrawCommand},
element::{MeasureContext, ProcessContext, UiElement}, element::{MeasureContext, ProcessContext, UiElement},
measure::{Hints, Response}, measure::{Hints, Response},
LayoutInfo, UiDirection, UiSize LayoutInfo, UiDirection, UiSize
@ -62,7 +62,7 @@ pub struct Container {
pub borders: Sides<Option<Border>>, pub borders: Sides<Option<Border>>,
//pub clip: bool, //TODO clip children //pub clip: bool, //TODO clip children
pub elements: Vec<Box<dyn UiElement>>, pub elements: Vec<Box<dyn UiElement>>,
pub corner_radius: Option<f32>, pub corner_radius: Option<CornerRadius>,
} }
impl Default for Container { impl Default for Container {
@ -170,7 +170,7 @@ impl UiElement for Container {
position, position,
size: ctx.measure.size, size: ctx.measure.size,
color, color,
corner_radius: self.corner_radius, rounded_corners: self.corner_radius.map(RoundedCorners::from_radius),
}); });
} }

View file

@ -56,7 +56,7 @@ impl UiElement for ProgressBar {
position: ctx.layout.position, position: ctx.layout.position,
size: ctx.measure.size, size: ctx.measure.size,
color: self.color_background, color: self.color_background,
corner_radius: None, rounded_corners: None,
}); });
} }
if value > 0. { if value > 0. {
@ -64,7 +64,7 @@ impl UiElement for ProgressBar {
position: ctx.layout.position, position: ctx.layout.position,
size: ctx.measure.size * vec2(value, 1.0), size: ctx.measure.size * vec2(value, 1.0),
color: self.color_foreground, color: self.color_foreground,
corner_radius: None, rounded_corners: None,
}); });
} }
} }

View file

@ -46,7 +46,7 @@ impl UiElement for Rect {
position: ctx.layout.position, position: ctx.layout.position,
size: ctx.measure.size, size: ctx.measure.size,
color, color,
corner_radius: None, rounded_corners: None,
}); });
} }
} }

3
hui/src/input.rs Normal file
View file

@ -0,0 +1,3 @@
pub(crate) struct UiInputState {
}

View file

@ -5,8 +5,11 @@
//! Simple UI library for games and other interactive applications //! Simple UI library for games and other interactive applications
//! //!
use std::collections::VecDeque;
pub mod element; pub mod element;
pub mod event; pub mod event;
pub mod input;
pub mod draw; pub mod draw;
pub mod measure; pub mod measure;
pub mod state; pub mod state;
@ -14,6 +17,7 @@ pub mod text;
pub mod interaction; pub mod interaction;
use element::{MeasureContext, ProcessContext, UiElement}; use element::{MeasureContext, ProcessContext, UiElement};
use event::UiEvent;
use state::StateRepo; use state::StateRepo;
use draw::{UiDrawCommands, UiDrawPlan}; use draw::{UiDrawCommands, UiDrawPlan};
use text::{TextRenderer, FontTextureInfo, FontHandle}; use text::{TextRenderer, FontTextureInfo, FontHandle};
@ -37,6 +41,7 @@ pub struct UiInstance {
draw_plan: UiDrawPlan, draw_plan: UiDrawPlan,
draw_plan_modified: bool, draw_plan_modified: bool,
text_renderer: TextRenderer, text_renderer: TextRenderer,
events: VecDeque<UiEvent>,
} }
impl UiInstance { impl UiInstance {
@ -52,6 +57,7 @@ impl UiInstance {
draw_plan_modified: false, draw_plan_modified: false,
// ftm: FontTextureManager::default(), // ftm: FontTextureManager::default(),
text_renderer: TextRenderer::new(), text_renderer: TextRenderer::new(),
events: VecDeque::new(),
} }
} }
@ -101,6 +107,10 @@ impl UiInstance {
pub fn font_texture(&self) -> FontTextureInfo { pub fn font_texture(&self) -> FontTextureInfo {
self.text_renderer.font_texture() self.text_renderer.font_texture()
} }
pub fn push_event(&mut self, event: UiEvent) {
self.events.push_back(event);
}
} }
impl Default for UiInstance { impl Default for UiInstance {