use crate::layout::Rect;
use crate::widget::traits::{RenderContext, View, WidgetProps};
use crate::{impl_props_builders, impl_styled_view};
struct LayerChild {
child: Box<dyn View>,
z_index: i16,
}
pub struct Layers {
children: Vec<LayerChild>,
min_width: u16,
min_height: u16,
max_width: u16,
max_height: u16,
props: WidgetProps,
}
impl Layers {
pub fn new() -> Self {
Self {
children: Vec::new(),
min_width: 0,
min_height: 0,
max_width: 0,
max_height: 0,
props: WidgetProps::new(),
}
}
pub fn child<V: View + 'static>(mut self, child: V) -> Self {
self.children.push(LayerChild {
child: Box::new(child),
z_index: 0,
});
self
}
pub fn child_z<V: View + 'static>(mut self, child: V, z_index: i16) -> Self {
self.children.push(LayerChild {
child: Box::new(child),
z_index,
});
self
}
pub fn children<I, V>(mut self, children: I) -> Self
where
I: IntoIterator<Item = V>,
V: View + 'static,
{
for child in children {
self.children.push(LayerChild {
child: Box::new(child),
z_index: 0,
});
}
self
}
pub fn len(&self) -> usize {
self.children.len()
}
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
pub fn min_width(mut self, width: u16) -> Self {
self.min_width = width;
self
}
pub fn min_height(mut self, height: u16) -> Self {
self.min_height = height;
self
}
pub fn max_width(mut self, width: u16) -> Self {
self.max_width = width;
self
}
pub fn max_height(mut self, height: u16) -> Self {
self.max_height = height;
self
}
pub fn min_size(self, width: u16, height: u16) -> Self {
self.min_width(width).min_height(height)
}
pub fn max_size(self, width: u16, height: u16) -> Self {
self.max_width(width).max_height(height)
}
pub fn constrain(self, min_w: u16, min_h: u16, max_w: u16, max_h: u16) -> Self {
self.min_width(min_w)
.min_height(min_h)
.max_width(max_w)
.max_height(max_h)
}
fn apply_constraints(&self, area: Rect) -> Rect {
let eff_max_w = if self.max_width > 0 {
self.max_width.max(self.min_width)
} else {
u16::MAX
};
let eff_max_h = if self.max_height > 0 {
self.max_height.max(self.min_height)
} else {
u16::MAX
};
let width = area.width.clamp(self.min_width, eff_max_w);
let height = area.height.clamp(self.min_height, eff_max_h);
Rect::new(area.x, area.y, width, height)
}
}
impl Default for Layers {
fn default() -> Self {
Self::new()
}
}
impl View for Layers {
crate::impl_view_meta!("Layers");
fn render(&self, ctx: &mut RenderContext) {
if self.children.is_empty() {
return;
}
let _area = self.apply_constraints(ctx.area);
let mut order: Vec<usize> = (0..self.children.len()).collect();
order.sort_by_key(|&i| self.children[i].z_index);
for &idx in &order {
if self.children[idx].child.needs_render() {
self.children[idx].child.render(ctx);
}
}
}
}
impl_styled_view!(Layers);
impl_props_builders!(Layers);
pub fn layers() -> Layers {
Layers::new()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::render::Buffer;
use crate::widget::Text;
#[test]
fn test_layers_new_empty() {
let l = Layers::new();
assert!(l.is_empty());
assert_eq!(l.len(), 0);
}
#[test]
fn test_layers_add_children() {
let l = Layers::new()
.child(Text::new("Bottom"))
.child(Text::new("Top"));
assert_eq!(l.len(), 2);
}
#[test]
fn test_layers_add_with_z_index() {
let l = Layers::new()
.child_z(Text::new("Low"), -1)
.child(Text::new("Default"))
.child_z(Text::new("High"), 10);
assert_eq!(l.len(), 3);
assert_eq!(l.children[0].z_index, -1);
assert_eq!(l.children[1].z_index, 0);
assert_eq!(l.children[2].z_index, 10);
}
#[test]
fn test_layers_render_order() {
let mut buf = Buffer::new(10, 3);
let area = Rect::new(0, 0, 10, 3);
let mut ctx = RenderContext::new(&mut buf, area);
let l = Layers::new()
.child_z(Text::new("AAAA"), 0)
.child_z(Text::new("BB"), 1); l.render(&mut ctx);
assert_eq!(buf.get(0, 0).unwrap().symbol, 'B');
assert_eq!(buf.get(1, 0).unwrap().symbol, 'B');
assert_eq!(buf.get(2, 0).unwrap().symbol, 'A');
assert_eq!(buf.get(3, 0).unwrap().symbol, 'A');
}
#[test]
fn test_layers_render_empty_no_panic() {
let mut buf = Buffer::new(10, 5);
let area = Rect::new(0, 0, 10, 5);
let mut ctx = RenderContext::new(&mut buf, area);
let l = Layers::new();
l.render(&mut ctx);
}
#[test]
fn test_layers_default() {
let l = Layers::default();
assert!(l.is_empty());
}
#[test]
fn test_layers_helper_fn() {
let l = layers().child(Text::new("X"));
assert_eq!(l.len(), 1);
}
}