fresh/app/
keybinding_editor_actions.rs1use super::keybinding_editor::KeybindingEditor;
6use super::Editor;
7use crate::input::handler::InputResult;
8use crate::view::keybinding_editor::{handle_keybinding_editor_input, KeybindingEditorAction};
9use crate::view::ui::point_in_rect;
10use crossterm::event::{KeyEvent, MouseButton, MouseEvent, MouseEventKind};
11
12impl Editor {
13 pub fn open_keybinding_editor(&mut self) {
15 use crate::config::MenuExt;
16 let config_path = self.dir_context.config_path().display().to_string();
17 let cmd_registry = self.command_registry.read().unwrap();
18 let keybindings = self.keybindings.read().unwrap();
19 let menu_names: Vec<String> = self
23 .menus
24 .menus
25 .iter()
26 .chain(self.menu_state.plugin_menus.iter())
27 .map(|m| m.match_id().to_string())
28 .collect();
29 self.keybinding_editor = Some(KeybindingEditor::new(
30 &self.config,
31 &keybindings,
32 &self.mode_registry,
33 &cmd_registry,
34 config_path,
35 &menu_names,
36 ));
37 }
38
39 pub fn handle_keybinding_editor_input(&mut self, event: &KeyEvent) -> InputResult {
41 let mut editor = match self.keybinding_editor.take() {
42 Some(e) => e,
43 None => return InputResult::Ignored,
44 };
45
46 let action = handle_keybinding_editor_input(&mut editor, event);
47
48 match action {
49 KeybindingEditorAction::Consumed => {
50 self.keybinding_editor = Some(editor);
51 InputResult::Consumed
52 }
53 KeybindingEditorAction::Close => {
54 self.set_status_message("Keybinding editor closed".to_string());
56 InputResult::Consumed
57 }
58 KeybindingEditorAction::SaveAndClose => {
59 self.save_keybinding_editor_changes(&editor);
61 InputResult::Consumed
62 }
63 KeybindingEditorAction::StatusMessage(msg) => {
64 self.set_status_message(msg);
65 self.keybinding_editor = Some(editor);
66 InputResult::Consumed
67 }
68 }
69 }
70
71 fn save_keybinding_editor_changes(&mut self, editor: &KeybindingEditor) {
73 if !editor.has_changes {
74 return;
75 }
76
77 for remove in editor.get_pending_removes() {
79 self.config_mut().keybindings.retain(|kb| {
80 !(kb.action == remove.action
81 && kb.key == remove.key
82 && kb.modifiers == remove.modifiers
83 && kb.when == remove.when)
84 });
85 }
86
87 let new_bindings = editor.get_custom_bindings();
89 for binding in new_bindings {
90 self.config_mut().keybindings.push(binding);
91 }
92
93 self.keybindings
96 .write()
97 .unwrap()
98 .reload_from_config(&self.config);
99
100 let config_value = match serde_json::to_value(&self.config.keybindings) {
102 Ok(v) => v,
103 Err(e) => {
104 self.set_status_message(format!("Failed to serialize keybindings: {}", e));
105 return;
106 }
107 };
108
109 let mut changes = std::collections::HashMap::new();
110 changes.insert("/keybindings".to_string(), config_value);
111
112 let resolver = crate::config_io::ConfigResolver::new(
113 self.dir_context.clone(),
114 self.working_dir().to_path_buf(),
115 );
116
117 match resolver.save_changes_to_layer(
118 &changes,
119 &std::collections::HashSet::new(),
120 crate::config_io::ConfigLayer::User,
121 ) {
122 Ok(()) => {
123 self.set_status_message("Keybinding changes saved".to_string());
124 }
125 Err(e) => {
126 self.set_status_message(format!("Failed to save keybindings: {}", e));
127 }
128 }
129 }
130
131 pub fn is_keybinding_editor_active(&self) -> bool {
133 self.keybinding_editor.is_some()
134 }
135
136 pub fn handle_keybinding_editor_mouse(
139 &mut self,
140 mouse_event: MouseEvent,
141 ) -> anyhow::Result<bool> {
142 let mut editor = match self.keybinding_editor.take() {
143 Some(e) => e,
144 None => return Ok(false),
145 };
146
147 let col = mouse_event.column;
148 let row = mouse_event.row;
149 let layout = &editor.layout;
150
151 if !point_in_rect(layout.modal_area, col, row) {
154 self.keybinding_editor = Some(editor);
155 return Ok(false);
156 }
157
158 match mouse_event.kind {
159 MouseEventKind::ScrollUp => {
160 if editor.edit_dialog.is_none() && !editor.showing_confirm_dialog {
165 editor.scroll.scroll_by(-3);
166 }
167 }
168 MouseEventKind::ScrollDown => {
169 if editor.edit_dialog.is_none() && !editor.showing_confirm_dialog {
170 editor.scroll.scroll_by(3);
171 }
172 }
173 MouseEventKind::Drag(MouseButton::Left) => {
174 if let Some(sb) = editor.layout.table_scrollbar {
178 let sb_state = scrollbar_state_for(&editor);
179 if let Some(new_offset) = editor.scrollbar_mouse.drag(sb_state, sb, row) {
180 editor.scroll.offset = new_offset as u16;
181 }
182 }
183 }
184 MouseEventKind::Up(MouseButton::Left) => {
185 editor.scrollbar_mouse.release();
186 }
187 MouseEventKind::Down(MouseButton::Left) => {
188 if editor.showing_confirm_dialog {
190 if let Some((save_r, discard_r, cancel_r)) = layout.confirm_buttons {
191 if point_in_rect(save_r, col, row) {
192 self.save_keybinding_editor_changes(&editor);
193 return Ok(true);
194 } else if point_in_rect(discard_r, col, row) {
195 self.set_status_message("Keybinding editor closed".to_string());
196 return Ok(true);
197 } else if point_in_rect(cancel_r, col, row) {
198 editor.showing_confirm_dialog = false;
199 }
200 }
201 self.keybinding_editor = Some(editor);
202 return Ok(true);
203 }
204
205 if editor.edit_dialog.is_some() {
207 if let Some((save_r, cancel_r)) = layout.dialog_buttons {
209 if point_in_rect(save_r, col, row) {
210 if let Some(err) = editor.apply_edit_dialog() {
212 self.set_status_message(err);
213 }
214 self.keybinding_editor = Some(editor);
215 return Ok(true);
216 } else if point_in_rect(cancel_r, col, row) {
217 editor.edit_dialog = None;
219 self.keybinding_editor = Some(editor);
220 return Ok(true);
221 }
222 }
223 if let Some(r) = layout.dialog_key_field {
225 if point_in_rect(r, col, row) {
226 if let Some(ref mut dialog) = editor.edit_dialog {
227 dialog.focus_area = 0;
228 dialog.mode = crate::app::keybinding_editor::EditMode::RecordingKey;
229 }
230 }
231 }
232 if let Some(r) = layout.dialog_action_field {
233 if point_in_rect(r, col, row) {
234 if let Some(ref mut dialog) = editor.edit_dialog {
235 dialog.focus_area = 1;
236 dialog.mode =
237 crate::app::keybinding_editor::EditMode::EditingAction;
238 }
239 }
240 }
241 if let Some(r) = layout.dialog_context_field {
242 if point_in_rect(r, col, row) {
243 if let Some(ref mut dialog) = editor.edit_dialog {
244 dialog.focus_area = 2;
245 dialog.mode =
246 crate::app::keybinding_editor::EditMode::EditingContext;
247 }
248 }
249 }
250 self.keybinding_editor = Some(editor);
251 return Ok(true);
252 }
253
254 if let Some(search_r) = layout.search_bar {
256 if point_in_rect(search_r, col, row) {
257 editor.start_search();
258 self.keybinding_editor = Some(editor);
259 return Ok(true);
260 }
261 }
262
263 if let Some(sb) = layout.table_scrollbar {
269 let sb_state = scrollbar_state_for(&editor);
270 if let Some(new_offset) = editor.scrollbar_mouse.press(sb_state, sb, col, row) {
271 editor.scroll.offset = new_offset as u16;
272 self.keybinding_editor = Some(editor);
273 return Ok(true);
274 }
275 }
276
277 let table_area = layout.table_area;
279 let first_row_y = layout.table_first_row_y;
280 if point_in_rect(table_area, col, row) && row >= first_row_y {
281 let clicked_row = (row - first_row_y) as usize;
282 let new_selected = editor.scroll.offset as usize + clicked_row;
283 if new_selected < editor.display_rows.len() {
284 editor.selected = new_selected;
285 if editor.selected_is_section_header() {
286 editor.toggle_section_at_selected();
287 }
288 }
289 }
290 }
291 _ => {}
292 }
293
294 self.keybinding_editor = Some(editor);
295 Ok(true)
296 }
297
298 pub(crate) fn kbedit_select_display_row(&mut self, idx: usize) {
302 if let Some(ed) = self.keybinding_editor.as_mut() {
303 if idx < ed.display_rows.len() {
304 ed.selected = idx;
305 if ed.selected_is_section_header() {
306 ed.toggle_section_at_selected();
307 }
308 }
309 }
310 }
311}
312
313fn scrollbar_state_for(editor: &KeybindingEditor) -> crate::view::ui::scrollbar::ScrollbarState {
316 crate::view::ui::scrollbar::ScrollbarState::new(
317 editor.scroll.content_height as usize,
318 editor.scroll.viewport as usize,
319 editor.scroll.offset as usize,
320 )
321}