use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Panel {
Status,
Commits,
Branches,
Stashes,
Details,
Help,
Input,
}
impl Panel {
pub fn name(&self) -> &'static str {
match self {
Panel::Status => "Status",
Panel::Commits => "Commits",
Panel::Branches => "Branches",
Panel::Stashes => "Stashes",
Panel::Details => "Details",
Panel::Help => "Help",
Panel::Input => "Input",
}
}
pub fn shortcut(&self) -> char {
match self {
Panel::Status => '1',
Panel::Commits => '2',
Panel::Branches => '3',
Panel::Stashes => '4',
Panel::Details => '5',
Panel::Help => '?',
Panel::Input => 'i',
}
}
}
pub struct FocusManager {
current: Panel,
history: Vec<Panel>,
max_history: usize,
visible: HashMap<Panel, bool>,
}
impl FocusManager {
pub fn new(initial: Panel) -> Self {
let mut visible = HashMap::new();
visible.insert(Panel::Status, true);
visible.insert(Panel::Commits, true);
visible.insert(Panel::Branches, true);
visible.insert(Panel::Stashes, true);
visible.insert(Panel::Details, true);
Self {
current: initial,
history: Vec::new(),
max_history: 10,
visible,
}
}
pub fn current(&self) -> Panel {
self.current
}
pub fn focus(&mut self, panel: Panel) {
if panel != self.current && self.is_visible(panel) {
if self.history.len() >= self.max_history {
self.history.remove(0);
}
self.history.push(self.current);
self.current = panel;
}
}
pub fn focus_next(&mut self) {
let panels = [Panel::Status, Panel::Commits, Panel::Branches, Panel::Stashes];
let current_idx = panels.iter().position(|&p| p == self.current).unwrap_or(0);
for i in 1..=panels.len() {
let next_idx = (current_idx + i) % panels.len();
if self.is_visible(panels[next_idx]) {
self.focus(panels[next_idx]);
return;
}
}
}
pub fn focus_prev(&mut self) {
let panels = [Panel::Status, Panel::Commits, Panel::Branches, Panel::Stashes];
let current_idx = panels.iter().position(|&p| p == self.current).unwrap_or(0);
for i in 1..=panels.len() {
let prev_idx = (current_idx + panels.len() - i) % panels.len();
if self.is_visible(panels[prev_idx]) {
self.focus(panels[prev_idx]);
return;
}
}
}
pub fn focus_back(&mut self) -> bool {
if let Some(prev) = self.history.pop() {
self.current = prev;
true
} else {
false
}
}
pub fn is_visible(&self, panel: Panel) -> bool {
*self.visible.get(&panel).unwrap_or(&true)
}
pub fn set_visible(&mut self, panel: Panel, visible: bool) {
self.visible.insert(panel, visible);
if !visible && self.current == panel {
self.focus_next();
}
}
pub fn is_focused(&self, panel: Panel) -> bool {
self.current == panel
}
}
impl Default for FocusManager {
fn default() -> Self {
Self::new(Panel::Status)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_focus_cycle() {
let mut fm = FocusManager::default();
assert_eq!(fm.current(), Panel::Status);
fm.focus_next();
assert_eq!(fm.current(), Panel::Commits);
fm.focus_next();
assert_eq!(fm.current(), Panel::Branches);
}
#[test]
fn test_focus_back() {
let mut fm = FocusManager::default();
fm.focus(Panel::Commits);
fm.focus(Panel::Branches);
assert!(fm.focus_back());
assert_eq!(fm.current(), Panel::Commits);
}
}