use crate::tui::ui::SidebarSections;
use std::path::PathBuf;
pub use crate::config::{SidebarOrientation, SidebarPosition};
pub const SIDEBAR_MIN_WIDTH: u16 = 120;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResolvedSidebarLayout {
Hidden,
SideBySide { sidebar_left: bool },
Stacked,
}
impl ResolvedSidebarLayout {
pub fn split_percentages(self) -> Option<(u16, u16)> {
match self {
ResolvedSidebarLayout::Hidden => None,
ResolvedSidebarLayout::SideBySide { .. } => Some((55, 45)),
ResolvedSidebarLayout::Stacked => Some((42, 58)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SidebarMode {
Commits,
Stashes,
}
impl SidebarMode {
pub fn label(self) -> &'static str {
match self {
SidebarMode::Commits => "commits",
SidebarMode::Stashes => "stashes",
}
}
}
pub fn split_section_heights(available: u16, agents_len: u16, wt_len: u16, commits_len: u16) -> (u16, u16, u16) {
let natural = |len: u16| if len == 0 { 0 } else { len.saturating_add(2) };
let natural_a = natural(agents_len);
let natural_w = natural(wt_len);
let natural_c = commits_len.saturating_add(2).max(3);
if natural_a as u32 + natural_w as u32 + natural_c as u32 <= available as u32 {
return (natural_a, natural_w, available - natural_a - natural_w);
}
let floor_a = natural_a;
let floor_w = natural_w.min(7);
let floor_c = natural_c.min(5);
let base = floor_a + floor_w + floor_c;
if base > available {
let c = floor_c.min(available);
let rest = available - c;
let w = floor_w.min(rest);
let a = floor_a.min(rest - w);
return (a, w, c);
}
let surplus = available - base;
let total = agents_len as u32 + wt_len as u32 + commits_len as u32;
let give = |len: u16, floor_v: u16, nat: u16| -> u16 {
if total == 0 {
return 0;
}
((surplus as u32 * len as u32 / total) as u16).min(nat - floor_v)
};
let mut a = floor_a + give(agents_len, floor_a, natural_a);
let mut w = floor_w + give(wt_len, floor_w, natural_w);
let mut c = floor_c + give(commits_len, floor_c, natural_c);
let mut residue = available - a - w - c;
let mut top_up = |h: &mut u16, nat: u16| {
let room = (nat - *h).min(residue);
*h += room;
residue -= room;
};
top_up(&mut c, natural_c);
top_up(&mut w, natural_w);
top_up(&mut a, natural_a);
(a, w, c)
}
#[derive(Debug)]
pub struct SidebarState {
pub open: bool,
pub position: SidebarPosition,
pub orientation: SidebarOrientation,
pub focused: bool,
pub scroll: u16,
pub max_scroll: u16,
pub wt_scroll: u16,
pub wt_max_scroll: u16,
pub cache: Option<((PathBuf, SidebarMode), SidebarSections)>,
pub mode: SidebarMode,
}
impl Default for SidebarState {
fn default() -> Self {
Self::new()
}
}
impl SidebarState {
pub fn new() -> Self {
Self {
open: true,
position: SidebarPosition::default(),
orientation: SidebarOrientation::default(),
focused: false,
scroll: 0,
max_scroll: 0,
wt_scroll: 0,
wt_max_scroll: 0,
cache: None,
mode: SidebarMode::Commits,
}
}
pub fn resolve_layout(&self, width: u16) -> ResolvedSidebarLayout {
if !self.open {
return ResolvedSidebarLayout::Hidden;
}
let side_by_side = ResolvedSidebarLayout::SideBySide {
sidebar_left: self.position.is_left(),
};
match self.orientation {
SidebarOrientation::SideBySide => side_by_side,
SidebarOrientation::Stacked => ResolvedSidebarLayout::Stacked,
SidebarOrientation::Auto => {
if width >= SIDEBAR_MIN_WIDTH {
side_by_side
} else {
ResolvedSidebarLayout::Stacked
}
}
}
}
pub fn cycle_orientation(&mut self) {
self.orientation = self.orientation.next();
}
pub fn toggle_position(&mut self) {
self.position = match self.position {
SidebarPosition::Left => SidebarPosition::Right,
SidebarPosition::Right => SidebarPosition::Left,
};
}
pub fn cycle_mode(&mut self) {
self.mode = match self.mode {
SidebarMode::Commits => SidebarMode::Stashes,
SidebarMode::Stashes => SidebarMode::Commits,
};
self.scroll = 0;
self.wt_scroll = 0;
self.cache = None;
}
pub fn on_navigation(&mut self) {
self.scroll = 0;
self.wt_scroll = 0;
self.cache = None;
}
pub fn invalidate(&mut self) {
self.cache = None;
}
pub fn scroll_down(&mut self) {
self.scroll = self.scroll.saturating_add(1).min(self.max_scroll);
}
pub fn scroll_up(&mut self) {
self.scroll = self.scroll.saturating_sub(1);
}
pub fn wt_scroll_down(&mut self) {
self.wt_scroll = self.wt_scroll.saturating_add(1).min(self.wt_max_scroll);
}
pub fn wt_scroll_up(&mut self) {
self.wt_scroll = self.wt_scroll.saturating_sub(1);
}
pub fn toggle_open(&mut self) {
self.open = !self.open;
if !self.open {
self.focused = false;
}
}
pub fn toggle_focus(&mut self) {
if !self.open {
return;
}
self.focused = !self.focused;
}
pub fn focus_table(&mut self) {
self.focused = false;
}
pub fn focus_panel(&mut self) {
self.open = true;
self.focused = true;
}
}