Compare commits

...

3 commits

Author SHA1 Message Date
griffi-gh 2e91b3a9ee main axis align 2023-11-28 11:32:09 +01:00
griffi-gh eb23bdb448 tst 2023-11-28 10:39:25 +01:00
griffi-gh bf5b05295d fix stuff 2023-11-28 10:31:38 +01:00
6 changed files with 181 additions and 53 deletions

View file

@ -1,5 +1,5 @@
use std::time::Instant; use std::time::Instant;
use glam::{Vec2, IVec2, UVec2}; use glam::{Vec2, IVec2, UVec2, vec4};
use glium::{backend::glutin::SimpleWindowBuilder, Surface}; use glium::{backend::glutin::SimpleWindowBuilder, Surface};
use winit::{ use winit::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
@ -7,7 +7,7 @@ use winit::{
}; };
use kubi_ui::{ use kubi_ui::{
KubiUi, KubiUi,
backend::glium::GliumUiRenderer, element::{progress_bar::ProgressBar, container::{Container, Sides}, UiElement}, UiSize backend::glium::GliumUiRenderer, element::{progress_bar::ProgressBar, container::{Container, Sides, Alignment}, UiElement, rect::Rect}, UiSize, UiDirection
}; };
fn main() { fn main() {
@ -34,18 +34,85 @@ fn main() {
kui.begin(); kui.begin();
let z = instant.elapsed().as_secs_f32().sin().powi(2);
kui.add(Container { kui.add(Container {
gap: 5., gap: 5.,
padding: Sides::all(5.), padding: Sides::all(5.),
align: (Alignment::Begin, Alignment::Center),
size: (UiSize::Percentage(1.), UiSize::Percentage(1.)),
elements: vec![ elements: vec![
Box::new(ProgressBar { Box::new(ProgressBar {
value: 0.5, value: 0.5,
..Default::default() ..Default::default()
}), }),
],
..Default::default()
}, resolution);
kui.add(Container {
gap: 5.,
padding: Sides::all(5.),
align: (Alignment::End, Alignment::Center),
size: (UiSize::Percentage(1.), UiSize::Percentage(1.)),
elements: vec![
Box::new(ProgressBar { Box::new(ProgressBar {
value: instant.elapsed().as_secs_f32().sin().powi(2), value: z,
..Default::default() ..Default::default()
}), }),
Box::new(Container {
size: (UiSize::Percentage(1.), UiSize::Auto),
align: (Alignment::Center, Alignment::End),
padding: Sides::all(5.),
gap: 10.,
elements: vec![
Box::new(Rect {
size: (UiSize::Percentage(0.5), UiSize::Pixels(30.)),
color: Some(vec4(0.75, 0., 0., 1.))
}),
Box::new(Rect {
size: (UiSize::Percentage(z / 2. + 0.5), UiSize::Pixels(30.)),
color: Some(vec4(0., 0.75, 0., 1.))
}),
],
..Default::default()
}),
Box::new(Rect {
size: (UiSize::Percentage(z / 2. + 0.5), UiSize::Pixels(30.)),
color: Some(vec4(0., 0.75, 0., 1.))
}),
Box::new(Container {
gap: 5.,
padding: Sides::all(5.),
background: Some(vec4(0., 0., 0., 0.5)),
direction: UiDirection::Horizontal,
elements: {
let mut x: Vec<Box<dyn UiElement>> = vec![];
for i in 0..10 {
x.push(Box::new(Rect {
size: (UiSize::Pixels(50.), UiSize::Pixels(50.)),
color: if i == 1 {
Some(vec4(0.75, 0.75, 0.75, 0.75))
} else {
Some(vec4(0.5, 0.5, 0.5, 0.75))
}
}));
}
x
},
..Default::default()
}),
Box::new(Container {
background: Some(vec4(1., 0., 0., 1.)),
padding: Sides::horizontal_vertical(30., 5.),
elements: vec![
Box::new(Rect {
size: (UiSize::Pixels(50.), UiSize::Pixels(50.)),
color: Some(vec4(1., 1., 1., 0.75))
})
],
..Default::default()
})
], ],
..Default::default() ..Default::default()
}, resolution); }, resolution);

View file

@ -4,11 +4,12 @@ use crate::{
UiSize, UiSize,
LayoutInfo, LayoutInfo,
draw::UiDrawCommand, draw::UiDrawCommand,
measure::Response, measure::{Response, Hints},
state::StateRepo, state::StateRepo,
element::UiElement element::UiElement
}; };
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Alignment { pub enum Alignment {
Begin, Begin,
Center, Center,
@ -51,8 +52,9 @@ impl<T: Clone> Sides<T> {
} }
pub struct Container { pub struct Container {
pub min_size: (UiSize, UiSize), // pub min_size: (UiSize, UiSize),
pub max_size: (UiSize, UiSize), // pub max_size: (UiSize, UiSize),
pub size: (UiSize, UiSize),
pub direction: UiDirection, pub direction: UiDirection,
//pub reverse: bool, //pub reverse: bool,
pub gap: f32, pub gap: f32,
@ -67,13 +69,15 @@ pub struct Container {
impl Default for Container { impl Default for Container {
fn default() -> Self { fn default() -> Self {
Self { Self {
min_size: (UiSize::Auto, UiSize::Auto), // min_size: (UiSize::Auto, UiSize::Auto),
max_size: (UiSize::Auto, UiSize::Auto), // max_size: (UiSize::Auto, UiSize::Auto),
size: (UiSize::Auto, UiSize::Auto),
direction: UiDirection::Vertical, direction: UiDirection::Vertical,
//reverse: false, //reverse: false,
gap: 0., gap: 0.,
padding: Sides::all(0.), padding: Sides::all(0.),
align: (Alignment::Center, Alignment::Begin), ///Primary/secondary axis
align: (Alignment::Begin, Alignment::Begin),
background: Default::default(), background: Default::default(),
borders: Default::default(), borders: Default::default(),
clip: Default::default(), clip: Default::default(),
@ -83,7 +87,8 @@ impl Default for Container {
} }
impl Container { impl Container {
pub fn measure_max_size(&self, layout: &LayoutInfo) -> Vec2 { pub fn measure_max_inner_size(&self, layout: &LayoutInfo) -> Vec2 {
//TODO take explicit size into account
layout.max_size - vec2( layout.max_size - vec2(
self.padding.left + self.padding.right, self.padding.left + self.padding.right,
self.padding.top + self.padding.bottom, self.padding.top + self.padding.bottom,
@ -94,28 +99,55 @@ impl Container {
impl UiElement for Container { impl UiElement for Container {
fn measure(&self, state: &StateRepo, layout: &LayoutInfo) -> Response { fn measure(&self, state: &StateRepo, layout: &LayoutInfo) -> Response {
let mut size = Vec2::ZERO; let mut size = Vec2::ZERO;
//if matches!(self.size.0, UiSize::Auto) || matches!(self.size.1, UiSize::Auto) {
let mut leftover_gap = Vec2::ZERO; let mut leftover_gap = Vec2::ZERO;
for element in &self.elements { for element in &self.elements {
let measure = element.measure(state, &LayoutInfo { let measure = element.measure(state, &LayoutInfo {
position: layout.position + size, position: layout.position + size,
max_size: self.measure_max_size(layout), // - size TODO max_size: self.measure_max_inner_size(layout), // - size TODO
direction: self.direction, direction: self.direction,
}); });
match self.direction { match self.direction {
UiDirection::Horizontal => { UiDirection::Horizontal => {
size.x += measure.desired_size.x + self.gap; size.x += measure.size.x + self.gap;
size.y = size.y.max(measure.desired_size.y); size.y = size.y.max(measure.size.y);
leftover_gap.x = self.gap; leftover_gap.x = self.gap;
}, },
UiDirection::Vertical => { UiDirection::Vertical => {
size.x = size.x.max(measure.desired_size.x); size.x = size.x.max(measure.size.x);
size.y += measure.desired_size.y + self.gap; size.y += measure.size.y + self.gap;
leftover_gap.y = self.gap; leftover_gap.y = self.gap;
} }
} }
} }
size -= leftover_gap; size -= leftover_gap;
Response { desired_size: size }
let inner_content_size = Some(size);
size += vec2(
self.padding.left + self.padding.right,
self.padding.top + self.padding.bottom,
);
match self.size.0 {
UiSize::Auto => (),
UiSize::Percentage(percentage) => size.x = layout.max_size.x * percentage,
UiSize::Pixels(pixels) => size.x = pixels,
}
match self.size.1 {
UiSize::Auto => (),
UiSize::Percentage(percentage) => size.y = layout.max_size.y * percentage,
UiSize::Pixels(pixels) => size.y = pixels,
}
Response {
size,
hints: Hints {
inner_content_size,
..Default::default()
},
user_data: None
}
} }
fn process(&self, measure: &Response, state: &mut StateRepo, layout: &LayoutInfo, draw: &mut Vec<UiDrawCommand>) { fn process(&self, measure: &Response, state: &mut StateRepo, layout: &LayoutInfo, draw: &mut Vec<UiDrawCommand>) {
@ -125,7 +157,7 @@ impl UiElement for Container {
if let Some(color) = self.background { if let Some(color) = self.background {
draw.push(UiDrawCommand::Rectangle { draw.push(UiDrawCommand::Rectangle {
position, position,
size: measure.desired_size, size: measure.size,
color color
}); });
} }
@ -134,48 +166,61 @@ impl UiElement for Container {
position += vec2(self.padding.left, self.padding.top); position += vec2(self.padding.left, self.padding.top);
//alignment //alignment
//TODO: REQUIRES MAX MEASURE SIZES! match (self.align.0, self.direction) {
// match self.align.0 { (Alignment::Begin, _) => (),
// Alignment::Begin => (), (Alignment::Center, UiDirection::Horizontal) => {
// Alignment::Center => { position.x += (measure.size.x - measure.hints.inner_content_size.unwrap().x) / 2.;
// position.x += (layout.max_size.x - measure.desired_size.x) / 2.; },
// }, (Alignment::Center, UiDirection::Vertical) => {
// Alignment::End => { position.y += (measure.size.y - measure.hints.inner_content_size.unwrap().y) / 2.;
// position.x += layout.max_size.x - measure.desired_size.x; },
// } (Alignment::End, UiDirection::Horizontal) => {
// } position.x += measure.size.x - measure.hints.inner_content_size.unwrap().x - self.padding.right - self.padding.left;
// match self.align.1 { },
// Alignment::Begin => (), (Alignment::End, UiDirection::Vertical) => {
// Alignment::Center => { position.y += measure.size.y - measure.hints.inner_content_size.unwrap().y - self.padding.bottom - self.padding.top;
// position.y += (layout.max_size.y - measure.desired_size.y) / 2.; }
// }, }
// Alignment::End => {
// position.y += layout.max_size.y - measure.desired_size.y;
// }
// }
for element in &self.elements { for element in &self.elements {
//(passing max size from layout rather than actual bounds for the sake of consistency with measure() above) //(passing max size from layout rather than actual bounds for the sake of consistency with measure() above)
let layout = LayoutInfo { let mut el_layout = LayoutInfo {
position, position,
max_size: self.measure_max_size(layout), max_size: self.measure_max_inner_size(layout),
direction: self.direction, direction: self.direction,
}; };
//measure //measure
let el_measure = element.measure(state, &layout); let el_measure = element.measure(state, &el_layout);
//align (on sec. axis)
match (self.align.1, self.direction) {
(Alignment::Begin, _) => (),
(Alignment::Center, UiDirection::Horizontal) => {
el_layout.position.y += (measure.size.y - self.padding.bottom - self.padding.top - el_measure.size.y) / 2.;
},
(Alignment::Center, UiDirection::Vertical) => {
el_layout.position.x += (measure.size.x - self.padding.left - self.padding.right - el_measure.size.x) / 2.;
},
(Alignment::End, UiDirection::Horizontal) => {
el_layout.position.y += measure.size.y - el_measure.size.y - self.padding.bottom;
},
(Alignment::End, UiDirection::Vertical) => {
el_layout.position.x += measure.size.x - el_measure.size.x - self.padding.right;
}
}
//process //process
element.process(&el_measure, state, &layout, draw); element.process(&el_measure, state, &el_layout, draw);
//layout //layout
match self.direction { match self.direction {
UiDirection::Horizontal => { UiDirection::Horizontal => {
position.x += el_measure.desired_size.x + self.gap; position.x += el_measure.size.x + self.gap;
}, },
UiDirection::Vertical => { UiDirection::Vertical => {
position.y += el_measure.desired_size.y + self.gap; position.y += el_measure.size.y + self.gap;
} }
} }
} }

View file

@ -33,7 +33,7 @@ impl UiElement for ProgressBar {
fn measure(&self, _: &StateRepo, layout: &LayoutInfo) -> Response { fn measure(&self, _: &StateRepo, layout: &LayoutInfo) -> Response {
Response { Response {
desired_size: vec2( size: vec2(
match self.size.0 { match self.size.0 {
UiSize::Auto => layout.max_size.x.max(300.), UiSize::Auto => layout.max_size.x.max(300.),
UiSize::Percentage(p) => layout.max_size.x * p, UiSize::Percentage(p) => layout.max_size.x * p,
@ -44,7 +44,9 @@ impl UiElement for ProgressBar {
UiSize::Percentage(p) => layout.max_size.y * p, UiSize::Percentage(p) => layout.max_size.y * p,
UiSize::Pixels(p) => p, UiSize::Pixels(p) => p,
} }
) ),
hints: Default::default(),
user_data: None,
} }
} }
@ -53,14 +55,14 @@ impl UiElement for ProgressBar {
if value < 1. { if value < 1. {
draw.push(UiDrawCommand::Rectangle { draw.push(UiDrawCommand::Rectangle {
position: layout.position, position: layout.position,
size: measure.desired_size, size: measure.size,
color: self.color_background color: self.color_background
}); });
} }
if value > 0. { if value > 0. {
draw.push(UiDrawCommand::Rectangle { draw.push(UiDrawCommand::Rectangle {
position: layout.position, position: layout.position,
size: measure.desired_size * vec2(value, 1.0), size: measure.size * vec2(value, 1.0),
color: self.color_foreground color: self.color_foreground
}); });
} }

View file

@ -25,7 +25,7 @@ impl Default for Rect {
impl UiElement for Rect { impl UiElement for Rect {
fn measure(&self, _state: &StateRepo, layout: &LayoutInfo) -> Response { fn measure(&self, _state: &StateRepo, layout: &LayoutInfo) -> Response {
Response { Response {
desired_size: vec2( size: vec2(
match self.size.0 { match self.size.0 {
UiSize::Auto => layout.max_size.x, UiSize::Auto => layout.max_size.x,
UiSize::Percentage(percentage) => layout.max_size.x * percentage, UiSize::Percentage(percentage) => layout.max_size.x * percentage,
@ -36,7 +36,9 @@ impl UiElement for Rect {
UiSize::Percentage(percentage) => layout.max_size.y * percentage, UiSize::Percentage(percentage) => layout.max_size.y * percentage,
UiSize::Pixels(pixels) => pixels, UiSize::Pixels(pixels) => pixels,
}, },
) ),
hints: Default::default(),
user_data: None
} }
} }
@ -44,7 +46,7 @@ impl UiElement for Rect {
if let Some(color) = self.color { if let Some(color) = self.color {
draw.push(UiDrawCommand::Rectangle { draw.push(UiDrawCommand::Rectangle {
position: layout.position, position: layout.position,
size: measure.desired_size, size: measure.size,
color, color,
}); });
} }

View file

@ -19,10 +19,12 @@ impl Default for Spacer {
impl UiElement for Spacer { impl UiElement for Spacer {
fn measure(&self, state: &StateRepo, layout: &LayoutInfo) -> Response { fn measure(&self, state: &StateRepo, layout: &LayoutInfo) -> Response {
Response { Response {
desired_size: match layout.direction { size: match layout.direction {
UiDirection::Horizontal => vec2(self.0, 0.), UiDirection::Horizontal => vec2(self.0, 0.),
UiDirection::Vertical => vec2(0., self.0), UiDirection::Vertical => vec2(0., self.0),
} },
hints: Default::default(),
user_data: None
} }
} }

View file

@ -1,5 +1,15 @@
use glam::Vec2; use glam::Vec2;
pub struct Response { #[derive(Default)]
pub desired_size: Vec2 #[non_exhaustive]
pub struct Hints {
pub inner_content_size: Option<Vec2>,
pub inner_content_size_cache: Option<Vec<Vec2>>,
}
#[derive(Default)]
pub struct Response {
pub size: Vec2,
pub hints: Hints,
pub user_data: Option<Box<dyn std::any::Any>>,
} }