use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Paragraph},
Frame,
};
pub fn standard_tui_layout(area: Rect) -> (Rect, Rect) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Min(10), Constraint::Length(3), ])
.split(area);
(chunks[0], chunks[1])
}
pub fn main_and_sidebar_layout(area: Rect) -> (Rect, Rect) {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage(60), Constraint::Percentage(40), ])
.split(area);
(chunks[0], chunks[1])
}
pub fn two_panel_layout(area: Rect, left_percent: u16) -> (Rect, Rect) {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage(left_percent),
Constraint::Percentage(100 - left_percent),
])
.split(area);
(chunks[0], chunks[1])
}
pub fn render_footer_hints(f: &mut Frame, area: Rect, hints: &[(&str, &str, Color)]) {
let mut spans = vec![Span::raw(" ")];
for (i, (key, label, color)) in hints.iter().enumerate() {
spans.push(Span::styled(
format!("{}:", key),
Style::default().fg(*color).add_modifier(Modifier::BOLD),
));
let trailing = if i + 1 == hints.len() { "" } else { " " };
spans.push(Span::raw(format!(" {}{}", label, trailing)));
}
let footer = Paragraph::new(vec![Line::from(spans)]).block(
Block::default()
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::DarkGray)),
);
f.render_widget(footer, area);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn standard_tui_layout_splits_content_and_footer() {
let area = Rect::new(0, 0, 100, 40);
let (content, footer) = standard_tui_layout(area);
assert_eq!(footer.height, 3);
assert_eq!(content.height, 37);
assert_eq!(content.y, 0);
assert_eq!(footer.y, 37);
}
#[test]
fn two_panel_layout_splits_by_given_percentage() {
let area = Rect::new(0, 0, 100, 40);
let (left, right) = two_panel_layout(area, 30);
assert_eq!(left.width, 30);
assert_eq!(right.width, 70);
assert_eq!(left.x, 0);
assert_eq!(right.x, 30);
}
#[test]
fn two_panel_layout_matches_main_and_sidebar_at_60_40() {
let area = Rect::new(0, 0, 100, 40);
let (left, right) = two_panel_layout(area, 60);
let (expected_main, expected_sidebar) = main_and_sidebar_layout(area);
assert_eq!((left.width, left.x), (expected_main.width, expected_main.x));
assert_eq!((right.width, right.x), (expected_sidebar.width, expected_sidebar.x));
}
}