cursive-extras 0.13.4

Extra views for the Cursive TUI library as well some helper functions and macros
Documentation
use super::{TabIter, TabIterMut};
use std::cmp::min;
use cursive_core::{
    Printer, Vec2, View, Rect,
    views::BoxedView,
    direction::Direction,
    event::{AnyCb, Event, EventResult},
    view::{CannotFocus, Selector, ViewNotFound}
};

/// The internal view used by the tabbed views
///
/// This is a lightweight clone of the `TabView` view in `cursive-tabs` that uses numeric
/// IDs instead of string IDs
pub struct TabContainer {
    tabs: Vec<(usize, BoxedView)>,
    focused_id: Option<usize>
}

impl TabContainer {
    /// Create a new `TabContainer`
    #[must_use]
    pub fn new() -> TabContainer {
        TabContainer {
            tabs: vec![],
            focused_id: None
        }
    }

    /// The numerical ID of the current view being shown
    pub fn cur_id(&self) -> Option<usize> { self.focused_id }

    /// The current view being shown (the 'focused' view)
    pub fn cur_view(&self) -> Option<&BoxedView> {
        if let Some(focused) = self.focused_id {
            self.get_view(focused)
        }
        else { None }
    }

    /// Mutable version of `cur_view()`
    pub fn cur_view_mut(&mut self) -> Option<&mut BoxedView> {
        if let Some(focused) = self.focused_id {
            self.get_view_mut(focused)
        }
        else { None }
    }

    /// Get a view by ID
    pub fn get_view(&self, id: usize) -> Option<&BoxedView> {
        for (view_id, view) in &self.tabs {
            if id == *view_id { return Some(view); }
        }
        None
    }

    /// Mutable version of `get_view()`
    pub fn get_view_mut(&mut self, id: usize) -> Option<&mut BoxedView> {
        for (view_id, view) in &mut self.tabs {
            if id == *view_id { return Some(view); }
        }
        None
    }

    /// Iterate over all the views and their IDs in this `TabContainer`
    pub fn views(&self) -> TabIter { self.tabs.iter() }

    //TODO: create special type that makes tab IDs immutable?
    /// Mutable version of `views()`
    ///
    /// Do NOT change the view IDs!
    pub fn views_mut(&mut self) -> TabIterMut { self.tabs.iter_mut() }

    /// Return all the tab IDs from least to greatest
    ///
    /// Since `usize` is `Copy`, this returns the ids as a vector
    pub fn ids(&self) -> Vec<usize> {
        let mut ids = self.views().map(|(id, _)| *id).collect::<Vec<_>>();
        ids.sort_unstable();
        ids
    }

    /// Set the current tab by ID
    pub fn set_cur_tab(&mut self, id: usize) {
        if self.get_view(id).is_some() {
            self.focused_id = Some(id)
        }
    }

    /// Add a new view tab and returns the ID of the new tab
    pub fn add_tab<V: View>(&mut self, view: V) -> usize {
        if self.tabs.is_empty() {
            self.tabs.push((0, BoxedView::boxed(view)));
            0
        }
        else {
            let new_id = self.ids().last().unwrap() + 1;
            self.tabs.push((new_id, BoxedView::boxed(view)));
            new_id
        }
    }

    /// Add a new view tab
    ///
    /// Chainable version
    #[must_use]
    pub fn tab<V: View>(mut self, view: V) -> TabContainer {
        self.add_tab(view);
        self.set_cur_tab(0);
        self
    }

    /// Delete tab by ID
    ///
    /// Does nothing if the tab ID does not exist
    pub fn remove_tab(&mut self, id: usize) {
        self.tabs.retain(|(v_id, _)| *v_id != id);
        if self.tabs.is_empty() {
            self.focused_id = None;
        }
        else {
            let last_id = self.tabs.last().unwrap().0;
            self.set_cur_tab(last_id);
        }
    }

    /// Swap 2 tabs
    ///
    /// Does nothing if the IDs don't exist
    pub fn swap_tabs(&mut self, id_1: usize, id_2: usize) {
        if let Some(idx_1) = self.find_tab_idx(id_1) {
            if let Some(idx_2) = self.find_tab_idx(id_2) {
                self.tabs.swap(idx_1, idx_2);
            }
        }
    }

    /// Go to the next tab
    pub fn next(&mut self) {
        if let Some(focused) = self.focused_id {
            let idx = self.find_tab_idx(focused).unwrap();
            let mut new_idx = 0;
            if idx < self.tabs.len() - 1 {
                new_idx = idx + 1;
            }

            self.set_cur_tab(self.tabs[new_idx].0);
        }
    }

    /// Go to the previous tab
    pub fn prev(&mut self) {
        if let Some(focused) = self.focused_id {
            let idx = self.find_tab_idx(focused).unwrap();
            let mut new_idx = self.tabs.len() - 1;
            if idx > 0 {
                new_idx = idx - 1;
            }

            self.set_cur_tab(self.tabs[new_idx].0);
        }
    }

    /// Set the specified ID as the first tab
    pub fn set_as_first(&mut self, id: usize) {
        if let Some(idx) = self.find_tab_idx(id) {
            let tab = self.tabs.remove(idx);
            self.tabs.insert(0, tab);
        }
    }

    /// Set the specified ID as the last tab
    pub fn set_as_last(&mut self, id: usize) {
        if let Some(idx) = self.find_tab_idx(id) {
            let tab = self.tabs.remove(idx);
            self.tabs.push(tab);
        }
    }

    // find the index of a tab by its ID
    fn find_tab_idx(&self, id: usize) -> Option<usize> {
        for (i, (v_id, _)) in self.views().enumerate() {
            if id == *v_id { return Some(i) }
        }
        None
    }
}

impl View for TabContainer {
    fn draw(&self, printer: &Printer) {
        if let Some(view) = self.cur_view() {
            view.draw(printer)
        }
    }

    fn layout(&mut self, size: Vec2) {
        if let Some(view) = self.cur_view_mut() {
            view.layout(size);
        }
    }

    fn required_size(&mut self, constraint: Vec2) -> Vec2 {
        if let Some(view) = self.cur_view_mut() {
            let tab_size = view.required_size(constraint);
            Vec2::from((min(tab_size.x, constraint.x), min(tab_size.y, constraint.y)))
        }
        else { Vec2::from((1, 1)) }
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        if let Some(view) = self.cur_view_mut() { view.on_event(event) }
        else { EventResult::Ignored }
    }

    fn take_focus(&mut self, dir: Direction) -> Result<EventResult, CannotFocus> {
        if let Some(view) = self.cur_view_mut() { view.take_focus(dir) }
        else { Err(CannotFocus) }
    }

    fn call_on_any(&mut self, sel: &Selector, cb: AnyCb) {
        for (_, view) in self.views_mut() {
            view.call_on_any(sel, cb);
        }
    }

    fn focus_view(&mut self, sel: &Selector) -> Result<EventResult, ViewNotFound> {
        if let Some(view) = self.cur_view_mut() { view.focus_view(sel) }
        else { Err(ViewNotFound) }
    }

    fn needs_relayout(&self) -> bool {
        if let Some(view) = self.cur_view() { view.needs_relayout() }
        else { true }
    }

    fn important_area(&self, size: Vec2) -> Rect {
        if let Some(view) = self.cur_view() { view.important_area(size) }
        else { Rect::from_point((1, 1)) }
    }
}

impl Default for TabContainer {
    fn default() -> Self { Self::new() }
}