use alloc::{boxed::Box, vec::Vec};
use rlvgl_core::{
event::Event,
renderer::Renderer,
widget::{Rect, Widget},
};
use rlvgl_widgets::container::Container;
pub struct VStack {
bounds: Rect,
spacing: i32,
children: Vec<Box<dyn Widget>>,
next_y: i32,
}
impl VStack {
pub fn new(width: i32) -> Self {
Self {
bounds: Rect {
x: 0,
y: 0,
width,
height: 0,
},
spacing: 0,
children: Vec::new(),
next_y: 0,
}
}
pub fn spacing(mut self, spacing: i32) -> Self {
self.spacing = spacing;
self
}
pub fn child<W, F>(mut self, height: i32, builder: F) -> Self
where
W: Widget + 'static,
F: FnOnce(Rect) -> W,
{
let rect = Rect {
x: 0,
y: self.next_y,
width: self.bounds.width,
height,
};
self.next_y += height + self.spacing;
self.bounds.height = self.next_y - self.spacing;
self.children.push(Box::new(builder(rect)));
self
}
}
impl Widget for VStack {
fn bounds(&self) -> Rect {
self.bounds
}
fn draw(&self, renderer: &mut dyn Renderer) {
for child in &self.children {
child.draw(renderer);
}
}
fn handle_event(&mut self, event: &Event) -> bool {
for child in &mut self.children {
if child.handle_event(event) {
return true;
}
}
false
}
}
pub struct HStack {
bounds: Rect,
spacing: i32,
children: Vec<Box<dyn Widget>>,
next_x: i32,
}
impl HStack {
pub fn new(height: i32) -> Self {
Self {
bounds: Rect {
x: 0,
y: 0,
width: 0,
height,
},
spacing: 0,
children: Vec::new(),
next_x: 0,
}
}
pub fn spacing(mut self, spacing: i32) -> Self {
self.spacing = spacing;
self
}
pub fn child<W, F>(mut self, width: i32, builder: F) -> Self
where
W: Widget + 'static,
F: FnOnce(Rect) -> W,
{
let rect = Rect {
x: self.next_x,
y: 0,
width,
height: self.bounds.height,
};
self.next_x += width + self.spacing;
self.bounds.width = self.next_x - self.spacing;
self.children.push(Box::new(builder(rect)));
self
}
}
impl Widget for HStack {
fn bounds(&self) -> Rect {
self.bounds
}
fn draw(&self, renderer: &mut dyn Renderer) {
for child in &self.children {
child.draw(renderer);
}
}
fn handle_event(&mut self, event: &Event) -> bool {
for child in &mut self.children {
if child.handle_event(event) {
return true;
}
}
false
}
}
pub struct Grid {
bounds: Rect,
cols: i32,
cell_w: i32,
cell_h: i32,
spacing: i32,
children: Vec<Box<dyn Widget>>,
next: i32,
}
impl Grid {
pub fn new(cols: i32, cell_w: i32, cell_h: i32) -> Self {
Self {
bounds: Rect {
x: 0,
y: 0,
width: 0,
height: 0,
},
cols,
cell_w,
cell_h,
spacing: 0,
children: Vec::new(),
next: 0,
}
}
pub fn spacing(mut self, spacing: i32) -> Self {
self.spacing = spacing;
self
}
pub fn child<W, F>(mut self, builder: F) -> Self
where
W: Widget + 'static,
F: FnOnce(Rect) -> W,
{
let col = self.next % self.cols;
let row = self.next / self.cols;
let x = col * (self.cell_w + self.spacing);
let y = row * (self.cell_h + self.spacing);
let rect = Rect {
x,
y,
width: self.cell_w,
height: self.cell_h,
};
self.children.push(Box::new(builder(rect)));
self.next += 1;
let w = x + self.cell_w;
let h = y + self.cell_h;
if w > self.bounds.width {
self.bounds.width = w;
}
if h > self.bounds.height {
self.bounds.height = h;
}
self
}
}
impl Widget for Grid {
fn bounds(&self) -> Rect {
self.bounds
}
fn draw(&self, renderer: &mut dyn Renderer) {
for child in &self.children {
child.draw(renderer);
}
}
fn handle_event(&mut self, event: &Event) -> bool {
for child in &mut self.children {
if child.handle_event(event) {
return true;
}
}
false
}
}
pub struct BoxLayout {
inner: Container,
}
impl BoxLayout {
pub fn new(bounds: Rect) -> Self {
Self {
inner: Container::new(bounds),
}
}
pub fn style_mut(&mut self) -> &mut rlvgl_core::style::Style {
&mut self.inner.style
}
}
impl Widget for BoxLayout {
fn bounds(&self) -> Rect {
self.inner.bounds()
}
fn draw(&self, renderer: &mut dyn Renderer) {
self.inner.draw(renderer);
}
fn handle_event(&mut self, event: &Event) -> bool {
self.inner.handle_event(event)
}
}
#[cfg(test)]
mod tests {
use super::*;
use rlvgl_widgets::label::Label;
#[test]
fn vstack_stacks_vertically() {
let stack = VStack::new(20)
.spacing(2)
.child(10, |r| Label::new("a", r))
.child(10, |r| Label::new("b", r));
assert_eq!(stack.bounds().height, 22);
}
#[test]
fn hstack_stacks_horizontally() {
let stack = HStack::new(10)
.spacing(1)
.child(5, |r| Label::new("a", r))
.child(5, |r| Label::new("b", r));
assert_eq!(stack.bounds().width, 11);
}
#[test]
fn grid_places_cells() {
let grid = Grid::new(2, 5, 5)
.spacing(1)
.child(|r| Label::new("a", r))
.child(|r| Label::new("b", r))
.child(|r| Label::new("c", r));
assert_eq!(grid.bounds().height, 11);
assert_eq!(grid.bounds().width, 11);
}
}