tengri/src/term.rs
facile pop culture reference 145047b7ff more space/ and draw/ modules
- 26 errors and 16 doctest fails
- getting there, perpetually
2026-04-24 02:06:04 +03:00

714 lines
24 KiB
Rust

use crate::{*, lang::*, draw::*, space::{*, Split::*}, color::*, text::*, task::*};
//use unicode_width::{UnicodeWidthStr, UnicodeWidthChar};
//use rand::distributions::uniform::UniformSampler;
use ::{
std::{
io::{stdout, Write},
time::Duration,
ops::{Deref, DerefMut},
},
better_panic::{Settings, Verbosity},
ratatui::{
prelude::{Style, Buffer as ScreenBuffer, Position, Backend, Color},
style::{Modifier, Color::*},
backend::{CrosstermBackend, ClearType},
layout::{Size, Rect},
buffer::{Buffer, Cell},
},
crossterm::{
ExecutableCommand,
terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode},
//event::{poll, read, Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState},
}
};
#[macro_export] macro_rules! tui_main {
($state:expr) => {
pub fn main () -> Usually<()> {
tengri::exit::Exit::run(|exit|{
let state = Arc::new(RwLock::new($state));
let input = ::tengri::keys::tui_input(
exit.as_ref(),
&state,
::std::time::Duration::from_millis(100)
)?;
let output = ::tengri::term::tui_output(
stdout(),
exit.as_ref(),
&state,
::std::time::Duration::from_millis(10)
)?;
Ok(())
})
}
}
}
#[macro_export] macro_rules! tui_keys {
(|$self:ident:$State:ty,$input:ident|$body:block) => {
impl Apply<TuiEvent, Usually<Self>> for $State {
fn apply (&mut $self, $input: &TuiEvent) -> Usually<Self> $body
}
};
}
#[macro_export] macro_rules! tui_view {
(|$self:ident: $State:ty|$body:block) => {
impl View<Tui> for $State {
fn view (&$self) -> impl Draw<Tui> $body
}
}
}
/// Spawn the TUI output thread which writes colored characters to the terminal.
pub fn tui_output <W: Write + Send + Sync + 'static, T: View<Tui> + Send + Sync + 'static> (
output: W,
exited: &Arc<AtomicBool>,
state: &Arc<RwLock<T>>,
sleep: Duration
) -> Usually<Task> {
let state = state.clone();
tui_setup()?;
let mut backend = CrosstermBackend::new(output);
let Size { width, height } = backend.size().expect("get size failed");
let mut buffer_a = Buffer::empty(Rect { x: 0, y: 0, width, height });
let mut buffer_b = Buffer::empty(Rect { x: 0, y: 0, width, height });
Ok(Task::new_sleep(exited.clone(), sleep, move |perf| {
let Size { width, height } = backend.size().expect("get size failed");
if let Ok(state) = state.try_read() {
tui_resize(&mut backend, &mut buffer_a, Rect { x: 0, y: 0, width, height });
tui_redraw(&mut backend, &mut buffer_a, &mut buffer_b);
}
let timer = format!("{:>3.3}ms", perf.used.load(Relaxed));
buffer_a.set_string(0, 0, &timer, Style::default());
})?)
}
pub struct Tui(pub Buffer, pub XYWH<u16>);
impl Screen for Tui { type Unit = u16; }
impl Deref for Tui { type Target = Buffer; fn deref (&self) -> &Buffer { &self.0 } }
impl DerefMut for Tui { fn deref_mut (&mut self) -> &mut Buffer { &mut self.0 } }
impl HasOrigin for Tui { fn origin (&self) -> Origin { Origin::NW } }
impl X<u16> for Tui {
fn x (&self) -> u16 { self.1.0 }
fn w (&self) -> u16 { self.1.2 }
}
impl Y<u16> for Tui {
fn y (&self) -> u16 { self.1.1 }
fn h (&self) -> u16 { self.1.3 }
}
impl Tui {
pub fn update (&mut self, callback: &impl Fn(&mut Cell, u16, u16)) -> XYWH<u16> {
for row in 0..self.h() {
let y = self.y() + row;
for col in 0..self.w() {
let x = self.x() + col;
if x < self.0.area.width && y < self.0.area.height {
if let Some(cell) = self.0.cell_mut(Position { x, y }) {
callback(cell, col, row);
}
}
}
}
self.xywh()
}
pub fn tint_all (&mut self, fg: Color, bg: Color, modifier: Modifier) {
for cell in self.0.content.iter_mut() {
cell.fg = fg;
cell.bg = bg;
cell.modifier = modifier;
}
}
pub fn blit (&mut self, text: &impl AsRef<str>, x: u16, y: u16, style: Option<Style>) {
let text = text.as_ref();
let style = style.unwrap_or(Style::default());
if x < self.0.area.width && y < self.0.area.height {
self.0.set_string(x, y, text, style);
}
}
}
/// Apply foreground color.
pub const fn fg (fg: Color, draw: impl Draw<Tui>) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|{
to.update(&|cell,_,_|{ cell.set_fg(fg); });
draw.draw(to)
})
}
/// Apply background color.
pub const fn bg (bg: Color, draw: impl Draw<Tui>) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|{
to.update(&|cell,_,_|{ cell.set_bg(bg); });
draw.draw(to)
})
}
pub const fn fg_bg (fg: Color, bg: Color, draw: impl Draw<Tui>) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|{
to.update(&|cell,_,_|{ cell.set_fg(fg); cell.set_bg(bg); });
draw.draw(to)
})
}
pub const fn fill_char (c: char) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|Ok(to.update(&|cell,_,_|{
cell.set_char(c);
})))
}
/// Draw contents with modifier applied.
pub const fn modify (on: bool, modifier: Modifier, draw: impl Draw<Tui>) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|{
fill_mod(on, modifier).draw(to)?;
draw.draw(to)
})
}
pub const fn fill_mod (on: bool, modifier: Modifier) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|Ok({
if on {
to.update(&|cell,_,_|cell.modifier.insert(modifier))
} else {
to.update(&|cell,_,_|cell.modifier.remove(modifier))
}
}))
}
/// Draw contents with bold modifier applied.
pub const fn bold (on: bool, draw: impl Draw<Tui>) -> impl Draw<Tui> {
modify(on, Modifier::BOLD, draw)
}
pub const fn fill_ul (color: Option<Color>) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|Ok(if let Some(color) = color {
to.update(&|cell,_,_|{
cell.modifier.insert(Modifier::UNDERLINED);
cell.underline_color = color;
})
} else {
to.update(&|cell,_,_|{
cell.modifier.remove(Modifier::UNDERLINED);
cell.underline_color = Reset;
})
}))
}
/// TUI works in u16 coordinates.
impl Coord for u16 {
fn plus (self, other: Self) -> Self {
self.saturating_add(other)
}
}
impl Draw<Tui> for u64 {
fn draw (self, _to: &mut Tui) -> Usually<XYWH<u16>> { todo!() }
}
impl Draw<Tui> for f64 {
fn draw (self, _to: &mut Tui) -> Usually<XYWH<u16>> { todo!() }
}
mod phat {
use super::*;
pub const LO: &'static str = "";
pub const HI: &'static str = "";
/// A phat line
pub fn lo (fg: Color, bg: Color) -> impl Draw<Tui> {
h_exact(1, fg_bg(fg, bg, x_repeat(self::phat::LO)))
}
/// A phat line
pub fn hi (fg: Color, bg: Color) -> impl Draw<Tui> {
h_exact(1, fg_bg(fg, bg, x_repeat(self::phat::HI)))
}
}
mod scroll {
pub const ICON_DEC_V: &[char] = &['▲'];
pub const ICON_INC_V: &[char] = &['▼'];
pub const ICON_DEC_H: &[char] = &[' ', '🞀', ' '];
pub const ICON_INC_H: &[char] = &[' ', '🞂', ' '];
}
pub const fn x_repeat (c: &str) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|{
let XYWH(x, y, w, h) = to.xywh();
for x in x..x+w {
if let Some(cell) = to.0.cell_mut(Position::from((x, y))) {
cell.set_symbol(&c);
}
}
Ok(XYWH(x, y, w, 1))
})
}
pub const fn y_repeat (c: &str) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|{
let XYWH(x, y, w, h) = to.xywh();
for y in y..y+h {
if let Some(cell) = to.0.cell_mut(Position::from((x, y))) {
cell.set_symbol(&c);
}
}
Ok(XYWH(x, y, 1, h))
})
}
pub const fn xy_repeat (c: &str) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|{
let XYWH(x, y, w, h) = to.xywh();
let a = c.len();
for (_v, y) in (y..y+h).enumerate() {
for (u, x) in (x..x+w).enumerate() {
if let Some(cell) = to.0.cell_mut(Position::from((x, y))) {
let u = u % a;
cell.set_symbol(&c[u..u+1]);
}
}
}
Ok(XYWH(x, y, w, h))
})
}
/// ```
/// let _ = tengri::button_2("", "", true);
/// let _ = tengri::button_2("", "", false);
/// ```
pub const fn button_2 <'a> (key: impl Draw<Tui>, label: impl Draw<Tui>, hide: bool) -> impl Draw<Tui> {
let c1 = tui_orange();
let c2 = tui_g(0);
let c3 = tui_g(96);
let c4 = tui_g(255);
bold(true, fg_bg(c1, c2, east(fg(c2, east(key, fg(c3, ""))), when(!hide, fg_bg(c4, c3, label)))))
}
/// ```
/// let _ = tengri::button_3("", "", "", true);
/// let _ = tengri::button_3("", "", "", false);
/// ```
pub const fn button_3 <'a> (
key: impl Draw<Tui>, label: impl Draw<Tui>, value: impl Draw<Tui>, editing: bool,
) -> impl Draw<Tui> {
bold(true, east(
fg_bg(tui_orange(), tui_g(0),
east(fg(tui_g(0), ""), east(key, fg(if editing { tui_g(128) } else { tui_g(96) }, "")))),
east(
when(!editing, east(fg_bg(tui_g(255), tui_g(96), label), fg_bg(tui_g(128), tui_g(96), ""),)),
east(fg_bg(tui_g(224), tui_g(128), value), fg_bg(tui_g(128), Reset, ""), ))))
}
macro_rules! border {
($($T:ident {
$nw:literal $n:literal $ne:literal $w:literal $e:literal $sw:literal $s:literal $se:literal
$($x:tt)*
}),+) => {$(
impl BorderStyle for $T {
const NW: &'static str = $nw;
const N: &'static str = $n;
const NE: &'static str = $ne;
const W: &'static str = $w;
const E: &'static str = $e;
const SW: &'static str = $sw;
const S: &'static str = $s;
const SE: &'static str = $se;
$($x)*
fn enabled (&self) -> bool { self.0 }
}
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
//impl Layout<Tui> for $T {}
impl Draw<Tui> for $T {
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
when(self.enabled(), thunk(|to: &mut Tui|BorderStyle::draw(self, to))).draw(to)
}
}
)+}
}
border! {
Square {
"" "" ""
"" ""
"" "" "" fn style (&self) -> Option<Style> { Some(self.1) }
},
SquareBold {
"" "" ""
"" ""
"" "" "" fn style (&self) -> Option<Style> { Some(self.1) }
},
TabLike {
"" "" ""
"" ""
"" " " "" fn style (&self) -> Option<Style> { Some(self.1) }
},
Lozenge {
"" "" ""
"" ""
"" "" "" fn style (&self) -> Option<Style> { Some(self.1) }
},
Brace {
"" "" ""
"" ""
"" "" "" fn style (&self) -> Option<Style> { Some(self.1) }
},
LozengeDotted {
"" "" ""
"" ""
"" "" "" fn style (&self) -> Option<Style> { Some(self.1) }
},
Quarter {
"" "" "🮇"
"" "🮇"
"" "" "🮇" fn style (&self) -> Option<Style> { Some(self.1) }
},
QuarterV {
"" "" "🮇"
"" "🮇"
"" "" "🮇" fn style (&self) -> Option<Style> { Some(self.1) }
},
Chamfer {
"🭂" "" "🭍"
"" "🮇"
"🭓" "" "🭞" fn style (&self) -> Option<Style> { Some(self.1) }
},
Corners {
"🬆" "" "🬊" // 🬴 🬸
"" ""
"🬱" "" "🬵" fn style (&self) -> Option<Style> { Some(self.1) }
},
CornersTall {
"🭽" "" "🭾"
"" ""
"🭼" "" "🭿" fn style (&self) -> Option<Style> { Some(self.1) }
},
Outer {
"🭽" "" "🭾"
"" ""
"🭼" "" "🭿"
const W0: &'static str = "[";
const E0: &'static str = "]";
const N0: &'static str = "";
const S0: &'static str = "";
fn style (&self) -> Option<Style> { Some(self.1) }
},
Thick {
"" "" ""
"" ""
"" "" ""
fn style (&self) -> Option<Style> { Some(self.1) }
},
Rugged {
"" "" ""
"" ""
"" "🮂" ""
fn style (&self) -> Option<Style> { Some(self.1) }
},
Skinny {
"" "" ""
"" ""
"" "" ""
fn style (&self) -> Option<Style> { Some(self.1) }
},
Brackets {
"" "" ""
"" ""
"" "" ""
const W0: &'static str = "[";
const E0: &'static str = "]";
const N0: &'static str = "";
const S0: &'static str = "";
fn style (&self) -> Option<Style> { Some(self.1) }
},
Reticle {
"" "" ""
"" ""
"" "" ""
const W0: &'static str = "";
const E0: &'static str = "";
const N0: &'static str = "";
const S0: &'static str = "";
fn style (&self) -> Option<Style> { Some(self.1) }
}
}
pub trait BorderStyle: Draw<Tui> + Copy {
fn enabled (&self) -> bool;
fn border_n (&self) -> &str { Self::N }
fn border_s (&self) -> &str { Self::S }
fn border_e (&self) -> &str { Self::E }
fn border_w (&self) -> &str { Self::W }
fn border_nw (&self) -> &str { Self::NW }
fn border_ne (&self) -> &str { Self::NE }
fn border_sw (&self) -> &str { Self::SW }
fn border_se (&self) -> &str { Self::SE }
#[inline] fn draw <'a> (self, to: &mut Tui) -> Usually<XYWH<u16>> {
if self.enabled() {
self.draw_h(to, None)?;
self.draw_v(to, None)?;
self.draw_c(to, None)?;
}
Ok(to.1)
}
#[inline] fn draw_h (self, to: &mut Tui, style: Option<Style>) -> Usually<XYWH<u16>> {
let style = style.or_else(||self.style_horizontal());
let y1 = to.y_north();
let y2 = to.y_south().saturating_sub(1);
for x in to.x_west()..to.x_east().saturating_sub(1) {
to.blit(&Self::N, x, y1, style);
to.blit(&Self::S, x, y2, style)
}
Ok(to.xywh())
}
#[inline] fn draw_v (self, to: &mut Tui, style: Option<Style>) -> Usually<XYWH<u16>> {
let area = to.1;
let style = style.or_else(||self.style_vertical());
let [x, x2, y, y2] = area.lrtb();
let h = y2 - y;
if h > 1 {
for y in y..y2.saturating_sub(1) {
to.blit(&Self::W, x, y, style);
to.blit(&Self::E, x2.saturating_sub(1), y, style);
}
} else if h > 0 {
to.blit(&Self::W0, x, y, style);
to.blit(&Self::E0, x2.saturating_sub(1), y, style);
}
Ok(area)
}
#[inline] fn draw_c (self, to: &mut Tui, style: Option<Style>) -> Usually<XYWH<u16>> {
let area = to.1;
let style = style.or_else(||self.style_corners());
let XYWH(x, y, w, h) = area;
if w > 1 && h > 1 {
to.blit(&Self::NW, x, y, style);
to.blit(&Self::NE, x + w - 1, y, style);
to.blit(&Self::SW, x, y + h- 1, style);
to.blit(&Self::SE, x + w - 1, y + h - 1, style);
}
Ok(area)
}
#[inline] fn style (&self) -> Option<Style> { None }
#[inline] fn style_horizontal (&self) -> Option<Style> { self.style() }
#[inline] fn style_vertical (&self) -> Option<Style> { self.style() }
#[inline] fn style_corners (&self) -> Option<Style> { self.style() }
const NW: &'static str = "";
const N: &'static str = "";
const NE: &'static str = "";
const E: &'static str = "";
const SE: &'static str = "";
const S: &'static str = "";
const SW: &'static str = "";
const W: &'static str = "";
const N0: &'static str = "";
const S0: &'static str = "";
const W0: &'static str = "";
const E0: &'static str = "";
}
/// Stackably padded.
///
/// ```
/// /// TODO
/// ```
pub fn phat (w: u16, h: u16, [fg, bg, hi, lo]: [Color;4], draw: impl Draw<Tui>) -> impl Draw<Tui> {
let top = w_exact(1, self::phat::lo(bg, hi));
let low = w_exact(1, self::phat::hi(bg, lo));
let draw = fg_bg(fg, bg, draw);
wh_min(Some(w), Some(h), south(top, north(low, draw)))
}
fn x_scroll () -> impl Draw<Tui> {
thunk(|Tui(buf, XYWH(x1, y1, w, h)): &mut Tui|{
let x2 = *x1 + *w;
for (i, x) in (*x1..=x2).enumerate() {
if let Some(cell) = buf.cell_mut(Position::from((x, *y1))) {
if i < (self::scroll::ICON_DEC_H.len()) {
cell.set_fg(Rgb(255, 255, 255));
cell.set_bg(Rgb(0, 0, 0));
cell.set_char(self::scroll::ICON_DEC_H[i as usize]);
} else if i > (*w as usize - self::scroll::ICON_INC_H.len()) {
cell.set_fg(Rgb(255, 255, 255));
cell.set_bg(Rgb(0, 0, 0));
cell.set_char(self::scroll::ICON_INC_H[*w as usize - i]);
} else if false {
cell.set_fg(Rgb(255, 255, 255));
cell.set_bg(Reset);
cell.set_char('━');
} else {
cell.set_fg(Rgb(0, 0, 0));
cell.set_bg(Reset);
cell.set_char('╌');
}
}
}
Ok(XYWH(*x1, *y1, *w, 1))
})
}
fn y_scroll () -> impl Draw<Tui> {
thunk(|Tui(buf, XYWH(x1, y1, w, h)): &mut Tui|{
let y2 = *y1 + *h;
for (i, y) in (*y1..=y2).enumerate() {
if let Some(cell) = buf.cell_mut(Position::from((*x1, y))) {
if (i as usize) < (self::scroll::ICON_DEC_V.len()) {
cell.set_fg(Rgb(255, 255, 255));
cell.set_bg(Rgb(0, 0, 0));
cell.set_char(self::scroll::ICON_DEC_V[i as usize]);
} else if (i as usize) > (*h as usize - self::scroll::ICON_INC_V.len()) {
cell.set_fg(Rgb(255, 255, 255));
cell.set_bg(Rgb(0, 0, 0));
cell.set_char(self::scroll::ICON_INC_V[*h as usize - i]);
} else if false {
cell.set_fg(Rgb(255, 255, 255));
cell.set_bg(Reset);
cell.set_char('‖'); // ━
} else {
cell.set_fg(Rgb(0, 0, 0));
cell.set_bg(Reset);
cell.set_char('╎'); // ━
}
}
}
Ok(XYWH(*x1, *y1, 1, *h))
})
}
pub fn tui_redraw <'b, W: Write> (
backend: &mut CrosstermBackend<W>,
mut prev_buffer: &'b mut Buffer,
mut next_buffer: &'b mut Buffer
) {
let updates = prev_buffer.diff(&next_buffer);
backend.draw(updates.into_iter()).expect("failed to render");
Backend::flush(backend).expect("failed to flush output new_buffer");
std::mem::swap(&mut prev_buffer, &mut next_buffer);
next_buffer.reset();
}
pub fn tui_setup () -> Usually<()> {
let better_panic_handler = Settings::auto().verbosity(Verbosity::Full).create_panic_handler();
std::panic::set_hook(Box::new(move |info: &std::panic::PanicHookInfo|{
stdout().execute(LeaveAlternateScreen).unwrap();
CrosstermBackend::new(stdout()).show_cursor().unwrap();
disable_raw_mode().unwrap();
better_panic_handler(info);
}));
stdout().execute(EnterAlternateScreen)?;
CrosstermBackend::new(stdout()).hide_cursor()?;
enable_raw_mode().map_err(Into::into)
}
pub fn tui_resize <W: Write> (
backend: &mut CrosstermBackend<W>,
buffer: &mut Buffer,
size: Rect
) {
if buffer.area != size {
backend.clear_region(ClearType::All).unwrap();
buffer.resize(size);
buffer.reset();
}
}
pub fn tui_teardown <W: Write> (backend: &mut CrosstermBackend<W>) -> Usually<()> {
stdout().execute(LeaveAlternateScreen)?;
backend.show_cursor()?;
disable_raw_mode().map_err(Into::into)
}
pub fn tui_update (
Tui(buf, ..): &mut Tui, area: XYWH<u16>, callback: &impl Fn(&mut Cell, u16, u16)
) {
}
/// Draw border around shrinked item.
///
/// ```
/// /// TODO
/// ```
pub const fn border <T, S: BorderStyle> (on: bool, style: S, draw: impl Draw<Tui>) -> impl Draw<Tui> {
let content = wh_pad(1, 1, draw);
let outline = when(on, thunk(move|to: &mut Tui|{
let XYWH(x, y, w, h) = to.1;
if w > 0 && h > 0 {
to.blit(&style.border_nw(), x, y, style.style());
to.blit(&style.border_ne(), x + w - 1, y, style.style());
to.blit(&style.border_sw(), x, y + h - 1, style.style());
to.blit(&style.border_se(), x + w - 1, y + h - 1, style.style());
for x in x+1..x+w-1 {
to.blit(&style.border_n(), x, y, style.style());
to.blit(&style.border_s(), x, y + h - 1, style.style());
}
for y in y+1..y+h-1 {
to.blit(&style.border_w(), x, y, style.style());
to.blit(&style.border_e(), x + w - 1, y, style.style());
}
}
Ok(XYWH(x, y, w, h))
}));
above(outline, content)
}
/// Draw TUI content or its error message.
///
/// ```
/// let _ = tengri::term::catcher(Ok(Some("hello")));
/// let _ = tengri::term::catcher(Ok(None));
/// let _ = tengri::term::catcher(Err("draw fail".into()));
/// ```
pub fn catcher <T: Draw<Tui>> (result: Usually<T>) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|match result {
Ok(content) => content.draw(to),
Err(e) => {
let err_fg = Color::Rgb(255,224,244);
let err_bg = Color::Rgb(96, 24, 24);
let title = east(bold(true, "upsi daisy. "), "rendering failed.");
let error = east("\"why?\" ", bold(true, format!("{e}")));
fg(err_fg, bg(err_bg, south(title, error))).draw(to)
}
})
}
/// TUI buffer sized by `usize` instead of `u16`.
#[derive(Default)]
pub struct BigBuffer {
pub width: usize,
pub height: usize,
pub content: Vec<Cell>
}
impl_from!(BigBuffer: |size:(usize, usize)| Self::new(size.0, size.1));
impl_debug!(BigBuffer |self, f| { write!(f, "[BB {}x{} ({})]", self.width, self.height, self.content.len()) });
impl BigBuffer {
pub fn new (width: usize, height: usize) -> Self {
Self { width, height, content: vec![Cell::default(); width*height] }
}
pub fn get (&self, x: usize, y: usize) -> Option<&Cell> {
let i = self.index_of(x, y);
self.content.get(i)
}
pub fn get_mut (&mut self, x: usize, y: usize) -> Option<&mut Cell> {
let i = self.index_of(x, y);
self.content.get_mut(i)
}
pub fn index_of (&self, x: usize, y: usize) -> usize {
y * self.width + x
}
}
use self::colors::*; mod colors {
use ratatui::prelude::Color;
pub const fn tui_color_bg () -> Color { Color::Rgb(28, 35, 25) }
pub const fn tui_bg0 () -> Color { Color::Rgb(20, 20, 20) }
pub const fn tui_bo1 () -> Color { Color::Rgb(100, 110, 40) }
pub const fn tui_bo2 () -> Color { Color::Rgb(70, 80, 50) }
pub const fn tui_border_bg () -> Color { Color::Rgb(40, 50, 30) }
pub const fn tui_border_fg (f: bool) -> Color { if f { tui_bo1() } else { tui_bo2() } }
pub const fn tui_brown () -> Color { Color::Rgb(128,255,0) }
pub const fn tui_electric () -> Color { Color::Rgb(0,255,128) }
pub const fn tui_g (g: u8) -> Color { Color::Rgb(g, g, g) }
pub const fn tui_green () -> Color { Color::Rgb(0,255,0) }
pub const fn tui_mode_bg () -> Color { Color::Rgb(150, 160, 90) }
pub const fn tui_mode_fg () -> Color { Color::Rgb(255, 255, 255) }
pub const fn tui_null () -> Color { Color::Reset }
pub const fn tui_orange () -> Color { Color::Rgb(255,128,0) }
pub const fn tui_red () -> Color { Color::Rgb(255,0, 0) }
pub const fn tui_separator_fg (_: bool) -> Color { Color::Rgb(0, 0, 0) }
pub const fn tui_status_bar_bg () -> Color { Color::Rgb(28, 35, 25) }
pub const fn tui_ti1 () -> Color { Color::Rgb(150, 160, 90) }
pub const fn tui_ti2 () -> Color { Color::Rgb(120, 130, 100) }
pub const fn tui_title_fg (f: bool) -> Color { if f { tui_ti1() } else { tui_ti2() } }
pub const fn tui_yellow () -> Color { Color::Rgb(255,255,0) }
}