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.write().unwrap() =
95 crate::input::keybindings::KeybindingResolver::new(&self.config);
96
97 let config_value = match serde_json::to_value(&self.config.keybindings) {
99 Ok(v) => v,
100 Err(e) => {
101 self.set_status_message(format!("Failed to serialize keybindings: {}", e));
102 return;
103 }
104 };
105
106 let mut changes = std::collections::HashMap::new();
107 changes.insert("/keybindings".to_string(), config_value);
108
109 let resolver = crate::config_io::ConfigResolver::new(
110 self.dir_context.clone(),
111 self.working_dir.clone(),
112 );
113
114 match resolver.save_changes_to_layer(
115 &changes,
116 &std::collections::HashSet::new(),
117 crate::config_io::ConfigLayer::User,
118 ) {
119 Ok(()) => {
120 self.set_status_message("Keybinding changes saved".to_string());
121 }
122 Err(e) => {
123 self.set_status_message(format!("Failed to save keybindings: {}", e));
124 }
125 }
126 }
127
128 pub fn is_keybinding_editor_active(&self) -> bool {
130 self.keybinding_editor.is_some()
131 }
132
133 pub fn handle_keybinding_editor_mouse(
136 &mut self,
137 mouse_event: MouseEvent,
138 ) -> anyhow::Result<bool> {
139 let mut editor = match self.keybinding_editor.take() {
140 Some(e) => e,
141 None => return Ok(false),
142 };
143
144 let col = mouse_event.column;
145 let row = mouse_event.row;
146 let layout = &editor.layout;
147
148 if !point_in_rect(layout.modal_area, col, row) {
151 self.keybinding_editor = Some(editor);
152 return Ok(false);
153 }
154
155 match mouse_event.kind {
156 MouseEventKind::ScrollUp => {
157 if editor.edit_dialog.is_none() && !editor.showing_confirm_dialog {
162 editor.scroll.scroll_by(-3);
163 }
164 }
165 MouseEventKind::ScrollDown => {
166 if editor.edit_dialog.is_none() && !editor.showing_confirm_dialog {
167 editor.scroll.scroll_by(3);
168 }
169 }
170 MouseEventKind::Drag(MouseButton::Left) => {
171 if let Some(sb) = editor.layout.table_scrollbar {
175 let sb_state = scrollbar_state_for(&editor);
176 if let Some(new_offset) = editor.scrollbar_mouse.drag(sb_state, sb, row) {
177 editor.scroll.offset = new_offset as u16;
178 }
179 }
180 }
181 MouseEventKind::Up(MouseButton::Left) => {
182 editor.scrollbar_mouse.release();
183 }
184 MouseEventKind::Down(MouseButton::Left) => {
185 if editor.showing_confirm_dialog {
187 if let Some((save_r, discard_r, cancel_r)) = layout.confirm_buttons {
188 if point_in_rect(save_r, col, row) {
189 self.save_keybinding_editor_changes(&editor);
190 return Ok(true);
191 } else if point_in_rect(discard_r, col, row) {
192 self.set_status_message("Keybinding editor closed".to_string());
193 return Ok(true);
194 } else if point_in_rect(cancel_r, col, row) {
195 editor.showing_confirm_dialog = false;
196 }
197 }
198 self.keybinding_editor = Some(editor);
199 return Ok(true);
200 }
201
202 if editor.edit_dialog.is_some() {
204 if let Some((save_r, cancel_r)) = layout.dialog_buttons {
206 if point_in_rect(save_r, col, row) {
207 if let Some(err) = editor.apply_edit_dialog() {
209 self.set_status_message(err);
210 }
211 self.keybinding_editor = Some(editor);
212 return Ok(true);
213 } else if point_in_rect(cancel_r, col, row) {
214 editor.edit_dialog = None;
216 self.keybinding_editor = Some(editor);
217 return Ok(true);
218 }
219 }
220 if let Some(r) = layout.dialog_key_field {
222 if point_in_rect(r, col, row) {
223 if let Some(ref mut dialog) = editor.edit_dialog {
224 dialog.focus_area = 0;
225 dialog.mode = crate::app::keybinding_editor::EditMode::RecordingKey;
226 }
227 }
228 }
229 if let Some(r) = layout.dialog_action_field {
230 if point_in_rect(r, col, row) {
231 if let Some(ref mut dialog) = editor.edit_dialog {
232 dialog.focus_area = 1;
233 dialog.mode =
234 crate::app::keybinding_editor::EditMode::EditingAction;
235 }
236 }
237 }
238 if let Some(r) = layout.dialog_context_field {
239 if point_in_rect(r, col, row) {
240 if let Some(ref mut dialog) = editor.edit_dialog {
241 dialog.focus_area = 2;
242 dialog.mode =
243 crate::app::keybinding_editor::EditMode::EditingContext;
244 }
245 }
246 }
247 self.keybinding_editor = Some(editor);
248 return Ok(true);
249 }
250
251 if let Some(search_r) = layout.search_bar {
253 if point_in_rect(search_r, col, row) {
254 editor.start_search();
255 self.keybinding_editor = Some(editor);
256 return Ok(true);
257 }
258 }
259
260 if let Some(sb) = layout.table_scrollbar {
266 let sb_state = scrollbar_state_for(&editor);
267 if let Some(new_offset) = editor.scrollbar_mouse.press(sb_state, sb, col, row) {
268 editor.scroll.offset = new_offset as u16;
269 self.keybinding_editor = Some(editor);
270 return Ok(true);
271 }
272 }
273
274 let table_area = layout.table_area;
276 let first_row_y = layout.table_first_row_y;
277 if point_in_rect(table_area, col, row) && row >= first_row_y {
278 let clicked_row = (row - first_row_y) as usize;
279 let new_selected = editor.scroll.offset as usize + clicked_row;
280 if new_selected < editor.display_rows.len() {
281 editor.selected = new_selected;
282 if editor.selected_is_section_header() {
283 editor.toggle_section_at_selected();
284 }
285 }
286 }
287 }
288 _ => {}
289 }
290
291 self.keybinding_editor = Some(editor);
292 Ok(true)
293 }
294}
295
296fn scrollbar_state_for(editor: &KeybindingEditor) -> crate::view::ui::scrollbar::ScrollbarState {
299 crate::view::ui::scrollbar::ScrollbarState::new(
300 editor.scroll.content_height as usize,
301 editor.scroll.viewport as usize,
302 editor.scroll.offset as usize,
303 )
304}