diff_tool/view/
footer.rs

1use std::collections::{HashMap, HashSet};
2
3use ratatui::{
4    layout::{Constraint, Layout, Rect},
5    style::{Color, Style},
6    text::Line,
7    widgets::{Block, BorderType, List, ListDirection, Row, Table},
8    Frame,
9};
10
11use crate::{app::App, update::message::Message};
12
13pub(super) fn render_footer(app: &App, area: Rect, f: &mut Frame) {
14    // Footer Layout (Console & Help)
15    let [left, right] = Layout::horizontal(Constraint::from_percentages([50, 50])).areas(area);
16
17    // Console Section
18    let console = draw_console(app);
19    let mut console_state = app.console_state().borrow_mut();
20    f.render_stateful_widget(console, left, &mut console_state);
21
22    // Help Menu
23    let help_menu = build_help_table(app);
24    f.render_widget(help_menu, right);
25}
26
27/// Draws the console menu component
28fn draw_console(app: &App) -> List {
29    let items = app.console().to_owned();
30    List::new(items)
31        .block(Block::bordered().title("Console"))
32        .style(Style::default().fg(Color::White))
33        .highlight_symbol(">>")
34        .repeat_highlight_symbol(true)
35        .direction(ListDirection::TopToBottom)
36}
37
38/// Draws the help menu component
39fn build_help_table(app: &App) -> Table {
40    let key_style = Style::default().fg(Color::LightCyan);
41    let message_style = Style::default().fg(Color::Gray);
42
43    let keymaps = app.config().keymap();
44    let mut combined_keymaps = combine_keys_by_value(keymaps);
45    combined_keymaps.sort_unstable_by_key(|(_, message)| *message);
46
47    let longest_string = longest_combined_keymap(&combined_keymaps);
48    let widths = [Constraint::Length(longest_string), Constraint::Min(10)];
49
50    let keymap_rows = combined_keymaps.iter().map(|(keybinds, message)| {
51        Row::new([
52            Line::styled(keybinds.to_owned(), key_style),
53            Line::styled(message.to_string(), message_style),
54        ])
55    });
56
57    Table::new(keymap_rows, widths).block(
58        Block::bordered()
59            .border_type(BorderType::Plain)
60            .title("Help"),
61    )
62}
63
64fn longest_combined_keymap(combined_keymaps: &Vec<(String, &Message)>) -> u16 {
65    combined_keymaps
66        .iter()
67        .map(|(keys, _)| keys.len().try_into().unwrap())
68        .max()
69        .unwrap()
70}
71
72fn combine_keys_by_value(map: &HashMap<String, Message>) -> Vec<(String, &Message)> {
73    let mut result = Vec::new();
74    let mut processed_messages = HashSet::new();
75
76    for (_, message) in map.iter() {
77        if !processed_messages.contains(message) {
78            let combined_keys = map
79                .into_iter()
80                .filter(|(_, m)| *m == message)
81                .enumerate()
82                .map(|(i, (k, _))| {
83                    if i == 0 {
84                        k.to_owned().to_uppercase()
85                    } else {
86                        format!(" | {}", k.to_uppercase())
87                    }
88                })
89                .collect::<String>();
90            result.push((combined_keys, message));
91            processed_messages.insert(message);
92        }
93    }
94
95    result
96}