use ratatui::layout::{Constraint, Direction, Layout, Rect};
#[derive(Debug, Clone)]
pub struct Preset {
pub rows: Vec<LayoutRow>,
}
#[derive(Debug, Clone)]
pub struct LayoutRow {
pub panels: Vec<String>,
pub height: Constraint,
}
impl Preset {
#[must_use]
pub fn default_preset() -> Self {
Self {
rows: vec![
LayoutRow { panels: vec!["cpu".to_string()], height: Constraint::Percentage(30) },
LayoutRow {
panels: vec!["memory".to_string()],
height: Constraint::Percentage(25),
},
LayoutRow {
panels: vec!["process".to_string()],
height: Constraint::Percentage(45),
},
],
}
}
#[must_use]
pub fn calculate(&self, area: Rect) -> Vec<Vec<Rect>> {
let row_constraints: Vec<Constraint> = self.rows.iter().map(|r| r.height).collect();
let row_areas = Layout::default()
.direction(Direction::Vertical)
.constraints(row_constraints)
.split(area);
self.rows
.iter()
.enumerate()
.map(|(i, row)| {
let panel_count = row.panels.len();
if panel_count == 0 {
return vec![];
}
let panel_constraints: Vec<Constraint> =
(0..panel_count).map(|_| Constraint::Ratio(1, panel_count as u32)).collect();
Layout::default()
.direction(Direction::Horizontal)
.constraints(panel_constraints)
.split(row_areas[i])
.to_vec()
})
.collect()
}
}
impl Default for Preset {
fn default() -> Self {
Self::default_preset()
}
}
#[derive(Debug, Clone)]
pub struct LayoutManager {
presets: Vec<Preset>,
current: usize,
}
impl LayoutManager {
#[must_use]
pub fn new() -> Self {
Self { presets: vec![Preset::default_preset()], current: 0 }
}
pub fn switch_to(&mut self, index: usize) {
if index < self.presets.len() {
self.current = index;
}
}
#[must_use]
pub fn current(&self) -> &Preset {
&self.presets[self.current]
}
pub fn add_preset(&mut self, preset: Preset) {
self.presets.push(preset);
}
}
impl Default for LayoutManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_preset_default() {
let preset = Preset::default_preset();
assert_eq!(preset.rows.len(), 3);
}
#[test]
fn test_preset_calculate() {
let preset = Preset::default_preset();
let area = Rect::new(0, 0, 100, 50);
let areas = preset.calculate(area);
assert_eq!(areas.len(), 3);
assert!(!areas[0].is_empty());
}
#[test]
fn test_layout_manager_switch() {
let mut manager = LayoutManager::new();
manager.add_preset(Preset {
rows: vec![LayoutRow {
panels: vec!["cpu".to_string(), "memory".to_string()],
height: Constraint::Percentage(100),
}],
});
manager.switch_to(1);
assert_eq!(manager.current().rows.len(), 1);
manager.switch_to(0);
assert_eq!(manager.current().rows.len(), 3);
}
#[test]
fn test_layout_manager_invalid_switch() {
let mut manager = LayoutManager::new();
manager.switch_to(999); assert_eq!(manager.current, 0);
}
#[test]
fn test_preset_default_trait() {
let preset = Preset::default();
assert_eq!(preset.rows.len(), 3);
}
#[test]
fn test_layout_manager_default_trait() {
let manager = LayoutManager::default();
assert_eq!(manager.current().rows.len(), 3);
}
#[test]
fn test_preset_calculate_empty_row() {
let preset = Preset {
rows: vec![
LayoutRow {
panels: vec![], height: Constraint::Percentage(50),
},
LayoutRow { panels: vec!["cpu".to_string()], height: Constraint::Percentage(50) },
],
};
let area = Rect::new(0, 0, 100, 50);
let areas = preset.calculate(area);
assert_eq!(areas.len(), 2);
assert!(areas[0].is_empty()); assert!(!areas[1].is_empty()); }
#[test]
fn test_preset_debug_clone() {
let preset = Preset::default();
let cloned = preset.clone();
let _ = format!("{cloned:?}");
}
#[test]
fn test_layout_row_debug_clone() {
let row =
LayoutRow { panels: vec!["test".to_string()], height: Constraint::Percentage(50) };
let cloned = row.clone();
let _ = format!("{cloned:?}");
}
#[test]
fn test_layout_manager_debug_clone() {
let manager = LayoutManager::new();
let cloned = manager.clone();
let _ = format!("{cloned:?}");
}
}