Skip to main content

rtcom_tui/
layout.rs

1//! Top-level chrome layout for the TUI main screen.
2//!
3//! The main screen has three horizontal bands:
4//!
5//! ```text
6//! ┌────────────────────────────────────┐
7//! │ top bar (1 row, status / device)   │
8//! ├────────────────────────────────────┤
9//! │ body (serial pane)                 │
10//! │                                    │
11//! │                                    │
12//! ├────────────────────────────────────┤
13//! │ bottom bar (1 row, hint text)      │
14//! └────────────────────────────────────┘
15//! ```
16//!
17//! [`main_chrome`] splits a [`Rect`] into exactly those three bands so
18//! the top-level renderer can hand each sub-rect to the right widget.
19
20use ratatui::layout::{Constraint, Layout, Rect};
21
22/// Splits the terminal area into top bar (1 row), body (min 1 row),
23/// and bottom bar (1 row).
24///
25/// Returned tuple is `(top, body, bottom)`. When `area.height < 3`
26/// the ratatui [`Layout`] engine still yields three rects but some
27/// may be zero-sized; the caller is responsible for skipping them.
28///
29/// # Examples
30///
31/// ```
32/// use ratatui::layout::Rect;
33/// use rtcom_tui::layout::main_chrome;
34///
35/// let (top, body, bottom) = main_chrome(Rect::new(0, 0, 80, 24));
36/// assert_eq!(top.height, 1);
37/// assert_eq!(body.height, 22);
38/// assert_eq!(bottom.height, 1);
39/// ```
40#[must_use]
41pub fn main_chrome(area: Rect) -> (Rect, Rect, Rect) {
42    let rows = Layout::vertical([
43        Constraint::Length(1),
44        Constraint::Min(1),
45        Constraint::Length(1),
46    ])
47    .split(area);
48    (rows[0], rows[1], rows[2])
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn main_chrome_splits_80x24() {
57        let (top, body, bottom) = main_chrome(Rect::new(0, 0, 80, 24));
58        assert_eq!(top.height, 1);
59        assert_eq!(bottom.height, 1);
60        assert_eq!(body.height, 22);
61        assert_eq!(top.width, 80);
62        assert_eq!(body.width, 80);
63        assert_eq!(bottom.width, 80);
64    }
65
66    #[test]
67    fn main_chrome_preserves_origin() {
68        let (top, body, bottom) = main_chrome(Rect::new(5, 2, 40, 10));
69        assert_eq!(top.y, 2);
70        assert_eq!(body.y, 3);
71        // bottom.y = origin.y + height - 1 = 2 + 10 - 1 = 11
72        assert_eq!(bottom.y, 11);
73        assert_eq!(top.x, 5);
74        assert_eq!(body.x, 5);
75        assert_eq!(bottom.x, 5);
76    }
77}