mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-25 16:38:42 -06:00
update rounded corner api
This commit is contained in:
parent
c72a005ff6
commit
83c00e8c13
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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),
|
||||||
});
|
});
|
||||||
|
|
99
hui/src/draw/corner_radius.rs
Normal file
99
hui/src/draw/corner_radius.rs
Normal 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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
3
hui/src/input.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub(crate) struct UiInputState {
|
||||||
|
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue