termint 0.9.0

Library for colored printing and Terminal User Interfaces
Documentation
use std::process::ExitCode;

use fake::{Fake, faker::lorem::en::Words};
use termal::eprintcln;
use termint::{prelude::*, widgets::Grad};

const BG: Color = Color::Hex(0x02081e);
const BORDER: Color = Color::Hex(0x535C91);
const FG: Color = Color::Hex(0xc3c1f4);

const COLORS: [((u8, u8, u8), (u8, u8, u8)); 3] = [
    ((0, 220, 255), (175, 80, 255)),
    ((255, 255, 0), (0, 255, 255)),
    ((255, 15, 123), (248, 155, 41)),
];

fn main() -> ExitCode {
    if let Err(e) = run() {
        eprintcln!("{'r}Error:{'_} {e}");
        return ExitCode::FAILURE;
    }
    ExitCode::SUCCESS
}

fn run() -> Result<(), Error> {
    let mut app = App::default();
    Term::default().setup()?.run(&mut app)
}

struct App {
    lorem: String,
    align: TextAlign,
    dir: Direction,
    grad_id: usize,
}

impl Application for App {
    type Message = ();

    fn view(&self, _frame: &Frame) -> Element<Self::Message> {
        let (start_col, end_col) = COLORS[self.grad_id];

        let mut title = Paragraph::empty().align(self.align);
        title.push(Grad::new("Text", start_col, end_col));
        title.push("Example".italic());

        let mut block = Block::vertical()
            .title(title)
            .border_type(BorderType::Thicker)
            .border_style(Style::new().bg(BG).fg(BORDER))
            .style(Style::new().bg(BG).fg(FG));

        let span = "Span widget (static styling)"
            .bold()
            .fg(Color::Yellow)
            .align(self.align);
        block.push(span, 1..);

        let grad =
            Grad::new("Grad widget (gradient text)", start_col, end_col)
                .align(self.align);
        block.push(grad, 1..);

        let mut paragraph = Paragraph::empty().align(self.align);
        paragraph.push("Paragraphs can mix".fg(Color::Green));
        let grad = Grad::new("Gradients", start_col, end_col);
        paragraph.push(grad);
        paragraph.push("and");
        paragraph.push("Spans.".red().bold());
        block.push(paragraph, 0..);

        let header = Block::empty()
            .title("Paragraph with ellipsis".align(self.align))
            .border_style(Style::new().bg(BG).fg(BORDER));
        block.push(Spacer, 1);
        block.push(header, 1);

        let mut lorem = Paragraph::empty().align(self.align);
        lorem.push(
            Grad::new(self.lorem.as_str(), start_col, end_col)
                .direction(self.dir),
        );
        lorem.push(self.lorem.as_str().italic());

        let mut wrapper = Layout::horizontal();
        let (left, right) = match self.align {
            TextAlign::Left => (Constraint::Length(0), Constraint::Fill(1)),
            TextAlign::Center => (Constraint::Fill(1), Constraint::Fill(1)),
            TextAlign::Right => (Constraint::Fill(1), Constraint::Length(0)),
        };
        wrapper.push(Spacer, left);
        wrapper.push(lorem, ..50);
        wrapper.push(Spacer, right);
        block.push(wrapper, 8);

        block.push(Spacer, Constraint::Fill(1));
        let help = "[←/→]Cycle grad.  [r]Rotate grad.  [a]Align  [Esc/q] Quit"
            .fg(BORDER)
            .align(TextAlign::Center);
        block.push(help, 1..);

        block.into()
    }

    fn event(&mut self, event: Event) -> Action {
        match event {
            Event::Key(key) => self.key_listener(key),
            _ => Action::NONE,
        }
    }
}

impl App {
    fn key_listener(&mut self, key: KeyEvent) -> Action {
        match key.code {
            KeyCode::Esc | KeyCode::Char('q') => return Action::QUIT,
            KeyCode::Left | KeyCode::Char('h') => {
                self.grad_id =
                    self.grad_id.checked_sub(1).unwrap_or(COLORS.len() - 1);
            }
            KeyCode::Right | KeyCode::Char('l') => {
                self.grad_id = (self.grad_id + 1) % COLORS.len();
            }
            KeyCode::Char('r') => self.toggle_dir(),
            KeyCode::Char('a') => self.toggle_align(),
            _ => return Action::NONE,
        }
        Action::RENDER
    }

    fn toggle_dir(&mut self) {
        self.dir = match self.dir {
            Direction::Vertical => Direction::Horizontal,
            Direction::Horizontal => Direction::Vertical,
        };
    }

    fn toggle_align(&mut self) {
        self.align = match self.align {
            TextAlign::Left => TextAlign::Center,
            TextAlign::Center => TextAlign::Right,
            TextAlign::Right => TextAlign::Left,
        };
    }
}

impl Default for App {
    fn default() -> Self {
        Self {
            lorem: Words(35..36).fake::<Vec<String>>().join(" "),
            align: TextAlign::Left,
            dir: Direction::Horizontal,
            grad_id: 0,
        }
    }
}