git-igitt 0.1.21

Interactive Git terminal application to browse and visualize Git history graphs arranged for your branching model
Documentation
use tui::style::Style;
use tui::text::Text;

#[derive(Debug, Clone, Default)]
pub struct ListState {
    pub offset: usize,
    pub selected: Option<usize>,
    pub scroll_x: u16,
}

impl ListState {
    pub fn selected(&self) -> Option<usize> {
        self.selected
    }

    pub fn select(&mut self, index: Option<usize>) {
        self.selected = index;
        if index.is_none() {
            self.offset = 0;
        }
    }
}

pub trait ListItem {
    fn is_selectable(&self) -> bool;
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DefaultListItem<'a> {
    pub content: Text<'a>,
    pub style: Style,
}

impl<'a> DefaultListItem<'a> {
    pub fn new<T>(content: T) -> DefaultListItem<'a>
    where
        T: Into<Text<'a>>,
    {
        DefaultListItem {
            content: content.into(),
            style: Style::default(),
        }
    }

    pub fn style(mut self, style: Style) -> DefaultListItem<'a> {
        self.style = style;
        self
    }

    pub fn height(&self) -> usize {
        self.content.height()
    }
}

pub struct StatefulList<T> {
    pub state: ListState,
    pub items: Vec<T>,
}

impl<T: ListItem> Default for StatefulList<T> {
    fn default() -> StatefulList<T> {
        StatefulList {
            state: ListState::default(),
            items: Vec::new(),
        }
    }
}

impl<T: ListItem> StatefulList<T> {
    pub fn with_items(items: Vec<T>) -> StatefulList<T> {
        StatefulList {
            state: ListState::default(),
            items,
        }
    }

    pub fn fwd(&mut self, steps: usize) -> bool {
        match self.state.selected() {
            Some(sel) => {
                for _ in 0..steps {
                    if !self.next() {
                        break;
                    }
                }
                if let Some(new_sel) = self.state.selected() {
                    sel != new_sel
                } else {
                    true
                }
            }
            None => self.next(),
        }
    }

    pub fn bwd(&mut self, steps: usize) -> bool {
        match self.state.selected() {
            Some(sel) => {
                for _ in 0..steps {
                    if !self.previous() {
                        break;
                    }
                }
                if let Some(new_sel) = self.state.selected() {
                    sel != new_sel
                } else {
                    true
                }
            }
            None => self.next(),
        }
    }

    fn next(&mut self) -> bool {
        match self.state.selected() {
            Some(i) => {
                if i < self.items.len() - 1 {
                    for (idx, item) in self.items.iter().enumerate().skip(i + 1) {
                        if item.is_selectable() {
                            self.state.select(Some(idx));
                            return true;
                        }
                    }
                }
            }
            None => {
                for (idx, item) in self.items.iter().enumerate() {
                    if item.is_selectable() {
                        self.state.select(Some(idx));
                        return true;
                    }
                }
            }
        };
        false
    }

    fn previous(&mut self) -> bool {
        match self.state.selected() {
            Some(i) => {
                if i > 0 {
                    for (idx, item) in self
                        .items
                        .iter()
                        .enumerate()
                        .rev()
                        .skip(self.items.len() - i)
                    {
                        if item.is_selectable() {
                            self.state.select(Some(idx));
                            return true;
                        }
                    }
                }
            }
            None => {
                for (idx, item) in self.items.iter().enumerate() {
                    if item.is_selectable() {
                        self.state.select(Some(idx));
                        return true;
                    }
                }
            }
        };
        false
    }

    pub fn unselect(&mut self) {
        self.state.select(None);
    }
}