1extern crate cursive_core as cursive;
40
41use crossbeam::channel::{Receiver, Sender};
42use cursive::direction::Direction;
43use cursive::event::{AnyCb, Event, EventResult};
44use cursive::view::{CannotFocus, Selector, View, ViewNotFound};
45use cursive::views::NamedView;
46use cursive::{Printer, Rect, Vec2};
47use log::debug;
48use std::collections::HashMap;
49
50mod bar;
51mod error;
52mod panel;
53
54use bar::{Bar, TabBar};
56pub use panel::{Align, Placement, TabPanel};
57pub struct TabView {
59 current_id: Option<String>,
60 map: HashMap<String, Box<dyn View>>,
63 key_order: Vec<String>,
64 bar_rx: Option<Receiver<String>>,
65 active_key_tx: Option<Sender<String>>,
66 invalidated: bool,
67}
68
69impl Default for TabView {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl TabView {
76 pub fn new() -> Self {
91 Self {
92 current_id: None,
93 map: HashMap::new(),
94 key_order: Vec::new(),
95 bar_rx: None,
96 active_key_tx: None,
97 invalidated: true,
98 }
99 }
100
101 pub fn active_tab(&self) -> Option<&str> {
103 self.current_id.as_deref()
104 }
105
106 pub fn active_view(&self) -> Option<&dyn View> {
108 self.active_tab()
109 .and_then(|k| self.map.get(k).map(|v| &**v))
110 }
111
112 pub fn active_view_mut(&mut self) -> Option<&mut dyn View> {
114 if let Some(k) = self.current_id.as_ref() {
115 self.map.get_mut(k).map(|v| &mut **v)
116 } else {
117 None
118 }
119 }
120
121 pub fn views(&self) -> Vec<&dyn View> {
122 self.map.values().map(|v| &**v).collect()
123 }
124
125 pub fn views_mut(&mut self) -> Vec<&mut dyn View> {
127 self.map.values_mut().map(|v| &mut **v).collect()
128 }
129
130 pub fn set_active_tab(&mut self, id: &str) -> Result<(), error::IdNotFound> {
133 if self.map.contains_key(id) {
134 if let Some(sender) = &self.active_key_tx {
135 match sender.send(id.to_owned()) {
136 Ok(_) => {}
137 Err(e) => debug!(
138 "error occured while trying to send new active key to sender: {}",
139 e
140 ),
141 }
142 }
143 self.current_id = Some(id.to_owned());
144 self.invalidated = true;
145 Ok(())
146 } else {
147 Err(error::IdNotFound { id: id.to_owned() })
148 }
149 }
150
151 pub fn with_active_tab(mut self, id: &str) -> Result<Self, Self> {
156 match self.set_active_tab(id) {
157 Ok(_) => Ok(self),
158 Err(_) => Err(self),
159 }
160 }
161
162 pub fn add_tab<T: View>(&mut self, view: NamedView<T>) {
165 let id = view.name().to_owned();
166 self.map.insert(id.clone(), Box::new(view));
167 self.key_order.push(id.clone());
168 self.current_id = Some(id);
169 }
170
171 pub fn with_tab<T: View>(mut self, view: NamedView<T>) -> Self {
176 self.add_tab(view);
177 self
178 }
179
180 pub fn add_tab_at<T: View>(&mut self, view: NamedView<T>, pos: usize) {
185 let id = view.name().to_owned();
186 self.map.insert(id.clone(), Box::new(view));
187 if let Some(sender) = &self.active_key_tx {
188 match sender.send(id.clone()) {
189 Ok(_) => {}
190 Err(send_err) => debug!(
191 "Could not send new key to receiver in TabBar, has it been dropped? {}",
192 send_err
193 ),
194 }
195 }
196 self.current_id = Some(id.clone());
197 if self.key_order.len() > pos {
198 self.key_order.insert(pos, id)
199 } else {
200 self.key_order.push(id);
201 }
202 }
203
204 pub fn with_tab_at<T: View>(mut self, view: NamedView<T>, pos: usize) -> Self {
211 self.add_tab_at(view, pos);
212 self
213 }
214
215 pub fn swap_tabs(&mut self, fst: &str, snd: &str) {
218 let mut fst_pos: Option<usize> = None;
219 let mut snd_pos: Option<usize> = None;
220 for (pos, key) in self.tab_order().into_iter().enumerate() {
221 match key {
222 val if val == *fst => fst_pos = Some(pos),
223 val if val == *snd => snd_pos = Some(pos),
224 _ => {}
225 }
226 }
227 if let (Some(fst_pos), Some(snd_pos)) = (fst_pos, snd_pos) {
228 if let Some(cur) = self.current_id.as_ref() {
229 if self.active_key_tx.is_some() && (fst == cur || snd == cur) {
230 self.active_key_tx
231 .as_mut()
232 .unwrap()
233 .send(cur.to_owned())
234 .expect("Sending failed.");
235 }
236 }
237 self.key_order.swap(fst_pos, snd_pos);
238 }
239 }
240
241 pub fn remove_tab(&mut self, id: &str) -> Result<(), error::IdNotFound> {
245 if self.map.remove(id).is_some() {
246 if let Some(key) = &self.current_id {
247 if key == id {
248 self.current_id = None;
250 }
251 }
252 self.key_order.retain(|k| k != id);
254 self.invalidated = true;
255 Ok(())
256 } else {
257 Err(error::IdNotFound { id: id.to_owned() })
258 }
259 }
260
261 pub fn tab_order(&self) -> Vec<String> {
266 self.key_order.clone()
267 }
268
269 fn index_key(cur_key: &str, key_order: &[String]) -> usize {
272 for (idx, key) in key_order.iter().enumerate() {
273 if *key == *cur_key {
274 return idx;
275 }
276 }
277 key_order.len()
278 }
279
280 pub fn next(&mut self) {
282 if let Some(cur_key) = &self.current_id {
283 let idx = (Self::index_key(&cur_key, &self.key_order) + 1) % self.key_order.len();
284
285 let key = &self.key_order[idx].clone();
286 self.set_active_tab(key)
287 .expect("Key content changed during operation, this should not happen");
288 }
289 }
290
291 pub fn prev(&mut self) {
293 if let Some(cur_key) = self.current_id.as_ref().cloned() {
294 let idx_key = Self::index_key(&cur_key, &self.key_order);
295 let idx = (self.key_order.len() + idx_key - 1) % self.key_order.len();
296
297 let key = &self.key_order[idx].clone();
298 self.set_active_tab(key)
299 .expect("Key content changed during operation, this should not happen");
300 }
301 }
302
303 pub fn set_bar_rx(&mut self, rx: Receiver<String>) {
305 self.bar_rx = Some(rx);
306 }
307
308 pub fn set_active_key_tx(&mut self, tx: Sender<String>) {
310 self.active_key_tx = Some(tx);
311 }
312}
313
314impl View for TabView {
315 fn draw(&self, printer: &Printer) {
316 if let Some(key) = &self.current_id {
317 if let Some(view) = self.map.get(key) {
318 view.draw(printer);
319 }
320 }
321 }
322
323 fn layout(&mut self, size: Vec2) {
324 self.invalidated = false;
325 if let Some(key) = &self.current_id {
326 if let Some(view) = self.map.get_mut(key) {
327 view.layout(size);
328 }
329 }
330 }
331
332 fn required_size(&mut self, req: Vec2) -> Vec2 {
333 if let Some(rx) = &self.bar_rx {
334 if let Ok(evt) = rx.try_recv() {
335 match self.set_active_tab(&evt) {
336 Ok(_) => {}
337 Err(err) => debug!("could not accept tab bar event: {:?}", err),
338 }
339 }
340 }
341 if let Some(key) = &self.current_id {
342 if let Some(view) = self.map.get_mut(key) {
343 view.required_size(req)
344 } else {
345 (1, 1).into()
346 }
347 } else {
348 (1, 1).into()
349 }
350 }
351
352 fn on_event(&mut self, evt: Event) -> EventResult {
353 if let Some(key) = &self.current_id {
354 if let Some(view) = self.map.get_mut(key) {
355 view.on_event(evt)
356 } else {
357 EventResult::Ignored
358 }
359 } else {
360 EventResult::Ignored
361 }
362 }
363
364 fn take_focus(&mut self, src: Direction) -> Result<EventResult, CannotFocus> {
365 if let Some(key) = &self.current_id {
366 if let Some(view) = self.map.get_mut(key) {
367 view.take_focus(src)
368 } else {
369 Err(CannotFocus)
370 }
371 } else {
372 Err(CannotFocus)
373 }
374 }
375
376 fn call_on_any<'a>(&mut self, slt: &Selector, cb: AnyCb<'a>) {
377 for (_, view) in self.map.iter_mut() {
378 view.call_on_any(slt, cb);
379 }
380 }
381
382 fn focus_view(&mut self, slt: &Selector) -> Result<EventResult, ViewNotFound> {
383 if let Some(key) = &self.current_id {
384 if let Some(view) = self.map.get_mut(key) {
385 view.focus_view(slt)
386 } else {
387 Err(ViewNotFound)
388 }
389 } else {
390 Err(ViewNotFound)
391 }
392 }
393
394 fn needs_relayout(&self) -> bool {
395 self.invalidated || {
396 if let Some(key) = &self.current_id {
397 if let Some(view) = self.map.get(key) {
398 view.needs_relayout()
399 } else {
400 false
401 }
402 } else {
403 false
404 }
405 }
406 }
407
408 fn important_area(&self, size: Vec2) -> Rect {
409 if let Some(key) = &self.current_id {
410 if let Some(view) = self.map.get(key) {
411 view.important_area(size)
412 } else {
413 Rect::from_point((1, 1))
414 }
415 } else {
416 Rect::from_point((1, 1))
417 }
418 }
419}
420
421#[cfg(test)]
422mod test {
423 use super::TabView;
424 use cursive::{traits::Nameable, views::DummyView};
425
426 #[test]
427 fn smoke() {
428 let _ = TabView::new();
429 }
430
431 #[test]
432 fn insert() {
433 let mut tabs = TabView::new().with_tab(DummyView {}.with_name("0"));
434 tabs.add_tab(DummyView {}.with_name("1"));
435 }
436
437 #[test]
438 fn switch() {
439 let mut tabs = TabView::new();
440 tabs.add_tab(DummyView {}.with_name("0"));
441 tabs.add_tab(DummyView {}.with_name("1"));
442 assert_eq!(tabs.active_tab().expect("Id not correct"), "1");
443 tabs.set_active_tab("0").expect("Id not taken");
444 assert_eq!(tabs.active_tab().expect("Id not correct"), "0");
445 }
446
447 #[test]
448 fn remove() {
449 let mut tabs = TabView::new();
450 tabs.add_tab(DummyView {}.with_name("0"));
451 tabs.add_tab(DummyView {}.with_name("1"));
452 assert_eq!(tabs.remove_tab("1"), Ok(()));
453 assert!(tabs.active_tab().is_none());
454 }
455}