rust_kanban/ui/rendering/view/
edit_keybindings.rs

1use crate::{
2    app::{
3        state::{Focus, KeyBindingEnum},
4        App,
5    },
6    constants::{SCROLLBAR_BEGIN_SYMBOL, SCROLLBAR_END_SYMBOL, SCROLLBAR_TRACK_SYMBOL},
7    ui::{
8        rendering::{
9            common::{draw_title, render_close_button},
10            utils::{
11                check_if_active_and_get_style, get_button_style, get_mouse_focusable_field_style,
12            },
13            view::EditKeybindings,
14        },
15        Renderable,
16    },
17};
18use ratatui::{
19    layout::{Alignment, Constraint, Layout, Margin},
20    text::{Line, Span},
21    widgets::{
22        Block, BorderType, Borders, Cell, Paragraph, Row, Scrollbar, ScrollbarOrientation,
23        ScrollbarState, Table,
24    },
25    Frame,
26};
27
28impl Renderable for EditKeybindings {
29    fn render(rect: &mut Frame, app: &mut App, is_active: bool) {
30        let chunks = Layout::default()
31            .constraints(
32                [
33                    Constraint::Length(3),
34                    Constraint::Fill(1),
35                    Constraint::Length(5),
36                    Constraint::Length(3),
37                ]
38                .as_ref(),
39            )
40            .split(rect.area());
41
42        let default_style = check_if_active_and_get_style(
43            is_active,
44            app.current_theme.inactive_text_style,
45            app.current_theme.general_style,
46        );
47        let scrollbar_style = check_if_active_and_get_style(
48            is_active,
49            app.current_theme.inactive_text_style,
50            app.current_theme.progress_bar_style,
51        );
52        let reset_style =
53            get_button_style(app, Focus::SubmitButton, Some(&chunks[3]), is_active, true);
54        let current_element_style = check_if_active_and_get_style(
55            is_active,
56            app.current_theme.inactive_text_style,
57            app.current_theme.list_select_style,
58        );
59        let table_border_style = get_mouse_focusable_field_style(
60            app,
61            Focus::EditKeybindingsTable,
62            &chunks[1],
63            is_active,
64            false,
65        );
66        let help_key_style = check_if_active_and_get_style(
67            is_active,
68            app.current_theme.inactive_text_style,
69            app.current_theme.help_key_style,
70        );
71        let help_text_style = check_if_active_and_get_style(
72            is_active,
73            app.current_theme.inactive_text_style,
74            app.current_theme.help_text_style,
75        );
76
77        let up_key = app
78            .get_first_keybinding(KeyBindingEnum::Up)
79            .unwrap_or("".to_string());
80        let down_key = app
81            .get_first_keybinding(KeyBindingEnum::Down)
82            .unwrap_or("".to_string());
83        let next_focus_key = app
84            .get_first_keybinding(KeyBindingEnum::NextFocus)
85            .unwrap_or("".to_string());
86        let prv_focus_key = app
87            .get_first_keybinding(KeyBindingEnum::PrvFocus)
88            .unwrap_or("".to_string());
89        let accept_key = app
90            .get_first_keybinding(KeyBindingEnum::Accept)
91            .unwrap_or("".to_string());
92        let cancel_key = app
93            .get_first_keybinding(KeyBindingEnum::GoToPreviousViewOrCancel)
94            .unwrap_or("".to_string());
95
96        let edit_keybinding_help_spans = Line::from(vec![
97            Span::styled("Use ", help_text_style),
98            Span::styled(up_key, help_key_style),
99            Span::styled(" and ", help_text_style),
100            Span::styled(down_key, help_key_style),
101            Span::styled(" or scroll with the mouse", help_text_style),
102            Span::styled(" to select a keybinding, Press ", help_text_style),
103            Span::styled(accept_key.clone(), help_key_style),
104            Span::styled(" or ", help_text_style),
105            Span::styled("<Mouse Left Click>", help_key_style),
106            Span::styled(" to edit, ", help_text_style),
107            Span::styled(cancel_key, help_key_style),
108            Span::styled(
109                " to cancel, To Reset Keybindings to Default Press ",
110                help_text_style,
111            ),
112            Span::styled(next_focus_key, help_key_style),
113            Span::styled(" or ", help_text_style),
114            Span::styled(prv_focus_key, help_key_style),
115            Span::styled(" to highlight Reset Button and Press ", help_text_style),
116            Span::styled(accept_key, help_key_style),
117            Span::styled(" on the Reset Keybindings Button", help_text_style),
118        ]);
119
120        let mut table_items: Vec<Vec<String>> = Vec::new();
121        let keybindings = app.config.keybindings.clone();
122        for (key, value) in keybindings.iter() {
123            let mut row: Vec<String> = Vec::new();
124            row.push(keybindings.keybinding_enum_to_action(key).to_string());
125            let mut row_value = String::new();
126            for v in value.iter() {
127                row_value.push_str(&v.to_string());
128                // check if it's the last element
129                if value.iter().last().unwrap() != v {
130                    row_value.push_str(", ");
131                }
132            }
133            row.push(row_value);
134            table_items.push(row);
135        }
136        // sort according to the first string in the row
137        table_items.sort_by(|a, b| a[0].cmp(&b[0]));
138
139        let rows = table_items.iter().map(|item| {
140            let height = item
141                .iter()
142                .map(|content| content.chars().filter(|c| *c == '\n').count())
143                .max()
144                .unwrap_or(0)
145                + 1;
146            let cells = item.iter().map(|c| Cell::from(c.to_string()));
147            Row::new(cells).height(height as u16)
148        });
149
150        let current_index = app
151            .state
152            .app_table_states
153            .edit_keybindings
154            .selected()
155            .unwrap_or(0);
156        let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
157            .begin_symbol(SCROLLBAR_BEGIN_SYMBOL)
158            .style(scrollbar_style)
159            .end_symbol(SCROLLBAR_END_SYMBOL)
160            .track_symbol(SCROLLBAR_TRACK_SYMBOL)
161            .track_style(app.current_theme.inactive_text_style);
162        let mut scrollbar_state = ScrollbarState::new(table_items.len()).position(current_index);
163        let scrollbar_area = chunks[1].inner(Margin {
164            vertical: 1,
165            horizontal: 0,
166        });
167
168        let t = Table::new(rows, [Constraint::Fill(1), Constraint::Fill(1)])
169            .block(
170                Block::default()
171                    .title("Edit Keybindings")
172                    .style(default_style)
173                    .border_style(table_border_style)
174                    .borders(Borders::ALL)
175                    .border_type(BorderType::Rounded),
176            )
177            .row_highlight_style(current_element_style)
178            .highlight_symbol(">> ");
179
180        let edit_keybinding_help = Paragraph::new(edit_keybinding_help_spans)
181            .block(
182                Block::default()
183                    .title("Help")
184                    .borders(Borders::ALL)
185                    .border_type(BorderType::Rounded),
186            )
187            .style(default_style)
188            .alignment(Alignment::Center)
189            .wrap(ratatui::widgets::Wrap { trim: true });
190
191        let reset_button = Paragraph::new("Reset Keybindings to Default")
192            .block(
193                Block::default()
194                    .title("Reset")
195                    .borders(Borders::ALL)
196                    .border_type(BorderType::Rounded),
197            )
198            .style(reset_style)
199            .alignment(Alignment::Center);
200
201        rect.render_widget(draw_title(app, chunks[0], is_active), chunks[0]);
202        rect.render_stateful_widget(
203            t,
204            chunks[1],
205            &mut app.state.app_table_states.edit_keybindings,
206        );
207        rect.render_stateful_widget(scrollbar, scrollbar_area, &mut scrollbar_state);
208        rect.render_widget(edit_keybinding_help, chunks[2]);
209        rect.render_widget(reset_button, chunks[3]);
210        if app.config.enable_mouse_support {
211            render_close_button(rect, app, is_active)
212        }
213    }
214}