1use rustc_hash::FxHashMap;
2use std::fmt::Display;
3
4use crate::{colors::Colorscheme, mode::Mode};
5use ratatui::{
6 layout::Constraint,
7 style::{Color, Style},
8 text::{Line, Span},
9 widgets::{Cell, Row, Table},
10};
11
12#[derive(Debug, Clone)]
13pub struct DisplayableKeybindings {
14 bindings: FxHashMap<DisplayableAction, Vec<String>>,
15}
16
17impl DisplayableKeybindings {
18 pub fn new(bindings: FxHashMap<DisplayableAction, Vec<String>>) -> Self {
19 Self { bindings }
20 }
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24pub enum DisplayableAction {
25 ResultsNavigation,
26 PreviewNavigation,
27 SelectEntry,
28 CopyEntryToClipboard,
29 SendToChannel,
30 ToggleRemoteControl,
31 Cancel,
32 Quit,
33 ToggleHelpBar,
34}
35
36impl Display for DisplayableAction {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 let action = match self {
39 DisplayableAction::ResultsNavigation => "Results navigation",
40 DisplayableAction::PreviewNavigation => "Preview navigation",
41 DisplayableAction::SelectEntry => "Select entry",
42 DisplayableAction::CopyEntryToClipboard => {
43 "Copy entry to clipboard"
44 }
45 DisplayableAction::SendToChannel => "Send to channel",
46 DisplayableAction::ToggleRemoteControl => "Toggle Remote control",
47 DisplayableAction::Cancel => "Cancel",
48 DisplayableAction::Quit => "Quit",
49 DisplayableAction::ToggleHelpBar => "Toggle help bar",
50 };
51 write!(f, "{action}")
52 }
53}
54
55pub fn build_keybindings_table<'a>(
56 keybindings: &'a FxHashMap<Mode, DisplayableKeybindings>,
57 mode: Mode,
58 colorscheme: &'a Colorscheme,
59) -> Table<'a> {
60 match mode {
61 Mode::Channel => build_keybindings_table_for_channel(
62 &keybindings[&mode],
63 colorscheme,
64 ),
65 Mode::RemoteControl => build_keybindings_table_for_channel_selection(
66 &keybindings[&mode],
67 colorscheme,
68 ),
69 Mode::SendToChannel => {
70 build_keybindings_table_for_channel_transitions(
71 &keybindings[&mode],
72 colorscheme,
73 )
74 }
75 }
76}
77
78fn build_keybindings_table_for_channel<'a>(
79 keybindings: &'a DisplayableKeybindings,
80 colorscheme: &'a Colorscheme,
81) -> Table<'a> {
82 let results_navigation_keys = keybindings
84 .bindings
85 .get(&DisplayableAction::ResultsNavigation)
86 .unwrap();
87 let results_row = Row::new(build_cells_for_group(
88 "Results navigation",
89 results_navigation_keys,
90 colorscheme.help.metadata_field_name_fg,
91 colorscheme.mode.channel,
92 ));
93
94 let preview_navigation_keys = keybindings
96 .bindings
97 .get(&DisplayableAction::PreviewNavigation)
98 .unwrap();
99 let preview_row = Row::new(build_cells_for_group(
100 "Preview navigation",
101 preview_navigation_keys,
102 colorscheme.help.metadata_field_name_fg,
103 colorscheme.mode.channel,
104 ));
105
106 let select_entry_keys = keybindings
108 .bindings
109 .get(&DisplayableAction::SelectEntry)
110 .unwrap();
111 let select_entry_row = Row::new(build_cells_for_group(
112 "Select entry",
113 select_entry_keys,
114 colorscheme.help.metadata_field_name_fg,
115 colorscheme.mode.channel,
116 ));
117
118 let copy_entry_keys = keybindings
120 .bindings
121 .get(&DisplayableAction::CopyEntryToClipboard)
122 .unwrap();
123 let copy_entry_row = Row::new(build_cells_for_group(
124 "Copy entry to clipboard",
125 copy_entry_keys,
126 colorscheme.help.metadata_field_name_fg,
127 colorscheme.mode.channel,
128 ));
129
130 let send_to_channel_keys = keybindings
132 .bindings
133 .get(&DisplayableAction::SendToChannel)
134 .unwrap();
135 let send_to_channel_row = Row::new(build_cells_for_group(
136 "Send results to",
137 send_to_channel_keys,
138 colorscheme.help.metadata_field_name_fg,
139 colorscheme.mode.channel,
140 ));
141
142 let switch_channels_keys = keybindings
144 .bindings
145 .get(&DisplayableAction::ToggleRemoteControl)
146 .unwrap();
147 let switch_channels_row = Row::new(build_cells_for_group(
148 "Toggle Remote control",
149 switch_channels_keys,
150 colorscheme.help.metadata_field_name_fg,
151 colorscheme.mode.channel,
152 ));
153
154 let widths = vec![Constraint::Fill(1), Constraint::Fill(2)];
155
156 Table::new(
157 vec![
158 results_row,
159 preview_row,
160 select_entry_row,
161 copy_entry_row,
162 send_to_channel_row,
163 switch_channels_row,
164 ],
165 widths,
166 )
167}
168
169fn build_keybindings_table_for_channel_selection<'a>(
170 keybindings: &'a DisplayableKeybindings,
171 colorscheme: &'a Colorscheme,
172) -> Table<'a> {
173 let navigation_keys = keybindings
175 .bindings
176 .get(&DisplayableAction::ResultsNavigation)
177 .unwrap();
178 let results_row = Row::new(build_cells_for_group(
179 "Browse channels",
180 navigation_keys,
181 colorscheme.help.metadata_field_name_fg,
182 colorscheme.mode.remote_control,
183 ));
184
185 let select_entry_keys = keybindings
187 .bindings
188 .get(&DisplayableAction::SelectEntry)
189 .unwrap();
190 let select_entry_row = Row::new(build_cells_for_group(
191 "Select channel",
192 select_entry_keys,
193 colorscheme.help.metadata_field_name_fg,
194 colorscheme.mode.remote_control,
195 ));
196
197 let switch_channels_keys = keybindings
199 .bindings
200 .get(&DisplayableAction::ToggleRemoteControl)
201 .unwrap();
202 let switch_channels_row = Row::new(build_cells_for_group(
203 "Toggle Remote control",
204 switch_channels_keys,
205 colorscheme.help.metadata_field_name_fg,
206 colorscheme.mode.remote_control,
207 ));
208
209 Table::new(
210 vec![results_row, select_entry_row, switch_channels_row],
211 vec![Constraint::Fill(1), Constraint::Fill(2)],
212 )
213}
214
215fn build_keybindings_table_for_channel_transitions<'a>(
216 keybindings: &'a DisplayableKeybindings,
217 colorscheme: &'a Colorscheme,
218) -> Table<'a> {
219 let results_navigation_keys = keybindings
221 .bindings
222 .get(&DisplayableAction::ResultsNavigation)
223 .unwrap();
224 let results_row = Row::new(build_cells_for_group(
225 "Browse channels",
226 results_navigation_keys,
227 colorscheme.help.metadata_field_name_fg,
228 colorscheme.mode.send_to_channel,
229 ));
230
231 let select_entry_keys = keybindings
233 .bindings
234 .get(&DisplayableAction::SelectEntry)
235 .unwrap();
236 let select_entry_row = Row::new(build_cells_for_group(
237 "Send to channel",
238 select_entry_keys,
239 colorscheme.help.metadata_field_name_fg,
240 colorscheme.mode.send_to_channel,
241 ));
242
243 let cancel_keys = keybindings
245 .bindings
246 .get(&DisplayableAction::Cancel)
247 .unwrap();
248 let cancel_row = Row::new(build_cells_for_group(
249 "Cancel",
250 cancel_keys,
251 colorscheme.help.metadata_field_name_fg,
252 colorscheme.mode.send_to_channel,
253 ));
254
255 Table::new(
256 vec![results_row, select_entry_row, cancel_row],
257 vec![Constraint::Fill(1), Constraint::Fill(2)],
258 )
259}
260
261fn build_cells_for_group<'a>(
262 group_name: &str,
263 keys: &'a [String],
264 key_color: Color,
265 value_color: Color,
266) -> Vec<Cell<'a>> {
267 let mut cells = vec![Cell::from(Span::styled(
269 group_name.to_owned() + ": ",
270 Style::default().fg(key_color),
271 ))];
272
273 let spans = keys.iter().skip(1).fold(
274 vec![Span::styled(
275 keys[0].clone(),
276 Style::default().fg(value_color),
277 )],
278 |mut acc, key| {
279 acc.push(Span::raw(" / "));
280 acc.push(Span::styled(
281 key.to_owned(),
282 Style::default().fg(value_color),
283 ));
284 acc
285 },
286 );
287
288 cells.push(Cell::from(Line::from(spans)));
289
290 cells
291}