1use std::io::{self, Write};
6
7use crate::layout_panel::LayoutPanel;
8use crate::renderer::{Component, Renderer};
9
10pub enum Align {
12 Box,
14 Content(u16),
16 Full,
18}
19
20pub struct LayoutStack {
27 row: u16,
28 terminal_width: u16,
29 box_column: u16,
30 box_width: u16,
31}
32
33impl LayoutStack {
34 pub fn new(start_row: u16, terminal_width: u16, box_column: u16, box_width: u16) -> Self {
35 Self {
36 row: start_row,
37 terminal_width,
38 box_column,
39 box_width,
40 }
41 }
42
43 pub fn row(&self) -> u16 {
44 self.row
45 }
46
47 pub fn terminal_width(&self) -> u16 {
48 self.terminal_width
49 }
50
51 #[cfg(not(tarpaulin_include))]
54 pub fn draw<W: Write>(
55 &mut self,
56 renderer: &mut Renderer<W>,
57 component: &impl Component,
58 align: Align,
59 ) -> io::Result<u16> {
60 let panel = self.panel(align);
61 let rows = renderer.draw(component, panel)?;
62 self.row = self.row.saturating_add(rows);
63 Ok(rows)
64 }
65
66 pub fn skip(&mut self, rows: u16) {
68 self.row = self.row.saturating_add(rows);
69 }
70
71 fn panel(&self, align: Align) -> LayoutPanel {
72 match align {
73 Align::Box => LayoutPanel {
74 row: self.row,
75 column: self.box_column,
76 width: self.box_width,
77 },
78 Align::Content(width) => LayoutPanel::centered(self.terminal_width, width, self.row),
79 Align::Full => LayoutPanel {
80 row: self.row,
81 column: 0,
82 width: self.terminal_width,
83 },
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn new_starts_at_given_row() {
94 let stack = LayoutStack::new(5, 80, 10, 30);
95 assert_eq!(stack.row(), 5);
96 assert_eq!(stack.terminal_width(), 80);
97 }
98
99 #[test]
100 fn skip_advances_row() {
101 let mut stack = LayoutStack::new(0, 80, 10, 30);
102 stack.skip(3);
103 assert_eq!(stack.row(), 3);
104 stack.skip(2);
105 assert_eq!(stack.row(), 5);
106 }
107
108 #[test]
109 fn panel_box_uses_precomputed_geometry() {
110 let stack = LayoutStack::new(10, 80, 15, 40);
111 let panel = stack.panel(Align::Box);
112 assert_eq!(panel.row, 10);
113 assert_eq!(panel.column, 15);
114 assert_eq!(panel.width, 40);
115 }
116
117 #[test]
118 fn panel_content_centers_width() {
119 let stack = LayoutStack::new(5, 80, 0, 0);
120 let panel = stack.panel(Align::Content(20));
121 assert_eq!(panel.row, 5);
122 assert_eq!(panel.column, 30); assert_eq!(panel.width, 20);
124 }
125
126 #[test]
127 fn panel_full_spans_terminal() {
128 let stack = LayoutStack::new(7, 120, 10, 30);
129 let panel = stack.panel(Align::Full);
130 assert_eq!(panel.row, 7);
131 assert_eq!(panel.column, 0);
132 assert_eq!(panel.width, 120);
133 }
134}