use cursive_core::{
theme::{Color, ColorStyle, BaseColor},
views::{DummyView, ResizedView},
View,
Printer,
Vec2,
utils::markup::StyledString
};
use rust_utils::{encapsulated, new_default};
use std::{
time::Instant,
fmt::Display
};
mod loading;
pub use loading::LoadingAnimation;
mod lazy_view;
pub use lazy_view::LazyView;
#[cfg(feature = "image_view")]
mod image_view;
#[cfg(feature = "image_view")]
pub use image_view::ImageView;
#[cfg(feature = "tabs")]
mod tabs;
#[cfg(feature = "tabs")]
pub use tabs::*;
#[cfg(feature = "advanced")]
mod advanced;
#[cfg(feature = "advanced")]
pub use advanced::*;
#[cfg(feature = "log_view")]
mod log_view;
#[cfg(feature = "log_view")]
pub use log_view::*;
use crate::SpannedStrExt;
pub type Spacer = ResizedView<DummyView>;
#[derive(Copy, Clone, Eq, PartialEq)]
enum DividerSize {
Free,
Fixed(usize)
}
#[derive(Copy, Clone)]
pub struct HDivider(DividerSize, usize);
impl HDivider {
pub fn new() -> HDivider { HDivider(DividerSize::Free, 0) }
pub fn fixed(height: usize) -> HDivider {
HDivider(DividerSize::Fixed(height), height)
}
pub fn height(&self) -> usize {
self.1
}
}
impl View for HDivider {
fn draw(&self, printer: &Printer) {
let height = if let DividerSize::Fixed(height) = self.0 {
height
}
else { printer.size.y };
printer.with_high_border(false, |printer| printer.print_vline((0, 0), height, "│"));
}
fn required_size(&mut self, _bound: Vec2) -> Vec2 {
if let DividerSize::Fixed(height) = self.0 {
Vec2::new(1, height)
}
else {
Vec2::new(1, 1)
}
}
fn layout(&mut self, size: Vec2) {
if self.0 == DividerSize::Free {
self.1 = size.y;
}
}
}
new_default!(HDivider);
#[derive(Copy, Clone)]
pub struct VDivider(DividerSize, usize);
impl VDivider {
pub fn new() -> VDivider { VDivider(DividerSize::Free, 0) }
pub fn fixed(width: usize) -> VDivider {
VDivider(DividerSize::Fixed(width), width)
}
pub fn width(&self) -> usize {
self.1
}
}
impl View for VDivider {
fn draw(&self, printer: &Printer) {
let width = if let DividerSize::Fixed(width) = self.0 {
width
}
else { printer.size.x };
printer.with_high_border(false, |printer| printer.print_hline((0, 0), width, "─"));
}
fn required_size(&mut self, _bound: Vec2) -> Vec2 {
if let DividerSize::Fixed(width) = self.0 {
Vec2::new(width, 1)
}
else {
Vec2::new(1, 1)
}
}
fn layout(&mut self, size: Vec2) {
if self.0 == DividerSize::Free {
self.1 = size.x;
}
}
}
new_default!(VDivider);
#[derive(Clone)]
#[encapsulated]
pub struct StatusView {
#[setter(use_into_impl, doc = "Set the label of the [`StatusView`]")]
#[chainable(use_into_impl)]
label: StyledString,
cur_msg: StyledString,
time: Instant,
error: bool
}
impl StatusView {
pub fn new() -> StatusView {
StatusView {
label: StyledString::new(),
cur_msg: StyledString::new(),
time: Instant::now(),
error: false
}
}
pub fn report_error<T: Display>(&mut self, text: T) {
let err_style = ColorStyle::from(Color::Light(BaseColor::Red));
self.cur_msg = StyledString::styled(text.to_string(), err_style);
self.time = Instant::now();
self.error = true;
}
pub fn info<T: Into<StyledString>>(&mut self, text: T) {
self.cur_msg = text.into();
self.time = Instant::now();
}
}
impl Default for StatusView {
fn default() -> StatusView { StatusView::new() }
}
impl View for StatusView {
fn draw(&self, printer: &Printer) {
if printer.size.x == 0 && printer.size.y == 0 {
return;
}
if self.cur_msg.is_empty() {
if !self.label.is_empty() {
printer.print_styled((0, 0), self.label.as_spanned_str());
}
}
else {
printer.print_styled((0, 0), self.cur_msg.as_spanned_str());
}
}
fn required_size(&mut self, bound: Vec2) -> Vec2 {
let y = (!self.cur_msg.is_empty() || !self.label.is_empty()) as usize;
Vec2::new(bound.x, y)
}
fn layout(&mut self, _: Vec2) {
if self.time.elapsed().as_secs() >= 5 {
self.cur_msg = StyledString::new();
self.time = Instant::now();
self.error = false;
}
}
}