tui-journal 0.17.0

Tui app allows writing and managing journals/notes from within the terminal With different local back-ends
Documentation
use backend::DataProvider;
use ratatui::{
    Frame,
    layout::{Alignment, Rect},
    style::Style,
    widgets::{Block, Borders, Paragraph, Wrap},
};

use crate::app::{App, keymap::Keymap};

use super::{ControlType, UICommand, UIComponents};

const SEPARATOR: &str = " | ";

pub fn get_footer_height<D: DataProvider>(
    width: u16,
    ui_components: &UIComponents,
    app: &App<D>,
) -> u16 {
    let footer_text = get_footer_text(ui_components, app);
    footer_text.len() as u16 / width + 1
}

pub fn render_footer<D: DataProvider>(
    frame: &mut Frame,
    area: Rect,
    ui_components: &UIComponents,
    app: &App<D>,
) {
    let footer_text = get_footer_text(ui_components, app);
    let footer = Paragraph::new(footer_text)
        .alignment(Alignment::Left)
        .wrap(Wrap { trim: false })
        .block(
            Block::default()
                .borders(Borders::NONE)
                .style(Style::default()),
        );

    frame.render_widget(footer, area);
}

fn get_footer_text<D: DataProvider>(ui_components: &UIComponents, app: &App<D>) -> String {
    let (editor_mode, multi_select_mode) = (
        ui_components.editor.is_insert_mode(),
        ui_components.entries_list.multi_select_mode,
    );
    match (editor_mode, multi_select_mode) {
        (true, false) => get_editor_mode_text(ui_components),
        (false, true) => get_multi_select_text(ui_components),
        _ => get_standard_text(ui_components, app),
    }
}

fn get_editor_mode_text(ui_components: &UIComponents) -> String {
    let exit_editor_mode_keymap: Vec<_> = ui_components
        .editor_keymaps
        .iter()
        .filter(|keymap| keymap.command == UICommand::BackEditorNormalMode)
        .collect();

    format!(
        "{}{} Edit using Emacs motions",
        get_keymap_text(exit_editor_mode_keymap),
        SEPARATOR
    )
}

fn get_standard_text<D: DataProvider>(ui_components: &UIComponents, app: &App<D>) -> String {
    let close_keymap: Vec<_> = ui_components
        .global_keymaps
        .iter()
        .filter(|keymap| keymap.command == UICommand::Quit)
        .collect();

    let enter_editor_keymap: Vec<_> = ui_components
        .global_keymaps
        .iter()
        .filter(|keymap| keymap.command == UICommand::StartEditEntryContent)
        .collect();

    let mut footer_parts = vec![
        get_keymap_text(close_keymap),
        get_keymap_text(enter_editor_keymap),
    ];

    if ui_components.active_control == ControlType::EntriesList {
        if app.filter.is_none() {
            let show_filter_keymap: Vec<_> = ui_components
                .entries_list_keymaps
                .iter()
                .filter(|keymap| keymap.command == UICommand::ShowFilter)
                .collect();

            footer_parts.push(get_keymap_text(show_filter_keymap));
        } else {
            let reset_filter_keymap: Vec<_> = ui_components
                .entries_list_keymaps
                .iter()
                .filter(|keymap| keymap.command == UICommand::ResetFilter)
                .collect();

            footer_parts.push(get_keymap_text(reset_filter_keymap));
        }

        let sort_keymap = ui_components
            .entries_list_keymaps
            .iter()
            .filter(|keymap| keymap.command == UICommand::ShowSortOptions)
            .collect();

        footer_parts.push(get_keymap_text(sort_keymap));
    }

    if app.state.full_screen {
        let full_screen_keymap: Vec<_> = ui_components
            .global_keymaps
            .iter()
            .filter(|keymap| keymap.command == UICommand::ToggleFullScreenMode)
            .collect();
        footer_parts.push(get_keymap_text(full_screen_keymap));
    }

    let help_keymap: Vec<_> = ui_components
        .global_keymaps
        .iter()
        .filter(|keymap| keymap.command == UICommand::ShowHelp)
        .collect();

    footer_parts.push(get_keymap_text(help_keymap));

    footer_parts.join(SEPARATOR)
}

fn get_multi_select_text(ui_components: &UIComponents) -> String {
    let leave_keymap: Vec<_> = ui_components
        .multi_select_keymaps
        .iter()
        .filter(|keymap| keymap.command == UICommand::LeaveMultiSelectMode)
        .collect();

    let help_keymap: Vec<_> = ui_components
        .multi_select_keymaps
        .iter()
        .filter(|keymap| keymap.command == UICommand::ShowHelp)
        .collect();

    let parts = [get_keymap_text(leave_keymap), get_keymap_text(help_keymap)];

    parts.join(SEPARATOR)
}

fn get_keymap_text(keymaps: Vec<&Keymap>) -> String {
    let cmd_text = keymaps
        .first()
        .map(|keymap| keymap.command.get_info().name)
        .expect("Keymaps shouldn't be empty");

    let keys: Vec<String> = keymaps
        .iter()
        .map(|keymap| format!("'{}'", keymap.key))
        .collect();

    format!("{}: {}", cmd_text, keys.join(","))
}