use crossbeam::{Receiver, Sender};
use cursive::direction::Direction;
use cursive::event::{AnyCb, Event, EventResult};
use cursive::view::{Selector, View};
use cursive::{Printer, Rect, Vec2};
use log::debug;
use std::collections::HashMap;
use std::hash::Hash;
mod bar;
mod panel;
use bar::{Bar, TabBar};
pub use panel::TabPanel;
pub struct TabView<K: Hash> {
current_id: Option<K>,
map: HashMap<K, Box<dyn View + Send>>,
key_order: Vec<K>,
bar_rx: Option<Receiver<K>>,
active_key_tx: Option<Sender<K>>,
}
impl<K: Hash + Eq + Copy + 'static> TabView<K> {
pub fn new() -> Self {
Self {
current_id: None,
map: HashMap::new(),
key_order: Vec::new(),
bar_rx: None,
active_key_tx: None,
}
}
pub fn active_tab(&self) -> Option<K> {
self.current_id
}
pub fn set_active_tab(&mut self, id: K) -> Result<(), ()> {
if self.map.contains_key(&id) {
if let Some(sender) = &self.active_key_tx {
match sender.send(id) {
Ok(_) => {}
Err(e) => debug!(
"error occured while trying to send new active key to sender: {}",
e
),
}
}
self.current_id = Some(id);
Ok(())
} else {
Err(())
}
}
pub fn with_active_tab(mut self, id: K) -> Result<Self, ()> {
self.set_active_tab(id)?;
Ok(self)
}
pub fn add_tab<T: View + Send>(&mut self, id: K, view: T) {
self.map.insert(id, Box::new(view));
self.current_id = Some(id);
self.key_order.push(id);
}
pub fn with_tab<T: View + Send>(mut self, id: K, view: T) -> Self {
self.add_tab(id, view);
self
}
pub fn remove_tab(&mut self, id: K) -> Result<(), ()> {
if let Some(_) = self.map.remove(&id) {
if let Some(key) = &self.current_id {
if *key == id {
self.current_id = None;
}
}
self.key_order = self
.key_order
.iter()
.filter_map(|key| if id == *key { None } else { Some(*key) })
.collect();
Ok(())
} else {
Err(())
}
}
pub fn tab_order(&self) -> Vec<K> {
self.key_order.clone()
}
fn index_key(cur_key: &K, key_order: &Vec<K>) -> usize {
let mut count: usize = 0;
for key in key_order {
if *key != *cur_key {
count += 1;
} else {
break;
}
}
count
}
pub fn next(&mut self) {
if let Some(cur_key) = self.current_id {
self.set_active_tab(
self.key_order
[(Self::index_key(&cur_key, &self.key_order) + 1) % self.key_order.len()],
)
.expect("Key content changed during operation, this should not happen");
}
}
pub fn prev(&mut self) {
if let Some(cur_key) = self.current_id {
self.set_active_tab(
self.key_order[(self.key_order.len() + Self::index_key(&cur_key, &self.key_order)
- 1)
% self.key_order.len()],
)
.expect("Key content changed during operation, this should not happen");
}
}
}
impl<K: Hash + Eq + Copy + 'static> View for TabView<K> {
fn draw(&self, printer: &Printer) {
if let Some(key) = &self.current_id {
if let Some(view) = self.map.get(&key) {
view.draw(printer);
}
}
}
fn layout(&mut self, size: Vec2) {
if let Some(key) = &self.current_id {
if let Some(view) = self.map.get_mut(&key) {
view.layout(size);
}
}
}
fn required_size(&mut self, req: Vec2) -> Vec2 {
if let Some(rx) = &self.bar_rx {
if let Ok(evt) = rx.try_recv() {
match self.set_active_tab(evt) {
Ok(_) => {}
Err(err) => debug!("could not accept tab bar event: {:?}", err),
}
}
}
if let Some(key) = &self.current_id {
if let Some(view) = self.map.get_mut(&key) {
view.required_size(req)
} else {
(1, 1).into()
}
} else {
(1, 1).into()
}
}
fn on_event(&mut self, evt: Event) -> EventResult {
if let Some(key) = &self.current_id {
if let Some(view) = self.map.get_mut(&key) {
view.on_event(evt)
} else {
EventResult::Ignored
}
} else {
EventResult::Ignored
}
}
fn take_focus(&mut self, src: Direction) -> bool {
if let Some(key) = &self.current_id {
if let Some(view) = self.map.get_mut(&key) {
view.take_focus(src)
} else {
false
}
} else {
false
}
}
fn call_on_any<'a>(&mut self, slt: &Selector, cb: AnyCb<'a>) {
if let Some(key) = &self.current_id {
if let Some(view) = self.map.get_mut(&key) {
view.call_on_any(slt, cb);
}
}
}
fn focus_view(&mut self, slt: &Selector) -> Result<(), ()> {
if let Some(key) = &self.current_id {
if let Some(view) = self.map.get_mut(&key) {
view.focus_view(slt)
} else {
Err(())
}
} else {
Err(())
}
}
fn needs_relayout(&self) -> bool {
true
}
fn important_area(&self, size: Vec2) -> Rect {
if let Some(key) = &self.current_id {
if let Some(view) = self.map.get(&key) {
view.important_area(size)
} else {
Rect::from((1, 1))
}
} else {
Rect::from((1, 1))
}
}
}
#[cfg(test)]
mod test {
use super::TabView;
use cursive::views::DummyView;
#[test]
fn smoke() {
let _ = TabView::<i32>::new();
}
#[test]
fn insert() {
let mut tabs = TabView::<i32>::new().with_tab(0, DummyView);
tabs.add_tab(1, DummyView);
}
#[test]
fn switch() {
let mut tabs = TabView::<i32>::new();
tabs.add_tab(0, DummyView);
tabs.add_tab(1, DummyView);
assert_eq!(tabs.active_tab().expect("Id not correct"), 1);
tabs.set_active_tab(0).expect("Id not taken");
assert_eq!(tabs.active_tab().expect("Id not correct"), 0);
}
#[test]
fn remove() {
let mut tabs = TabView::<i32>::new();
tabs.add_tab(0, DummyView);
tabs.add_tab(1, DummyView);
assert_eq!(tabs.remove_tab(1), Ok(()));
assert!(tabs.active_tab().is_none());
}
}