effy 0.11.0

A small and friendly terminal FFmpeg helper that simplifies common tasks
use crossterm::event::KeyEvent;
use ratatui::prelude::Frame;
use ratatui::text::Line;
use ratatui::widgets::{Block, BorderType, Borders, Padding, Paragraph, Widget as _};
use ratatui::{layout::Constraint, style::Style};

use crate::ui::Theme;
use crate::ui::modal::{KeyboardHandler, ModalResult, UiModal};
use crate::ui::widget::BgClear;

#[derive(Debug)]
pub(crate) enum AlertKind {
    Info,
    #[allow(dead_code)]
    Warning,
    Error,
}

impl AlertKind {
    fn name(&self) -> &str {
        match *self {
            AlertKind::Info => " Info ",
            AlertKind::Warning => " Warning ",
            AlertKind::Error => " Error ",
        }
    }
}

#[derive(Debug)]
pub(crate) struct AlertModal<'a> {
    kind: AlertKind,
    message: &'a str,
    width: u16,
    height: u16,
}

impl<'a> UiModal for AlertModal<'a>
where
    'a: 'static,
{
    fn render(&mut self, frame: &mut Frame, theme: &Theme) {
        let modal_area = frame.area().centered(
            Constraint::Length(self.width + 10),
            Constraint::Length(self.height + 3),
        );

        frame.render_widget(BgClear::new(theme.background_color()), modal_area);
        Paragraph::new(self.message)
            .style(self.modal_style(theme).bold())
            .block(
                Block::default()
                    .title_top(Line::from(self.kind.name()).centered())
                    .borders(Borders::TOP)
                    .border_type(BorderType::Double)
                    .border_style(self.border_top_style(theme))
                    .padding(Padding::vertical(1)),
            )
            .centered()
            .render(modal_area, frame.buffer_mut());
    }
}

impl KeyboardHandler for AlertModal<'_> {
    fn handle_key(&mut self, _key: KeyEvent) -> ModalResult {
        ModalResult::Close
    }
}

impl<'a> AlertModal<'a> {
    pub(crate) fn new(kind: AlertKind, message: &'a str) -> AlertModal<'a> {
        Self {
            kind,
            message,
            width: message
                .lines()
                .map(|line| line.len() as u16)
                .max()
                .unwrap_or(0),
            height: message.lines().count() as u16,
        }
    }

    fn modal_style(&self, theme: &Theme) -> Style {
        match self.kind {
            AlertKind::Info => Style::new().fg(theme.info_text_color()),
            AlertKind::Warning => Style::new().fg(theme.warning_text_color()),
            AlertKind::Error => theme.error_bg_style(),
        }
    }

    fn border_top_style(&self, theme: &Theme) -> Style {
        match self.kind {
            AlertKind::Info => theme.info_style(),
            AlertKind::Warning => theme.warning_style(),
            AlertKind::Error => theme.error_bg_style(),
        }
    }
}