hUI/hui/src/element/builtin/container.rs

215 lines
6.7 KiB
Rust
Raw Normal View History

2024-02-17 14:43:46 -06:00
use glam::{Vec2, vec2, Vec4};
use crate::{
2024-02-20 10:30:26 -06:00
layout::{Alignment, LayoutInfo, UiDirection, UiSize},
rectangle::{Corners, Sides},
draw::{RoundedCorners, UiDrawCommand},
2024-02-18 10:22:31 -06:00
element::{MeasureContext, ProcessContext, UiElement},
measure::{Hints, Response},
2024-02-17 14:43:46 -06:00
};
pub struct Border {
pub color: Vec4,
pub width: f32,
}
pub struct Container {
// pub min_size: (UiSize, UiSize),
// pub max_size: (UiSize, UiSize),
pub size: (UiSize, UiSize),
pub direction: UiDirection,
//pub reverse: bool,
pub gap: f32,
pub padding: Sides<f32>,
///Primary/secondary axis
pub align: (Alignment, Alignment),
pub background: Option<Vec4>,
pub borders: Sides<Option<Border>>,
2024-02-19 07:44:01 -06:00
//pub clip: bool, //TODO clip children
2024-02-17 14:43:46 -06:00
pub elements: Vec<Box<dyn UiElement>>,
2024-02-20 10:30:26 -06:00
pub corner_radius: Option<Corners<f32>>,
2024-02-17 14:43:46 -06:00
}
impl Default for Container {
fn default() -> Self {
Self {
// min_size: (UiSize::Auto, UiSize::Auto),
// max_size: (UiSize::Auto, UiSize::Auto),
size: (UiSize::Auto, UiSize::Auto),
direction: UiDirection::Vertical,
//reverse: false,
gap: 0.,
padding: Sides::all(0.),
align: (Alignment::Begin, Alignment::Begin),
background: Default::default(),
borders: Default::default(),
elements: Vec::new(),
2024-02-18 20:41:48 -06:00
corner_radius: None,
2024-02-17 14:43:46 -06:00
}
}
}
impl Container {
pub fn measure_max_inner_size(&self, layout: &LayoutInfo) -> Vec2 {
let outer_size_x = match self.size.0 {
UiSize::Auto => layout.max_size.x,
2024-02-18 12:27:45 -06:00
UiSize::Fraction(p) => layout.max_size.x * p,
UiSize::Static(p) => p,
2024-02-17 14:43:46 -06:00
};
let outer_size_y = match self.size.1 {
UiSize::Auto => layout.max_size.y,
2024-02-18 12:27:45 -06:00
UiSize::Fraction(p) => layout.max_size.y * p,
UiSize::Static(p) => p,
2024-02-17 14:43:46 -06:00
};
vec2(
outer_size_x - (self.padding.left + self.padding.right),
outer_size_y - (self.padding.top + self.padding.bottom),
)
}
}
impl UiElement for Container {
fn measure(&self, ctx: MeasureContext) -> Response {
2024-02-17 14:43:46 -06:00
let mut size = Vec2::ZERO;
//if matches!(self.size.0, UiSize::Auto) || matches!(self.size.1, UiSize::Auto) {
let mut leftover_gap = Vec2::ZERO;
for element in &self.elements {
let measure = element.measure(MeasureContext{
state: ctx.state,
layout: &LayoutInfo {
position: ctx.layout.position + size,
max_size: self.measure_max_inner_size(ctx.layout), // - size TODO
direction: self.direction,
},
text_measure: ctx.text_measure,
2024-02-17 14:43:46 -06:00
});
match self.direction {
UiDirection::Horizontal => {
size.x += measure.size.x + self.gap;
size.y = size.y.max(measure.size.y);
leftover_gap.x = self.gap;
},
UiDirection::Vertical => {
size.x = size.x.max(measure.size.x);
size.y += measure.size.y + self.gap;
leftover_gap.y = self.gap;
}
}
}
size -= leftover_gap;
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 => (),
2024-02-18 12:27:45 -06:00
UiSize::Fraction(percentage) => size.x = ctx.layout.max_size.x * percentage,
UiSize::Static(pixels) => size.x = pixels,
2024-02-17 14:43:46 -06:00
}
match self.size.1 {
UiSize::Auto => (),
2024-02-18 12:27:45 -06:00
UiSize::Fraction(percentage) => size.y = ctx.layout.max_size.y * percentage,
UiSize::Static(pixels) => size.y = pixels,
2024-02-17 14:43:46 -06:00
}
Response {
size,
hints: Hints {
inner_content_size,
..Default::default()
},
user_data: None
}
}
fn process(&self, ctx: ProcessContext) {
let mut position = ctx.layout.position;
2024-02-17 14:43:46 -06:00
//background
if let Some(color) = self.background {
ctx.draw.add(UiDrawCommand::Rectangle {
2024-02-17 14:43:46 -06:00
position,
size: ctx.measure.size,
2024-02-18 20:41:48 -06:00
color,
2024-02-19 12:40:18 -06:00
rounded_corners: self.corner_radius.map(RoundedCorners::from_radius),
2024-02-17 14:43:46 -06:00
});
}
//padding
position += vec2(self.padding.left, self.padding.top);
//alignment
match (self.align.0, self.direction) {
(Alignment::Begin, _) => (),
(Alignment::Center, UiDirection::Horizontal) => {
2024-02-19 06:57:33 -06:00
position.x += (ctx.measure.size.x - ctx.measure.hints.inner_content_size.unwrap().x) / 2. - self.padding.left;
2024-02-17 14:43:46 -06:00
},
(Alignment::Center, UiDirection::Vertical) => {
2024-02-19 06:57:33 -06:00
position.y += (ctx.measure.size.y - ctx.measure.hints.inner_content_size.unwrap().y) / 2. - self.padding.top;
2024-02-17 14:43:46 -06:00
},
(Alignment::End, UiDirection::Horizontal) => {
position.x += ctx.measure.size.x - ctx.measure.hints.inner_content_size.unwrap().x - self.padding.right - self.padding.left;
2024-02-17 14:43:46 -06:00
},
(Alignment::End, UiDirection::Vertical) => {
position.y += ctx.measure.size.y - ctx.measure.hints.inner_content_size.unwrap().y - self.padding.bottom - self.padding.top;
2024-02-17 14:43:46 -06:00
}
}
for element in &self.elements {
//(passing max size from layout rather than actual bounds for the sake of consistency with measure() above)
let mut el_layout = LayoutInfo {
position,
max_size: self.measure_max_inner_size(ctx.layout),
2024-02-17 14:43:46 -06:00
direction: self.direction,
};
//measure
let el_measure = element.measure(MeasureContext {
state: ctx.state,
layout: &el_layout,
text_measure: ctx.text_measure,
});
2024-02-17 14:43:46 -06:00
//align (on sec. axis)
match (self.align.1, self.direction) {
(Alignment::Begin, _) => (),
(Alignment::Center, UiDirection::Horizontal) => {
el_layout.position.y += (ctx.measure.size.y - self.padding.bottom - self.padding.top - el_measure.size.y) / 2.;
2024-02-17 14:43:46 -06:00
},
(Alignment::Center, UiDirection::Vertical) => {
el_layout.position.x += (ctx.measure.size.x - self.padding.left - self.padding.right - el_measure.size.x) / 2.;
2024-02-17 14:43:46 -06:00
},
(Alignment::End, UiDirection::Horizontal) => {
el_layout.position.y += ctx.measure.size.y - el_measure.size.y - self.padding.bottom;
2024-02-17 14:43:46 -06:00
},
(Alignment::End, UiDirection::Vertical) => {
el_layout.position.x += ctx.measure.size.x - el_measure.size.x - self.padding.right;
2024-02-17 14:43:46 -06:00
}
}
//process
element.process(ProcessContext {
measure: &el_measure,
state: ctx.state,
layout: &el_layout,
draw: ctx.draw,
text_measure: ctx.text_measure,
});
2024-02-17 14:43:46 -06:00
//layout
match self.direction {
UiDirection::Horizontal => {
position.x += el_measure.size.x + self.gap;
},
UiDirection::Vertical => {
position.y += el_measure.size.y + self.gap;
}
}
}
}
}