use std::ops::DerefMut;
use crate::{
geometry::{point, window},
widget::*,
};
#[derive(Debug, Default)]
pub struct Stack<S> {
layout: StackLayout,
widgets: S,
}
impl<S> Stack<S> {
pub fn east_south(widgets: S) -> Self {
Self {
layout: StackLayout::EastSouth,
widgets,
}
}
pub fn south_east(widgets: S) -> Self {
Self {
layout: StackLayout::SouthEast,
widgets,
}
}
pub fn layered(widgets: S) -> Self {
Self {
layout: StackLayout::Layered,
widgets,
}
}
pub fn content(&self) -> &S {
&self.widgets
}
pub fn content_mut(&mut self) -> &mut S {
&mut self.widgets
}
}
impl<S> Stack<Vec<S>> {
pub fn add(mut self, widget: S) -> Self {
self.widgets.push(widget);
self
}
}
impl<'a, M, A: AppEvent> Stack<Vec<Box<dyn Widget<M, A> + 'a>>> {
pub fn east_south_boxes(widgets: Vec<Box<dyn Widget<M, A> + 'a>>) -> Self {
Self::east_south(widgets)
}
pub fn south_east_boxes(widgets: Vec<Box<dyn Widget<M, A> + 'a>>) -> Self {
Self::south_east(widgets)
}
pub fn layered_boxes(widgets: Vec<Box<dyn Widget<M, A> + 'a>>) -> Self {
Self::layered(widgets)
}
pub fn add_box(mut self, widget: impl Widget<M, A> + 'a) -> Self {
self.widgets.push(Box::new(widget));
self
}
}
impl<S> AnchorPlacementEnabled for Stack<S> {}
impl<M, S, W, A: AppEvent> Widget<M, A> for Stack<S>
where
W: Widget<M, A>,
S: DerefMut<Target = [W]>,
{
fn update(
&mut self,
model: &mut M,
input: &Event<A>,
screen: &mut Screen,
painter: &Painter,
) -> Window {
let pscope = painter.scope();
let mut position = pscope.position();
let mut bulk = 0;
let mut size = point(0, 0);
for widget in self.widgets.deref_mut().iter_mut() {
let scope = pscope & window(position, pscope.size());
let scope = widget.update_asserted(model, input, screen, &painter.with_scope(scope));
let botom_right = scope.bottom_right();
size = point(
u16::max(size.col, botom_right.col - pscope.col),
u16::max(size.row, botom_right.row - pscope.row),
);
match self.layout {
StackLayout::Layered => {}
StackLayout::EastSouth => {
bulk = u16::max(bulk, scope.height);
position = point(scope.col.saturating_add(scope.width), position.row);
if position.col >= pscope.col.saturating_add(pscope.width) {
position = point(pscope.col, position.row.saturating_add(bulk));
bulk = 0;
}
}
StackLayout::SouthEast => {
bulk = u16::max(bulk, scope.width);
position = point(position.col, scope.row.saturating_add(scope.height));
if position.row >= pscope.row.saturating_add(pscope.height) {
position = point(position.col.saturating_add(bulk), pscope.row);
bulk = 0;
}
}
};
}
window(pscope.position(), size)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum StackLayout {
Layered,
EastSouth,
SouthEast,
}
impl Default for StackLayout {
fn default() -> Self {
StackLayout::Layered
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::Widget;
use std::marker::PhantomData;
#[derive(Default, Debug, Clone, Copy)]
struct MockRogueSpacer<M> {
phantom: PhantomData<M>,
}
impl<M, A: AppEvent> Widget<M, A> for MockRogueSpacer<M> {
fn update(
&mut self,
_model: &mut M,
_input: &crate::input::events::Event<A>,
_screen: &mut crate::Screen,
_painter: &crate::Painter,
) -> crate::prelude::Window {
Window {
col: 1,
row: 1,
width: u16::MAX,
height: u16::MAX,
}
}
fn update_asserted(
&mut self,
model: &mut M,
input: &Event<A>,
screen: &mut Screen,
painter: &Painter,
) -> Window {
self.update(model, input, screen, painter)
}
}
#[test]
fn test_oversize() {
let stacks = vec![
Stack::east_south(vec![
MockRogueSpacer::default(),
MockRogueSpacer::default(),
MockRogueSpacer::default(),
]),
Stack::south_east(vec![
MockRogueSpacer::default(),
MockRogueSpacer::default(),
MockRogueSpacer::default(),
]),
];
let painter = Painter::default();
let painter = painter.with_scope(window(point(65530, 65530), point(65530, 65530)));
let mut screen = Screen::default();
let event = Event::<NoAppEvent>::Refresh(1);
for mut s in stacks {
s.update(&mut (), &event, &mut screen, &painter);
}
}
#[test]
fn test_reported_scope() {
let stacks = vec![
Stack::<Vec<Fill<()>>>::east_south(vec![
Fill::default(),
Fill::default(),
Fill::default(),
]),
Stack::south_east(vec![Fill::default(), Fill::default(), Fill::default()]),
];
let painter = Painter::default();
let painter = painter.with_scope(window(point(5, 3), point(4, 2)));
let mut screen = Screen::default();
let event = Event::<NoAppEvent>::Refresh(1);
for mut s in stacks {
s.update_asserted(&mut (), &event, &mut screen, &painter);
}
}
}