diff --git a/hui-examples/examples/align_test.rs b/hui-examples/examples/align_test.rs index 1987ab5..e0d340a 100644 --- a/hui-examples/examples/align_test.rs +++ b/hui-examples/examples/align_test.rs @@ -6,15 +6,9 @@ use winit::{ event_loop::{EventLoopBuilder, ControlFlow} }; use hui::{ - UiInstance, - layout::{Alignment, UiSize, UiDirection}, - rectangle::{Sides, Corners}, element::{ - UiElement, - progress_bar::ProgressBar, - container::Container, - rect::Rect - }, + container::Container, progress_bar::ProgressBar, rect::Rect, ElementList, UiElement + }, layout::{Alignment, UiDirection, UiSize}, rectangle::{Corners, Sides}, UiInstance }; use hui_glium::GliumUiRenderer; @@ -49,12 +43,12 @@ fn main() { padding: Sides::all(5.), align: (Alignment::Begin, Alignment::Center).into(), size: (UiSize::Fraction(1.), UiSize::Fraction(1.)), - elements: vec![ + children: ElementList(vec![ Box::new(ProgressBar { value: 0.5, ..Default::default() }), - ], + ]), ..Default::default() }, resolution); @@ -63,7 +57,7 @@ fn main() { padding: Sides::all(5.), align: (Alignment::End, Alignment::Center).into(), size: (UiSize::Fraction(1.), UiSize::Fraction(1.)), - elements: vec![ + children: ElementList(vec![ Box::new(ProgressBar { value: z, corner_radius: Corners::all(0.25 * ProgressBar::DEFAULT_HEIGHT), @@ -74,7 +68,7 @@ fn main() { align: (Alignment::Center, Alignment::End).into(), padding: Sides::all(5.), gap: 10., - elements: vec![ + children: ElementList(vec![ Box::new(Rect { size: (UiSize::Fraction(0.5), UiSize::Static(30.)), color: vec4(0.75, 0., 0., 1.).into() @@ -86,7 +80,7 @@ fn main() { vec4(0., 1., 0., 1.) ).into(), }), - ], + ]), ..Default::default() }), Box::new(Rect { @@ -98,7 +92,7 @@ fn main() { padding: Sides::all(5.), background: vec4(0., 0., 0., 0.5).into(), direction: UiDirection::Horizontal, - elements: { + children: { let mut x: Vec> = vec![]; for i in 0..10 { x.push(Box::new(Rect { @@ -110,7 +104,7 @@ fn main() { } })); } - x + ElementList(x) }, ..Default::default() }), @@ -128,15 +122,15 @@ fn main() { bottom_left: 0., bottom_right: 0., }, - elements: vec![ + children: ElementList(vec![ Box::new(Rect { size: (UiSize::Static(50.), UiSize::Static(50.)), color: vec4(1., 1., 1., 0.75).into() }), - ], + ]), ..Default::default() }), - ], + ]), ..Default::default() }, resolution); diff --git a/hui-examples/examples/text_center.rs b/hui-examples/examples/modern_testbed.rs similarity index 67% rename from hui-examples/examples/text_center.rs rename to hui-examples/examples/modern_testbed.rs index 1f58aa3..bb27fef 100644 --- a/hui-examples/examples/text_center.rs +++ b/hui-examples/examples/modern_testbed.rs @@ -1,4 +1,5 @@ -use glam::UVec2; +use std::time::Instant; +use glam::{UVec2, vec4}; use glium::{backend::glutin::SimpleWindowBuilder, Surface}; use winit::{ event::{Event, WindowEvent}, @@ -6,12 +7,13 @@ use winit::{ }; use hui::{ UiInstance, - rectangle::Sides, - layout::{UiSize, Alignment}, element::{ + ElementList, UiElementListExt, container::Container, text::Text, - } + }, + layout::{Alignment, UiDirection, UiSize}, + rectangle::{Corners, Sides}, }; use hui_glium::GliumUiRenderer; @@ -23,6 +25,7 @@ fn main() { let mut hui = UiInstance::new(); let mut backend = GliumUiRenderer::new(&display); + event_loop.run(|event, window_target| { window_target.set_control_flow(ControlFlow::Poll); match event { @@ -37,19 +40,15 @@ fn main() { hui.begin(); - hui.add(Container { - gap: 5., - padding: Sides::all(5.), - align: Alignment::Center.into(), - size: (UiSize::Fraction(1.), UiSize::Fraction(1.)), - elements: vec![ - Box::new(Text { - text: "Hello, world!\nGoodbye, world!\nowo\nuwu".into(), - text_size: 120, - ..Default::default() - }), - ], - ..Default::default() + hui.add({ + Container::default() + .with_padding(Sides::all(5.)) + .with_children(|ui: &mut ElementList| { + Text::default() + .with_text("Hello, world") + .with_text_size(12) + .add_to(ui); + }) }, resolution); hui.end(); diff --git a/hui-examples/examples/mom_downloader.rs b/hui-examples/examples/mom_downloader.rs index 7af88bc..16d5637 100644 --- a/hui-examples/examples/mom_downloader.rs +++ b/hui-examples/examples/mom_downloader.rs @@ -6,21 +6,21 @@ use winit::{ event_loop::{EventLoopBuilder, ControlFlow} }; use hui::{ - UiInstance, - layout::{Alignment, UiDirection, UiSize}, - rectangle::{Corners, Sides}, element::{ container::Container, progress_bar::ProgressBar, - text::Text, + text::Text, ElementList, }, + layout::{Alignment, UiDirection, UiSize}, + rectangle::{Corners, Sides}, + UiInstance, }; use hui_glium::GliumUiRenderer; -fn elements(mut f: impl FnMut(&mut Vec>)) -> Vec> { +fn elements(mut f: impl FnMut(&mut Vec>)) -> ElementList { let mut e = vec![]; f(&mut e); - e + ElementList(e) } fn main() { @@ -57,13 +57,13 @@ fn main() { align: Alignment::Center.into(), size: (UiSize::Fraction(1.), UiSize::Fraction(1.)), background: vec4(0.1, 0.1, 0.1, 1.).into(), - elements: vec![Box::new(Container { + children: ElementList(vec![Box::new(Container { gap: 5., padding: Sides::all(10.), size: (UiSize::Static(450.), UiSize::Auto), background: vec4(0.2, 0.2, 0.5, 1.).into(), corner_radius: Corners::all(8.), - elements: elements(|el| { + children: elements(|el| { if instant.elapsed().as_secs_f32() < 5. { el.push(Box::new(Text { text: "Downloading your mom...".into(), @@ -80,12 +80,12 @@ fn main() { direction: UiDirection::Horizontal, align: (Alignment::End, Alignment::Center).into(), size: (UiSize::Fraction(1.), UiSize::Auto), - elements: vec![Box::new(Text { + children: ElementList(vec![Box::new(Text { text: format!("{:.2}% ({:.1} GB)", mom_ratio * 100., mom_ratio * 10000.).into(), font: font_handle, text_size: 16, ..Default::default() - })], + })]), ..Default::default() })); } else if instant.elapsed().as_secs() < 10 { @@ -107,7 +107,7 @@ fn main() { } }), ..Default::default() - })], + })]), ..Default::default() }, resolution); diff --git a/hui-examples/examples/rounded_rect.rs b/hui-examples/examples/rounded_rect.rs index cdd0fbc..b286c9c 100644 --- a/hui-examples/examples/rounded_rect.rs +++ b/hui-examples/examples/rounded_rect.rs @@ -5,13 +5,13 @@ use winit::{ event_loop::{EventLoopBuilder, ControlFlow} }; use hui::{ - UiInstance, - layout::{Alignment, UiSize, UiDirection}, - rectangle::{Corners, Sides}, element::{ container::Container, - text::Text + text::Text, ElementList }, + layout::{Alignment, UiDirection, UiSize}, + rectangle::{Corners, Sides}, + UiInstance }; use hui_glium::GliumUiRenderer; @@ -41,7 +41,7 @@ fn main() { gap: 10., align: Alignment::Center.into(), size: (UiSize::Fraction(1.), UiSize::Fraction(1.)), - elements: vec![ + children: ElementList(vec![ Box::new(Container { align: Alignment::Center.into(), size: (UiSize::Fraction(0.5), UiSize::Fraction(0.5)), @@ -52,7 +52,7 @@ fn main() { bottom_left: 50., bottom_right: 80. }, - elements: vec![ + children: ElementList(vec![ Box::new(Container { padding: Sides::all(20.), direction: UiDirection::Horizontal, @@ -60,7 +60,7 @@ fn main() { size: (UiSize::Auto, UiSize::Auto), background: vec4(0.1, 0.1, 0.1, 0.5).into(), corner_radius: Corners::all(8.), - elements: vec![ + children: ElementList(vec![ Box::new(Text { text: "Corners".into(), text_size: 50, @@ -73,16 +73,16 @@ fn main() { color: vec4(1., 1., 0., 1.), ..Default::default() }), - ], + ]), ..Default::default() }), - ], + ]), ..Default::default() }), Box::new(Container { gap: 10., direction: UiDirection::Horizontal, - elements: vec![ + children: ElementList(vec![ Box::new(Container { size: (UiSize::Static(100.), UiSize::Static(100.)), background: Corners::left_right( @@ -119,10 +119,10 @@ fn main() { corner_radius: Corners::all(30.), ..Default::default() }), - ], + ]), ..Default::default() }), - ], + ]), ..Default::default() }, resolution); diff --git a/hui-examples/examples/stress.rs b/hui-examples/examples/stress.rs deleted file mode 100644 index 342ce4d..0000000 --- a/hui-examples/examples/stress.rs +++ /dev/null @@ -1,91 +0,0 @@ -use std::time::Instant; -use glam::UVec2; -use glium::{backend::glutin::SimpleWindowBuilder, Surface}; -use winit::{ - event::{Event, WindowEvent}, - event_loop::{EventLoopBuilder, ControlFlow} -}; -use hui::{ - UiInstance, - layout::UiSize, - rectangle::Sides, - element::{ - container::Container, - progress_bar::ProgressBar, - UiElement - }, -}; -use hui_glium::GliumUiRenderer; - -fn main() { - kubi_logging::init(); - - let event_loop = EventLoopBuilder::new().build().unwrap(); - let (_window, display) = SimpleWindowBuilder::new().build(&event_loop); - - let mut hui = UiInstance::new(); - let mut backend = GliumUiRenderer::new(&display); - - let instant = Instant::now(); - let mut pcnt = 0; - event_loop.run(|event, window_target| { - window_target.set_control_flow(ControlFlow::Poll); - match event { - Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { - window_target.exit(); - }, - Event::AboutToWait => { - let mut frame = display.draw(); - frame.clear_color_srgb(0.5, 0.5, 0.5, 0.); - - let resolution = UVec2::from(display.get_framebuffer_dimensions()).as_vec2(); - - hui.begin(); - - hui.add(Container { - gap: 5., - padding: Sides::all(5.), - elements: vec![ - Box::new(ProgressBar { - value: 0.5, - ..Default::default() - }), - Box::new(ProgressBar { - value: instant.elapsed().as_secs_f32().sin().powi(2), - ..Default::default() - }), - Box::new(Container { - gap: 1., - elements: { - let mut elements: Vec> = vec![]; - let cnt = instant.elapsed().as_secs() * 10000; - if pcnt != cnt { - log::info!("{cnt}"); - pcnt = cnt; - } - for i in 0..cnt { - elements.push(Box::new(ProgressBar { - value: (instant.elapsed().as_secs_f32() + (i as f32 / 10.)).sin().powi(2), - size: (UiSize::Auto, UiSize::Static(5.)), - ..Default::default() - })); - } - elements - }, - ..Default::default() - }) - ], - ..Default::default() - }, resolution); - - hui.end(); - - backend.update(&hui); - backend.draw(&mut frame, resolution); - - frame.finish().unwrap(); - } - _ => (), - } - }).unwrap(); -} diff --git a/hui-examples/examples/text_weird.rs b/hui-examples/examples/text_weird.rs index 242afdb..254be67 100644 --- a/hui-examples/examples/text_weird.rs +++ b/hui-examples/examples/text_weird.rs @@ -6,19 +6,16 @@ use winit::{ event_loop::{EventLoopBuilder, ControlFlow} }; use hui::{ - UiInstance, - layout::UiSize, element::{ - container::Container, - text::Text, rect::Rect, spacer::Spacer - }, + container::Container, rect::Rect, spacer::Spacer, text::Text, ElementList + }, layout::UiSize, UiInstance }; use hui_glium::GliumUiRenderer; -fn elements(mut f: impl FnMut(&mut Vec>)) -> Vec> { +fn elements(mut f: impl FnMut(&mut Vec>)) -> ElementList { let mut e = vec![]; f(&mut e); - e + ElementList(e) } fn main() { @@ -51,7 +48,7 @@ fn main() { hui.add(Container { size: (UiSize::Fraction(1.), UiSize::Fraction(1.)), background: vec4(0.1, 0.1, 0.1, 1.).into(), - elements: elements(|elem| { + children: elements(|elem| { elem.push(Box::new(Text { text: "THIS LINE SHOULD BE SHARP!".into(), ..Default::default() diff --git a/hui/Cargo.toml b/hui/Cargo.toml index d6cb573..a5ba448 100644 --- a/hui/Cargo.toml +++ b/hui/Cargo.toml @@ -24,6 +24,7 @@ rect_packer = "0.2" log = "0.4" nz = "0.3" document-features = "0.2" +derive_setters = "0.1" [features] default = ["builtin_elements", "builtin_font", "pixel_perfect_text"] diff --git a/hui/src/element.rs b/hui/src/element.rs index 16453e0..b3f065c 100644 --- a/hui/src/element.rs +++ b/hui/src/element.rs @@ -60,3 +60,35 @@ pub trait UiElement { /// You should process the user inputs and render the element here. fn process(&self, ctx: ProcessContext); } + +pub struct ElementList(pub Vec>); + +impl ElementList { + pub fn add<'a>(&mut self, element: impl UiElement + 'static) { + self.0.push(Box::new(element)) + } +} + +impl From for ElementList { + fn from(cb: T) -> Self { + let mut list = ElementList(Vec::new()); + cb(&mut list); + list + } +} + +impl From>> for ElementList { + fn from(value: Vec>) -> Self { + Self(value) + } +} + +pub trait UiElementListExt { + fn add_to(self, list: &mut ElementList); +} + +impl UiElementListExt for T { + fn add_to(self, list: &mut ElementList) { + list.add(self) + } +} diff --git a/hui/src/element/builtin/container.rs b/hui/src/element/builtin/container.rs index 7af761b..6bd0c3c 100644 --- a/hui/src/element/builtin/container.rs +++ b/hui/src/element/builtin/container.rs @@ -1,8 +1,9 @@ +use derive_setters::Setters; use glam::{Vec2, vec2}; use crate::{ background::BackgroundColor, draw::{RoundedCorners, UiDrawCommand}, - element::{MeasureContext, ProcessContext, UiElement}, + element::{ElementList, MeasureContext, ProcessContext, UiElement}, layout::{Alignment, Alignment2d, LayoutInfo, UiDirection, UiSize}, measure::{Hints, Response}, rectangle::{Corners, Sides} @@ -18,15 +19,21 @@ use crate::{ //TODO: borders //TODO: min/max size +#[derive(Setters)] +#[setters(prefix = "with_")] pub struct Container { pub size: (UiSize, UiSize), pub direction: UiDirection, pub gap: f32, pub padding: Sides, + #[setters(into)] pub align: Alignment2d, + #[setters(into)] pub background: BackgroundColor, + #[setters(into)] pub corner_radius: Corners, - pub elements: Vec>, + #[setters(into)] + pub children: ElementList, } impl Default for Container { @@ -38,7 +45,7 @@ impl Default for Container { padding: Sides::all(0.), align: Alignment2d::default(), background: Default::default(), - elements: Vec::new(), + children: ElementList(Vec::new()), corner_radius: Corners::all(0.), } } @@ -67,7 +74,7 @@ impl UiElement for Container { fn measure(&self, ctx: MeasureContext) -> Response { let mut size = Vec2::ZERO; let mut leftover_gap = Vec2::ZERO; - for element in &self.elements { + for element in &self.children.0 { let measure = element.measure(MeasureContext{ state: ctx.state, layout: &LayoutInfo { @@ -164,7 +171,7 @@ impl UiElement for Container { } } - for element in &self.elements { + for element in &self.children.0 { //(passing max size from layout rather than actual bounds for the sake of consistency with measure() above) let mut el_layout = LayoutInfo { diff --git a/hui/src/element/builtin/text.rs b/hui/src/element/builtin/text.rs index 7bba80f..9c2781c 100644 --- a/hui/src/element/builtin/text.rs +++ b/hui/src/element/builtin/text.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use derive_setters::Setters; use glam::{vec2, Vec4}; use crate::{ draw::UiDrawCommand, element::{MeasureContext, ProcessContext, UiElement}, layout::UiSize, measure::Response, text::{FontHandle, DEFAULT_FONT} @@ -12,7 +13,10 @@ use crate::{ // Constant(u8), // } +#[derive(Setters)] +#[setters(prefix = "with_")] pub struct Text { + #[setters(into)] pub text: Cow<'static, str>, pub size: (UiSize, UiSize), pub color: Vec4,