1use itertools::Itertools;
17use ratatui::{
18 crossterm::event::{self, Event, KeyCode, KeyEventKind},
19 layout::{
20 Constraint::{self, Length, Max, Min, Percentage, Ratio},
21 Layout, Rect,
22 },
23 style::{Color, Style, Stylize},
24 text::Line,
25 widgets::{Block, Paragraph},
26 DefaultTerminal, Frame,
27};
28
29fn main() -> color_eyre::Result<()> {
30 color_eyre::install()?;
31 let terminal = ratatui::init();
32 let app_result = run(terminal);
33 ratatui::restore();
34 app_result
35}
36
37fn run(mut terminal: DefaultTerminal) -> color_eyre::Result<()> {
38 loop {
39 terminal.draw(draw)?;
40 if let Event::Key(key) = event::read()? {
41 if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
42 break Ok(());
43 }
44 }
45 }
46}
47
48#[allow(clippy::too_many_lines)]
49fn draw(frame: &mut Frame) {
50 let vertical = Layout::vertical([
51 Length(4), Length(50), Min(0), ]);
55 let [text_area, examples_area, _] = vertical.areas(frame.area());
56
57 frame.render_widget(
59 Paragraph::new(vec![
60 Line::from("Horizontal Layout Example. Press q to quit".dark_gray()).centered(),
61 Line::from("Each line has 2 constraints, plus Min(0) to fill the remaining space."),
62 Line::from("E.g. the second line of the Len/Min box is [Length(2), Min(2), Min(0)]"),
63 Line::from("Note: constraint labels that don't fit are truncated"),
64 ]),
65 text_area,
66 );
67
68 let example_rows = Layout::vertical([
69 Length(9),
70 Length(9),
71 Length(9),
72 Length(9),
73 Length(9),
74 Min(0), ])
76 .split(examples_area);
77 let example_areas = example_rows.iter().flat_map(|area| {
78 Layout::horizontal([
79 Length(14),
80 Length(14),
81 Length(14),
82 Length(14),
83 Length(14),
84 Min(0), ])
86 .split(*area)
87 .iter()
88 .copied()
89 .take(5) .collect_vec()
91 });
92
93 let examples = [
96 (
97 "Len",
98 [
99 Length(0),
100 Length(2),
101 Length(3),
102 Length(6),
103 Length(10),
104 Length(15),
105 ],
106 ),
107 ("Min", [Min(0), Min(2), Min(3), Min(6), Min(10), Min(15)]),
108 ("Max", [Max(0), Max(2), Max(3), Max(6), Max(10), Max(15)]),
109 (
110 "Perc",
111 [
112 Percentage(0),
113 Percentage(25),
114 Percentage(50),
115 Percentage(75),
116 Percentage(100),
117 Percentage(150),
118 ],
119 ),
120 (
121 "Ratio",
122 [
123 Ratio(0, 4),
124 Ratio(1, 4),
125 Ratio(2, 4),
126 Ratio(3, 4),
127 Ratio(4, 4),
128 Ratio(6, 4),
129 ],
130 ),
131 ];
132
133 for ((a, b), area) in examples
134 .iter()
135 .cartesian_product(examples.iter())
136 .zip(example_areas)
137 {
138 let (name_a, examples_a) = a;
139 let (name_b, examples_b) = b;
140 let constraints = examples_a.iter().copied().zip(examples_b.iter().copied());
141 render_example_combination(frame, area, &format!("{name_a}/{name_b}"), constraints);
142 }
143}
144
145fn render_example_combination(
147 frame: &mut Frame,
148 area: Rect,
149 title: &str,
150 constraints: impl ExactSizeIterator<Item = (Constraint, Constraint)>,
151) {
152 let block = Block::bordered()
153 .title(title.gray())
154 .style(Style::reset())
155 .border_style(Style::default().fg(Color::DarkGray));
156 let inner = block.inner(area);
157 frame.render_widget(block, area);
158 let layout = Layout::vertical(vec![Length(1); constraints.len() + 1]).split(inner);
159 for ((a, b), &area) in constraints.into_iter().zip(layout.iter()) {
160 render_single_example(frame, area, vec![a, b, Min(0)]);
161 }
162 frame.render_widget(Paragraph::new("123456789012"), layout[6]);
165}
166
167fn render_single_example(frame: &mut Frame, area: Rect, constraints: Vec<Constraint>) {
169 let red = Paragraph::new(constraint_label(constraints[0])).on_red();
170 let blue = Paragraph::new(constraint_label(constraints[1])).on_blue();
171 let green = Paragraph::new("ยท".repeat(12)).on_green();
172 let horizontal = Layout::horizontal(constraints);
173 let [r, b, g] = horizontal.areas(area);
174 frame.render_widget(red, r);
175 frame.render_widget(blue, b);
176 frame.render_widget(green, g);
177}
178
179fn constraint_label(constraint: Constraint) -> String {
180 match constraint {
181 Constraint::Ratio(a, b) => format!("{a}:{b}"),
182 Constraint::Length(n)
183 | Constraint::Min(n)
184 | Constraint::Max(n)
185 | Constraint::Percentage(n)
186 | Constraint::Fill(n) => format!("{n}"),
187 }
188}