update ui system

This commit is contained in:
griffi-gh 2023-11-21 16:14:26 +01:00
parent 927337c86d
commit 5678b0c06f
7 changed files with 125 additions and 52 deletions

View file

@ -2,12 +2,13 @@ use std::any::Any;
use crate::{ use crate::{
LayoutInfo, LayoutInfo,
draw::UiDrawCall, draw::UiDrawCall,
measure::{IsMeasurable, Response}, measure::Response,
state::StateRepo state::StateRepo
}; };
#[cfg(feature = "builtin_elements")] pub mod container;
#[cfg(feature = "builtin_elements")] pub mod spacer;
#[cfg(feature = "builtin_elements")] pub mod progress_bar; #[cfg(feature = "builtin_elements")] pub mod progress_bar;
#[cfg(feature = "builtin_elements")] pub mod layout_box;
pub trait UiElement { pub trait UiElement {
fn name(&self) -> &'static str { "UiElement" } fn name(&self) -> &'static str { "UiElement" }
@ -15,7 +16,6 @@ pub trait UiElement {
fn is_stateful(&self) -> bool { self.state_id().is_some() } fn is_stateful(&self) -> bool { self.state_id().is_some() }
fn is_stateless(&self) -> bool { self.state_id().is_none() } fn is_stateless(&self) -> bool { self.state_id().is_none() }
fn init_state(&self) -> Option<Box<dyn Any>> { None } fn init_state(&self) -> Option<Box<dyn Any>> { None }
fn is_measurable(&self) -> IsMeasurable { IsMeasurable::No } fn measure(&self, state: &StateRepo, layout: &LayoutInfo) -> Response;
fn measure(&self, state: &StateRepo, layout: &LayoutInfo) -> Option<Response> { None } fn draw(&self, measure: &Response, state: &mut StateRepo, layout: &LayoutInfo, draw: &mut Vec<UiDrawCall>);
fn process(&self, state: &mut StateRepo, layout: &LayoutInfo, draw: &mut Vec<UiDrawCall>) -> Response;
} }

View file

@ -0,0 +1,87 @@
use glam::{Vec2, Vec4};
use crate::{UiDirection, LayoutInfo, draw::UiDrawCall, measure::{IsMeasurable, Response}, state::StateRepo, UiSize};
use super::UiElement;
#[derive(Default, Clone, Copy, Debug)]
pub struct ContainerBorders {
pub top: Option<(Vec4, f32)>,
pub bottom: Option<(Vec4, f32)>,
pub left: Option<(Vec4, f32)>,
pub right: Option<(Vec4, f32)>,
}
pub enum ContainerAlign {
Begin,
Center,
End,
}
pub struct Container {
pub min_size: (UiSize, UiSize),
pub max_size: (UiSize, UiSize),
pub direction: UiDirection,
pub gap: f32,
pub padding: f32,
pub align: (ContainerAlign, ContainerAlign),
pub background: Option<Vec4>,
pub borders: ContainerBorders,
pub clip: bool,
pub elements: Vec<Box<dyn UiElement>>,
}
impl Default for Container {
fn default() -> Self {
Self {
min_size: (UiSize::Auto, UiSize::Auto),
max_size: (UiSize::Auto, UiSize::Auto),
direction: UiDirection::Vertical,
gap: 0.,
padding: 0.,
align: (ContainerAlign::Center, ContainerAlign::Begin),
background: Default::default(),
borders: Default::default(),
clip: Default::default(),
elements: Vec::new(),
}
}
}
impl UiElement for Container {
fn measure(&self, state: &StateRepo, layout: &LayoutInfo) -> Response {
let mut size = Vec2::ZERO;
let mut leftover_gap = Vec2::ZERO;
for element in &self.elements {
let measure = element.measure(state, &LayoutInfo {
position: layout.position + size,
max_size: layout.max_size - size,
direction: self.direction,
});
match self.direction {
UiDirection::Horizontal => {
size.x += measure.desired_size.x + self.gap;
size.y = size.y.max(measure.desired_size.y);
leftover_gap.x = self.gap;
},
UiDirection::Vertical => {
size.x = size.x.max(measure.desired_size.x);
size.y += measure.desired_size.y + self.gap;
leftover_gap.y = self.gap;
}
}
}
size -= leftover_gap;
Response { desired_size: size }
}
fn draw(&self, measure: &Response, state: &mut StateRepo, layout: &LayoutInfo, draw: &mut Vec<UiDrawCall>) {
if let Some(color) = self.background {
draw.push(UiDrawCall::Rectangle {
position: layout.position,
size: measure.desired_size,
color
});
//TODO draw borders
}
}
}

View file

@ -1,29 +0,0 @@
use std::any::Any;
use crate::{UiDirection, LayoutInfo, draw::UiDrawCall, measure::{IsMeasurable, Response}, state::StateRepo};
use super::UiElement;
pub struct LayoutBox {
pub direction: UiDirection,
pub gap: f32,
pub elements: Vec<Box<dyn UiElement>>,
}
impl UiElement for LayoutBox {
fn is_measurable(&self) -> IsMeasurable {
IsMeasurable::Maybe
}
fn measure(&self, state: StateRepo, layout: &LayoutInfo) -> Option<Response> {
for element in &self.elements {
if element.is_measurable() == IsMeasurable::No {
return None
}
element.measure(None, layout);
}
todo!()
}
fn process(&self, _state: Option<&mut Box<dyn Any>>, layout: &LayoutInfo, draw: &mut Vec<UiDrawCall>) -> Response {
todo!()
}
}

View file

@ -1,8 +1,8 @@
use glam::{vec2, Vec2, Vec4}; use glam::{vec2, Vec4};
use crate::{ use crate::{
UiSize, LayoutInfo, UiSize, LayoutInfo,
draw::UiDrawCall, draw::UiDrawCall,
measure::{Response, IsMeasurable}, measure::Response,
state::StateRepo state::StateRepo
}; };
use super::UiElement; use super::UiElement;
@ -19,13 +19,11 @@ const BAR_HEIGHT: f32 = 20.0;
impl UiElement for ProgressBar { impl UiElement for ProgressBar {
fn name(&self) -> &'static str { "Progress bar" } fn name(&self) -> &'static str { "Progress bar" }
fn is_measurable(&self) -> IsMeasurable { IsMeasurable::Yes } fn measure(&self, _: &StateRepo, layout: &LayoutInfo) -> Response {
Response {
fn measure(&self, _: &StateRepo, layout: &LayoutInfo) -> Option<Response> { desired_size: vec2(
Some(Response {
size: Vec2::new(
match self.size.0 { match self.size.0 {
UiSize::Auto => layout.max_size.x, UiSize::Auto => layout.max_size.x.max(300.),
UiSize::Percentage(p) => layout.max_size.x * p, UiSize::Percentage(p) => layout.max_size.x * p,
UiSize::Pixels(p) => p, UiSize::Pixels(p) => p,
}, },
@ -35,24 +33,20 @@ impl UiElement for ProgressBar {
UiSize::Pixels(p) => p, UiSize::Pixels(p) => p,
} }
) )
}) }
} }
fn process(&self, state: &mut StateRepo, layout: &LayoutInfo, draw: &mut Vec<UiDrawCall>) -> Response { fn draw(&self, measure: &Response, state: &mut StateRepo, layout: &LayoutInfo, draw: &mut Vec<UiDrawCall>) {
let measure = self.measure(&state, layout).unwrap();
draw.push(UiDrawCall::Rectangle { draw.push(UiDrawCall::Rectangle {
position: layout.position, position: layout.position,
size: measure.size, size: measure.desired_size,
color: self.color_background color: self.color_background
}); });
draw.push(UiDrawCall::Rectangle { draw.push(UiDrawCall::Rectangle {
position: layout.position, position: layout.position,
size: measure.size * vec2(self.value, 1.0), size: measure.desired_size * vec2(self.value, 1.0),
color: self.color_foreground color: self.color_foreground
}); });
measure
} }
} }

