cursive_extras/views/tabs/
container.rs

1use super::{TabIter, TabIterMut};
2use std::cmp::min;
3use cursive_core::{
4    Printer, Vec2, View, Rect,
5    views::BoxedView,
6    direction::Direction,
7    event::{AnyCb, Event, EventResult},
8    view::{CannotFocus, Selector, ViewNotFound}
9};
10
11/// The internal view used by the tabbed views
12///
13/// This is a lightweight clone of the `TabView` view in `cursive-tabs` that uses numeric
14/// IDs instead of string IDs
15pub struct TabContainer {
16    tabs: Vec<(usize, BoxedView)>,
17    focused_id: Option<usize>
18}
19
20impl TabContainer {
21    /// Create a new `TabContainer`
22    #[must_use]
23    pub fn new() -> TabContainer {
24        TabContainer {
25            tabs: vec![],
26            focused_id: None
27        }
28    }
29
30    /// The numerical ID of the current view being shown
31    pub fn cur_id(&self) -> Option<usize> { self.focused_id }
32
33    /// The current view being shown (the 'focused' view)
34    pub fn cur_view(&self) -> Option<&BoxedView> {
35        if let Some(focused) = self.focused_id {
36            self.get_view(focused)
37        }
38        else { None }
39    }
40
41    /// Mutable version of `cur_view()`
42    pub fn cur_view_mut(&mut self) -> Option<&mut BoxedView> {
43        if let Some(focused) = self.focused_id {
44            self.get_view_mut(focused)
45        }
46        else { None }
47    }
48
49    /// Get a view by ID
50    pub fn get_view(&self, id: usize) -> Option<&BoxedView> {
51        for (view_id, view) in &self.tabs {
52            if id == *view_id { return Some(view); }
53        }
54        None
55    }
56
57    /// Mutable version of `get_view()`
58    pub fn get_view_mut(&mut self, id: usize) -> Option<&mut BoxedView> {
59        for (view_id, view) in &mut self.tabs {
60            if id == *view_id { return Some(view); }
61        }
62        None
63    }
64
65    /// Iterate over all the views and their IDs in this `TabContainer`
66    pub fn views(&self) -> TabIter { self.tabs.iter() }
67
68    //TODO: create special type that makes tab IDs immutable?
69    /// Mutable version of `views()`
70    ///
71    /// Do NOT change the view IDs!
72    pub fn views_mut(&mut self) -> TabIterMut { self.tabs.iter_mut() }
73
74    /// Return all the tab IDs from least to greatest
75    ///
76    /// Since `usize` is `Copy`, this returns the ids as a vector
77    pub fn ids(&self) -> Vec<usize> {
78        let mut ids = self.views().map(|(id, _)| *id).collect::<Vec<_>>();
79        ids.sort_unstable();
80        ids
81    }
82
83    /// Set the current tab by ID
84    pub fn set_cur_tab(&mut self, id: usize) {
85        if self.get_view(id).is_some() {
86            self.focused_id = Some(id)
87        }
88    }
89
90    /// Add a new view tab and returns the ID of the new tab
91    pub fn add_tab<V: View>(&mut self, view: V) -> usize {
92        if self.tabs.is_empty() {
93            self.tabs.push((0, BoxedView::boxed(view)));
94            0
95        }
96        else {
97            let new_id = self.ids().last().unwrap() + 1;
98            self.tabs.push((new_id, BoxedView::boxed(view)));
99            new_id
100        }
101    }
102
103    /// Add a new view tab
104    ///
105    /// Chainable version
106    #[must_use]
107    pub fn tab<V: View>(mut self, view: V) -> TabContainer {
108        self.add_tab(view);
109        self.set_cur_tab(0);
110        self
111    }
112
113    /// Delete tab by ID
114    ///
115    /// Does nothing if the tab ID does not exist
116    pub fn remove_tab(&mut self, id: usize) {
117        self.tabs.retain(|(v_id, _)| *v_id != id);
118        if self.tabs.is_empty() {
119            self.focused_id = None;
120        }
121        else {
122            let last_id = self.tabs.last().unwrap().0;
123            self.set_cur_tab(last_id);
124        }
125    }
126
127    /// Swap 2 tabs
128    ///
129    /// Does nothing if the IDs don't exist
130    pub fn swap_tabs(&mut self, id_1: usize, id_2: usize) {
131        if let Some(idx_1) = self.find_tab_idx(id_1) {
132            if let Some(idx_2) = self.find_tab_idx(id_2) {
133                self.tabs.swap(idx_1, idx_2);
134            }
135        }
136    }
137
138    /// Go to the next tab
139    pub fn next(&mut self) {
140        if let Some(focused) = self.focused_id {
141            let idx = self.find_tab_idx(focused).unwrap();
142            let mut new_idx = 0;
143            if idx < self.tabs.len() - 1 {
144                new_idx = idx + 1;
145            }
146
147            self.set_cur_tab(self.tabs[new_idx].0);
148        }
149    }
150
151    /// Go to the previous tab
152    pub fn prev(&mut self) {
153        if let Some(focused) = self.focused_id {
154            let idx = self.find_tab_idx(focused).unwrap();
155            let mut new_idx = self.tabs.len() - 1;
156            if idx > 0 {
157                new_idx = idx - 1;
158            }
159
160            self.set_cur_tab(self.tabs[new_idx].0);
161        }
162    }
163
164    /// Set the specified ID as the first tab
165    pub fn set_as_first(&mut self, id: usize) {
166        if let Some(idx) = self.find_tab_idx(id) {
167            let tab = self.tabs.remove(idx);
168            self.tabs.insert(0, tab);
169        }
170    }
171
172    /// Set the specified ID as the last tab
173    pub fn set_as_last(&mut self, id: usize) {
174        if let Some(idx) = self.find_tab_idx(id) {
175            let tab = self.tabs.remove(idx);
176            self.tabs.push(tab);
177        }
178    }
179
180    // find the index of a tab by its ID
181    fn find_tab_idx(&self, id: usize) -> Option<usize> {
182        for (i, (v_id, _)) in self.views().enumerate() {
183            if id == *v_id { return Some(i) }
184        }
185        None
186    }
187}
188
189impl View for TabContainer {
190    fn draw(&self, printer: &Printer) {
191        if let Some(view) = self.cur_view() {
192            view.draw(printer)
193        }
194    }
195
196    fn layout(&mut self, size: Vec2) {
197        if let Some(view) = self.cur_view_mut() {
198            view.layout(size);
199        }
200    }
201
202    fn required_size(&mut self, constraint: Vec2) -> Vec2 {
203        if let Some(view) = self.cur_view_mut() {
204            let tab_size = view.required_size(constraint);
205            Vec2::from((min(tab_size.x, constraint.x), min(tab_size.y, constraint.y)))
206        }
207        else { Vec2::from((1, 1)) }
208    }
209
210    fn on_event(&mut self, event: Event) -> EventResult {
211        if let Some(view) = self.cur_view_mut() { view.on_event(event) }
212        else { EventResult::Ignored }
213    }
214
215    fn take_focus(&mut self, dir: Direction) -> Result<EventResult, CannotFocus> {
216        if let Some(view) = self.cur_view_mut() { view.take_focus(dir) }
217        else { Err(CannotFocus) }
218    }
219
220    fn call_on_any(&mut self, sel: &Selector, cb: AnyCb) {
221        for (_, view) in self.views_mut() {
222            view.call_on_any(sel, cb);
223        }
224    }
225
226    fn focus_view(&mut self, sel: &Selector) -> Result<EventResult, ViewNotFound> {
227        if let Some(view) = self.cur_view_mut() { view.focus_view(sel) }
228        else { Err(ViewNotFound) }
229    }
230
231    fn needs_relayout(&self) -> bool {
232        if let Some(view) = self.cur_view() { view.needs_relayout() }
233        else { true }
234    }
235
236    fn important_area(&self, size: Vec2) -> Rect {
237        if let Some(view) = self.cur_view() { view.important_area(size) }
238        else { Rect::from_point((1, 1)) }
239    }
240}
241
242impl Default for TabContainer {
243    fn default() -> Self { Self::new() }
244}