#![allow(dead_code)]
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use super::state::{Mode, PanelId};
#[derive(Debug, Clone)]
pub struct PanelConfig {
pub id: PanelId,
pub title: &'static str,
pub focusable: bool,
pub min_width: u16,
}
#[derive(Debug, Clone)]
pub struct ModeLayout {
pub panels: Vec<PanelConfig>,
pub constraints: Vec<Constraint>,
}
impl ModeLayout {
pub fn get_panel(&self, id: PanelId) -> Option<&PanelConfig> {
self.panels.iter().find(|p| p.id == id)
}
}
pub fn get_mode_layout(mode: Mode) -> ModeLayout {
match mode {
Mode::Explore => explore_layout(),
Mode::Commit => commit_layout(),
Mode::Review => review_layout(),
Mode::PR => pr_layout(),
Mode::Changelog => changelog_layout(),
Mode::ReleaseNotes => release_notes_layout(),
}
}
fn explore_layout() -> ModeLayout {
ModeLayout {
panels: vec![
PanelConfig {
id: PanelId::Left,
title: "Files",
focusable: true,
min_width: 20,
},
PanelConfig {
id: PanelId::Center,
title: "Code",
focusable: true,
min_width: 40,
},
PanelConfig {
id: PanelId::Right,
title: "Context",
focusable: true,
min_width: 25,
},
],
constraints: vec![
Constraint::Percentage(20),
Constraint::Percentage(50),
Constraint::Percentage(30),
],
}
}
fn commit_layout() -> ModeLayout {
ModeLayout {
panels: vec![
PanelConfig {
id: PanelId::Left,
title: "Staged",
focusable: true,
min_width: 20,
},
PanelConfig {
id: PanelId::Center,
title: "Message",
focusable: true,
min_width: 40,
},
PanelConfig {
id: PanelId::Right,
title: "Diff",
focusable: true,
min_width: 25,
},
],
constraints: vec![
Constraint::Percentage(18),
Constraint::Percentage(42),
Constraint::Percentage(40),
],
}
}
fn review_layout() -> ModeLayout {
ModeLayout {
panels: vec![
PanelConfig {
id: PanelId::Left,
title: "Files",
focusable: true,
min_width: 20,
},
PanelConfig {
id: PanelId::Center,
title: "Review",
focusable: true,
min_width: 40,
},
PanelConfig {
id: PanelId::Right,
title: "Diff",
focusable: true,
min_width: 25,
},
],
constraints: vec![
Constraint::Percentage(18),
Constraint::Percentage(42),
Constraint::Percentage(40),
],
}
}
fn pr_layout() -> ModeLayout {
ModeLayout {
panels: vec![
PanelConfig {
id: PanelId::Left,
title: "Commits",
focusable: true,
min_width: 20,
},
PanelConfig {
id: PanelId::Center,
title: "PR",
focusable: true,
min_width: 40,
},
PanelConfig {
id: PanelId::Right,
title: "Diff",
focusable: true,
min_width: 25,
},
],
constraints: vec![
Constraint::Percentage(18),
Constraint::Percentage(42),
Constraint::Percentage(40),
],
}
}
fn changelog_layout() -> ModeLayout {
ModeLayout {
panels: vec![
PanelConfig {
id: PanelId::Left,
title: "Range",
focusable: true,
min_width: 15,
},
PanelConfig {
id: PanelId::Center,
title: "Changelog",
focusable: true,
min_width: 35,
},
PanelConfig {
id: PanelId::Right,
title: "Changes",
focusable: true,
min_width: 30,
},
],
constraints: vec![
Constraint::Percentage(15),
Constraint::Percentage(45),
Constraint::Percentage(40),
],
}
}
fn release_notes_layout() -> ModeLayout {
ModeLayout {
panels: vec![
PanelConfig {
id: PanelId::Left,
title: "Commits",
focusable: true,
min_width: 15,
},
PanelConfig {
id: PanelId::Center,
title: "Release Notes",
focusable: true,
min_width: 35,
},
PanelConfig {
id: PanelId::Right,
title: "Changes",
focusable: true,
min_width: 30,
},
],
constraints: vec![
Constraint::Percentage(15),
Constraint::Percentage(45),
Constraint::Percentage(40),
],
}
}
#[derive(Debug, Clone)]
pub struct LayoutAreas {
pub header: Rect,
pub tabs: Rect,
pub content: Rect,
pub panels: Vec<Rect>,
pub companion_bar: Option<Rect>,
pub status: Rect,
}
pub fn calculate_layout(area: Rect, mode: Mode) -> LayoutAreas {
let has_companion_bar = matches!(mode, Mode::Explore);
let constraints = if has_companion_bar {
vec![
Constraint::Length(1), Constraint::Length(2), Constraint::Min(10), Constraint::Length(1), Constraint::Length(1), ]
} else {
vec![
Constraint::Length(1), Constraint::Length(2), Constraint::Min(10), Constraint::Length(1), ]
};
let main_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(constraints)
.split(area);
let (header, tabs, content, companion_bar, status) = if has_companion_bar {
(
main_chunks[0],
main_chunks[1],
main_chunks[2],
Some(main_chunks[3]),
main_chunks[4],
)
} else {
(
main_chunks[0],
main_chunks[1],
main_chunks[2],
None,
main_chunks[3],
)
};
let mode_layout = get_mode_layout(mode);
let panel_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints(mode_layout.constraints.clone())
.split(content);
let panels = panel_chunks.to_vec();
LayoutAreas {
header,
tabs,
content,
panels,
companion_bar,
status,
}
}
pub fn panel_inner(area: Rect) -> Rect {
Rect {
x: area.x.saturating_add(1),
y: area.y.saturating_add(1),
width: area.width.saturating_sub(2),
height: area.height.saturating_sub(2),
}
}
pub fn is_wide_layout(width: u16) -> bool {
width >= 100
}
pub fn is_narrow_layout(width: u16) -> bool {
width < 80
}
pub fn responsive_constraints(width: u16, mode: Mode) -> Vec<Constraint> {
let default = get_mode_layout(mode).constraints;
if is_narrow_layout(width) {
vec![
Constraint::Percentage(25),
Constraint::Percentage(75),
Constraint::Length(0),
]
} else if !is_wide_layout(width) {
vec![
Constraint::Percentage(22),
Constraint::Percentage(48),
Constraint::Percentage(30),
]
} else {
default
}
}