tui-journal 0.17.0

Tui app allows writing and managing journals/notes from within the terminal With different local back-ends
Documentation
use crossterm::event::KeyCode;
use ratatui::{
    Frame,
    layout::{Alignment, Constraint, Direction, Layout, Rect},
    style::Style,
    text::Span,
    widgets::{Block, Borders, Clear, Paragraph, Wrap},
};

use crate::app::keymap::Input;

use super::{Styles, ui_functions::centered_rect_exact_height};

// Not all enums are used in this app at this point
#[allow(dead_code)]
#[derive(Debug)]
pub enum MsgBoxType {
    Error(String),
    Warning(String),
    Info(String),
    Question(String),
}

// Not all enums are used in this app at this point
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MsgBoxActions {
    Ok,
    OkCancel,
    YesNo,
    YesNoCancel,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MsgBoxResult {
    Ok,
    Cancel,
    Yes,
    No,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MsgBoxInputResult {
    Keep,
    Close(MsgBoxResult),
}

#[derive(Debug)]
pub struct MsgBox {
    msg_type: MsgBoxType,
    actions: MsgBoxActions,
}

impl MsgBox {
    pub fn new(msg_type: MsgBoxType, actions: MsgBoxActions) -> Self {
        Self { msg_type, actions }
    }

    pub fn render_widget(&mut self, frame: &mut Frame, area: Rect, styles: &Styles) {
        let area = centered_rect_exact_height(55, 8, area);

        let colors = &styles.msgbox;

        let (title, color, text) = match &self.msg_type {
            MsgBoxType::Error(text) => ("Error", colors.error, text),
            MsgBoxType::Warning(text) => ("Warning", colors.warning, text),
            MsgBoxType::Info(text) => ("Info", colors.info, text),
            MsgBoxType::Question(text) => ("", colors.question, text),
        };

        let border = Block::default()
            .borders(Borders::ALL)
            .title(title)
            .style(Style::default().fg(color));

        frame.render_widget(Clear, area);
        frame.render_widget(border, area);

        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .horizontal_margin(3)
            .vertical_margin(2)
            .constraints([Constraint::Min(2), Constraint::Length(1)].as_ref())
            .split(area);

        let text_paragraph = Paragraph::new(Span::raw(text))
            .style(Style::default().fg(color))
            .alignment(Alignment::Center)
            .wrap(Wrap { trim: false });

        frame.render_widget(text_paragraph, chunks[0]);

        let actions_text = match self.actions {
            MsgBoxActions::Ok => "(O)k",
            MsgBoxActions::OkCancel => "(O)k , (C)ancel",
            MsgBoxActions::YesNo => "(Y)es , (N)o",
            MsgBoxActions::YesNoCancel => "(Y)es , (N)o , (C)ancel",
        };

        let actions_paragraph = Paragraph::new(Span::raw(actions_text))
            .alignment(Alignment::Center)
            .wrap(Wrap { trim: false });

        frame.render_widget(actions_paragraph, chunks[1]);
    }

    pub fn handle_input(&self, input: &Input) -> MsgBoxInputResult {
        match self.actions {
            MsgBoxActions::Ok => match input.key_code {
                KeyCode::Enter | KeyCode::Esc | KeyCode::Char('o') => {
                    MsgBoxInputResult::Close(MsgBoxResult::Ok)
                }
                _ => MsgBoxInputResult::Keep,
            },
            MsgBoxActions::OkCancel => match input.key_code {
                KeyCode::Enter | KeyCode::Char('o') => MsgBoxInputResult::Close(MsgBoxResult::Ok),
                KeyCode::Esc | KeyCode::Char('c') => MsgBoxInputResult::Close(MsgBoxResult::Cancel),
                _ => MsgBoxInputResult::Keep,
            },
            MsgBoxActions::YesNo => match input.key_code {
                KeyCode::Enter | KeyCode::Char('y') => MsgBoxInputResult::Close(MsgBoxResult::Yes),
                KeyCode::Esc | KeyCode::Char('n') => MsgBoxInputResult::Close(MsgBoxResult::No),
                _ => MsgBoxInputResult::Keep,
            },
            MsgBoxActions::YesNoCancel => match input.key_code {
                KeyCode::Enter | KeyCode::Char('y') => MsgBoxInputResult::Close(MsgBoxResult::Yes),
                KeyCode::Char('n') => MsgBoxInputResult::Close(MsgBoxResult::No),
                KeyCode::Esc | KeyCode::Char('c') => MsgBoxInputResult::Close(MsgBoxResult::Cancel),
                _ => MsgBoxInputResult::Keep,
            },
        }
    }
}