use std::slice::{Iter, IterMut};
use cursive_core::{
Cursive, Printer, Vec2, Rect,
event::Callback,
align::HAlign,
theme::ColorStyle,
utils::markup::StyledString,
views::BoxedView
};
use unicode_width::UnicodeWidthStr;
use crate::SpannedStrExt;
mod container;
mod dialog;
mod layer;
pub use self::{
container::TabContainer,
dialog::TabDialog,
layer::TabLayer
};
pub type TabIter<'a> = Iter<'a, (usize, BoxedView)>;
pub type TabIterMut<'a> = IterMut<'a, (usize, BoxedView)>;
#[derive(Copy, Clone, PartialEq, Eq)]
enum TabFocus {
TitleBar(usize),
Content,
Buttons(usize)
}
enum ButtonAction {
Close, Next, Prev, CallBack(Callback) }
impl<F: Fn(&mut Cursive) + 'static> From<F> for ButtonAction {
fn from(func: F) -> Self { ButtonAction::CallBack(Callback::from_fn(func)) }
}
#[derive(Clone)]
struct TitleButton {
label: String,
rect: Rect,
b_type: TitleButtonType
}
impl TitleButton {
fn new(label: &str, id: usize) -> TitleButton {
TitleButton {
label: format!(" {label} "),
rect: Rect::from_size((1, 0), (label.width(), 1)),
b_type: TitleButtonType::Tab(id)
}
}
fn overflow() -> TitleButton {
TitleButton {
label: String::from("..."),
rect: Rect::from_size((1, 0), (3, 1)),
b_type: TitleButtonType::Overflow
}
}
fn set_label(&mut self, text: &str) {
self.label = format!(" {text} ");
self.rect = Rect::from_size((1, 0), (text.width(), 1));
}
fn width(&self) -> usize { self.label.width() + 1 }
fn has_mouse_pos(&self, pos: Vec2) -> bool { self.rect.contains(pos) }
fn offset(&mut self, offset: usize) { self.rect.offset((offset, 0)); }
fn draw(&self, printer: &Printer, selected: bool, focused: bool, first: bool, last: bool) {
let loc = self.rect.top_left() - (1, 0);
let fc = if first && focused { "┨" }
else if first { "┤" }
else if focused { "┃" }
else { "│" };
let lc = if last && focused { "┠" }
else if last { "├" }
else if focused { "┃" }
else { "│" };
printer.with_style(ColorStyle::primary(), |printer| {
printer.print(loc, fc);
printer.print(loc + (self.label.width() + 1, 0), lc);
});
let title_style =
if selected{ ColorStyle::highlight() }
else if focused { ColorStyle::title_primary() }
else { ColorStyle::title_secondary() };
printer.print_styled(loc + (1, 0), StyledString::styled(&self.label, title_style).as_spanned_str());
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
enum TitleButtonType {
Tab(usize),
Overflow
}
struct ViewButton {
text: String,
action: ButtonAction,
rect: Rect
}
impl ViewButton {
fn new(text: &str, action: ButtonAction) -> ViewButton {
ViewButton {
text: text.to_string(),
action,
rect: Rect::from_size((0, 0), (text.width() + 2, 1))
}
}
fn from_fn<F: Fn(&mut Cursive) + 'static>(text: &str, func: F) -> ViewButton {
Self::new(text, ButtonAction::from(func))
}
fn draw(&self, printer: &Printer, selected: bool) {
let style = if selected { ColorStyle::highlight() }
else { ColorStyle::primary() };
let text = StyledString::styled(format!("<{}>", self.text), style);
printer.print_styled(self.rect.top_left(), text.as_spanned_str());
}
fn width(&self) -> usize { self.text.chars().count() + 3 }
fn has_mouse_pos(&self, pos: Vec2) -> bool { self.rect.contains(pos) }
}
fn align_title_buttons(shown_buttons: &mut Vec<TitleButton>, buttons: &[TitleButton], align: HAlign, width: usize, dialog: bool) {
if !buttons.is_empty() {
shown_buttons.clear();
let mut buttons_len = 0;
for button in buttons.iter() {
shown_buttons.push(button.clone());
buttons_len += button.width();
}
if buttons_len > width {
shown_buttons.clear();
buttons_len = 0;
for (i, button) in buttons.iter().enumerate() {
if i >= 3 {
shown_buttons.push(TitleButton::overflow());
buttons_len += 4;
break;
}
else {
shown_buttons.push(button.clone());
buttons_len += button.width();
}
}
}
let offset = match align {
HAlign::Left => if dialog { 1 } else { 0 },
HAlign::Center => (width - buttons_len) / 2 ,
HAlign::Right => width - buttons_len - if dialog { 0 } else { 1 }
};
let mut cur_len = 0;
for button in shown_buttons.iter_mut() {
button.offset(offset + cur_len);
cur_len += button.width();
}
}
}
fn align_buttons(buttons: &mut [ViewButton], align: HAlign, size: Vec2, dialog: bool) {
if !buttons.is_empty() {
let mut buttons_len = 0;
for button in buttons.iter() {
buttons_len += button.width();
}
let offset = match align {
HAlign::Left => 1,
HAlign::Center => (size.x - buttons_len) / 2 ,
HAlign::Right => size.x + if dialog { 0 } else { 1 } - buttons_len
};
let mut cur_len = 0;
for button in buttons.iter_mut() {
let loc = (offset + cur_len, size.y - if dialog { 2 } else { 1 });
let rsize = button.rect.size();
button.rect = Rect::from_size(loc, rsize);
cur_len += button.width();
}
}
}