perch/src/view.rs

161 lines
6.6 KiB
Rust

use crate::*;
pub(crate) use tengri::tui::ratatui::style::{Color, Modifier};
pub(crate) use pad::PadStr;
mod table; pub use self::table::*;
impl Content<TuiOut> for Taggart {
fn content (&self) -> impl Render<TuiOut> {
let w = self.display.w();
let h = self.display.h();
let sizer = Fill::xy(&self.display);
let value_bar = |x|Bsp::n(self.value_bar(), x);
let mode_bar = |x|Bsp::n(self.mode_bar(format!("{w}x{h}")), x);
let title_bar = |x|Bsp::s(self.title_bar(), x);
let sized = |x|Bsp::b(sizer, Fill::xy(x));
value_bar(mode_bar(title_bar(sized(TreeTable(self)))))
}
fn render (&self, to: &mut TuiOut) {
self.content().render(to);
match self.mode {
Some(Mode::Save { value }) => {
to.tint_all(
Color::Rgb(96,96,96),
Color::Rgb(48,48,48),
Modifier::DIM
);
let options = [
if value == 0 { "[ Clear changes ]" } else { " Clear changes " },
if value == 1 { "[ Continue editing ]" } else { " Continue editing " },
if value == 2 { "[ Write and continue ]" } else { " Write and continue " },
];
let modal = Self::modal(Bsp::s(
format!("Save {} change(s)?", self.tasks.len()),
Bsp::s("", Bsp::e(options[0], Bsp::e(options[1], options[2])))));
Content::render(&modal, to)
},
Some(Mode::Quit { value }) => {
to.tint_all(
Color::Rgb(96,96,96),
Color::Rgb(48,48,48),
Modifier::DIM
);
let options = [
if value == 0 { "[ Exit without saving ]" } else { " Exit without saving " },
if value == 1 { "[ Cancel ]" } else { " Cancel " },
if value == 2 { "[ Write and exit ]" } else { " Write and exit " },
];
let modal = Self::modal(Bsp::s(
format!("Save {} change(s) before exiting?", self.tasks.len()),
Bsp::s("", Bsp::e(options[0], Bsp::e(options[1], options[2])))));
Content::render(&modal, to)
},
_ => {},
}
}
}
impl Taggart {
pub(crate) const FG_BROWSE: Color = Color::Rgb(255, 192, 0);
pub(crate) const BG_BROWSE: Color = Color::Rgb(0, 0, 0);
pub(crate) const BG_EDIT: Color = Color::Rgb(48, 128, 0);
pub(crate) const FG_EDIT: Color = Color::Rgb(255, 255, 255);
pub(crate) const BG_SAVE: Color = Color::Rgb(192, 96, 0);
pub(crate) const FG_SAVE: Color = Color::Rgb(255, 255, 255);
pub(crate) const BG_QUIT: Color = Color::Rgb(128, 0, 0);
pub(crate) const FG_QUIT: Color = Color::Rgb(255, 255, 255);
pub(crate) const FG_MODAL: Color = Color::Rgb(255, 255, 255);
pub(crate) const BG_MODAL: Color = Color::Rgb(0, 0, 0);
fn modal (content: impl Content<TuiOut>) -> impl Content<TuiOut> {
let position = |x|Fill::xy(
Align::c(x));
let style = |x|Tui::modify(false, Modifier::DIM,
Tui::fg_bg(Self::FG_MODAL, Self::BG_MODAL, x));
let border = |x|Margin::xy(1, 1, Bsp::b(
Border(true, Lozenge(true, Default::default())),
x));
position(style(border(content)))
}
fn title_bar (&self) -> impl Content<TuiOut> {
status_bar(
Color::Rgb(0, 0, 0),
Color::Rgb(192, 192, 192),
Align::w(self.columns.header())
)
}
fn value_bar (&self) -> impl Content<TuiOut> {
status_bar(
Color::Rgb(192, 192, 192),
Color::Rgb(0, 0, 0),
Fill::x(
Bsp::a(
Fill::x(Align::w(format!(
" {}/{} {}",
self.cursor + 1,
self.entries.len(),
(self.columns.0[self.column].getter)(&self.entries[self.cursor])
.map(|value|format!("{}: {value}", self.columns.0[self.column].title,))
.unwrap_or(String::default())
))),
Fill::x(Align::e(format!(
" {} unsaved changes ",
self.tasks.len()
)))
)
)
)
}
fn mode_bar (&self, size: String) -> impl Content<TuiOut> {
let mode = match self.mode {
Some(Mode::Save { .. }) => Tui::bg(Self::BG_SAVE, Tui::fg(Self::FG_SAVE, " SAVE ")),
Some(Mode::Quit { .. }) => Tui::bg(Self::BG_QUIT, Tui::fg(Self::FG_QUIT, " QUIT ")),
Some(Mode::Edit { .. }) => Tui::bg(Self::BG_EDIT, Tui::fg(Self::FG_EDIT, " EDIT ")),
_ => Tui::bg(Self::BG_BROWSE, Tui::fg(Self::FG_BROWSE, " BROWSE "))
};
let help = match self.mode {
Some(Mode::Save { .. }) => " Esc: cancel, Arrows: select, Enter: confirm",
Some(Mode::Quit { .. }) => " Esc: cancel, Arrows: select, Enter: confirm",
Some(Mode::Edit { .. }) => " Esc: cancel, Enter: set value",
_ => " Q: exit, W: save, Arrows: select, Space: open, Enter: edit",
};
status_bar(
Color::Rgb(0, 0, 0),
Color::Rgb(192, 192, 192),
Fill::x(Bsp::a(
Fill::x(Align::w(Tui::bold(true, Bsp::e(mode, help)))),
Fill::x(Align::e(size)),
))
)
}
}
pub fn status_bar (
fg: Color, bg: Color, content: impl Content<TuiOut>
) -> impl Content<TuiOut> {
Fixed::y(1, Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, content))))
}
impl Entry {
pub const ICON_DIRECTORY: &'static str = "";
pub const ICON_IMAGE: &'static str = "󰋩";
pub const ICON_MUSIC: &'static str = "";
pub const ICON_MUSIC_NO_META: &'static str = "󰎇";
pub const ICON_UNKNOWN: &'static str = "";
pub fn name (&self) -> Option<Arc<str>> {
let indent = "".pad_to_width((self.depth - 1) * 2);
let icon = self.icon();
let name = self.path.iter().last().expect("empty path").display();
Some(format!("{indent}{icon} {name}").into())
}
fn icon (&self) -> &'static str {
if self.is_directory() {
Self::ICON_DIRECTORY
} else if self.is_image() {
Self::ICON_IMAGE
} else if self.is_music() {
Self::ICON_MUSIC
} else {
Self::ICON_UNKNOWN
}
}
}