View file

@ -0,0 +1,18 @@
use glam::vec2;
use crate::{state::StateRepo, LayoutInfo, measure::Response, draw::UiDrawCall, UiDirection};
use super::UiElement;
pub struct Spacer(f32);
impl UiElement for Spacer {
fn measure(&self, state: &StateRepo, layout: &LayoutInfo) -> Response {
Response {
desired_size: match layout.direction {
UiDirection::Horizontal => vec2(self.0, 0.),
UiDirection::Vertical => vec2(0., self.0),
}
}
}
fn draw(&self, _measure: &Response, _state: &mut StateRepo, _layout: &LayoutInfo, _draw: &mut Vec<UiDrawCall>) {}
}

View file

@ -32,13 +32,15 @@ impl Default for KubiUi {
} }
} }
#[derive(Default)]
pub enum UiSize { pub enum UiSize {
#[default]
Auto, Auto,
Percentage(f32), Percentage(f32),
Pixels(f32), Pixels(f32),
} }
#[derive(Default)] #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum UiDirection { pub enum UiDirection {
#[default] #[default]
Vertical, Vertical,
@ -46,6 +48,7 @@ pub enum UiDirection {
} }
struct LayoutInfo { struct LayoutInfo {
///Not availabe during measuring step
position: Vec2, position: Vec2,
max_size: Vec2, max_size: Vec2,
direction: UiDirection, direction: UiDirection,

View file

@ -9,5 +9,5 @@ pub enum IsMeasurable {
} }
pub struct Response { pub struct Response {
pub size: Vec2 pub desired_size: Vec2
} }