todotxt-tui 0.3.0

Todo.txt TUI is a highly customizable terminal-based application for managing your todo tasks. It follows the todo.txt format and offers a wide range of configuration options to suit your needs.
Documentation
use crate::config::WidgetBorderType;
use tui::{
    layout::{Constraint, Flex, Layout, Rect},
    widgets::{Block, Clear, Paragraph, Wrap},
    Frame,
};

pub struct Popup {
    border_type: WidgetBorderType,
    message: Option<String>,
    help: Option<String>,
}

impl Popup {
    pub fn new(border_type: WidgetBorderType) -> Self {
        Self {
            border_type,
            message: None,
            help: None,
        }
    }

    fn center_popup_area(area: Rect, percent_width: u16, percent_height: u16) -> Rect {
        let vertical =
            Layout::vertical([Constraint::Percentage(percent_height)]).flex(Flex::Center);
        let horizontal =
            Layout::horizontal([Constraint::Percentage(percent_width)]).flex(Flex::Center);
        let [area] = vertical.areas(area);
        let [area] = horizontal.areas(area);
        area
    }

    pub fn render_popup(&mut self, frame: &mut Frame) {
        if let Some(help) = &self.help {
            let area = Self::center_popup_area(frame.area(), 80, 80);
            frame.render_widget(Clear, area);
            frame.render_widget(
                Paragraph::new(help.as_str())
                    .wrap(Wrap { trim: false })
                    .block(
                        Block::bordered()
                            .border_type(self.border_type.into())
                            .title("Keybindings (? or Esc to close)"),
                    ),
                area,
            );
        } else if let Some(message) = self.message.take() {
            let area = Self::center_popup_area(frame.area(), 50, 25);
            frame.render_widget(Clear, area);
            frame.render_widget(
                Paragraph::new(message).block(
                    Block::bordered()
                        .border_type(self.border_type.into())
                        .title("Error"),
                ),
                area,
            );
        }
    }

    pub fn add_message(&mut self, message: String) {
        self.message = Some(message);
    }

    pub fn show_help(&mut self, content: String) {
        self.help = Some(content);
    }

    pub fn hide_help(&mut self) {
        self.help = None;
    }

    pub fn is_help_visible(&self) -> bool {
        self.help.is_some()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn popup() {
        let mut popup = Popup::new(WidgetBorderType::Plain);
        popup.add_message(String::from("popup message"));
    }

    #[test]
    fn centering() {
        assert_eq!(
            Popup::center_popup_area(
                Rect {
                    x: 0,
                    y: 0,
                    width: 100,
                    height: 100,
                },
                50,
                50
            ),
            Rect {
                x: 25,
                y: 25,
                width: 50,
                height: 50
            }
        );
    }
}