libsurfer/
keys.rs

1//! Keyboard handling.
2use egui::{Context, Event, Key, Modifiers};
3use emath::Vec2;
4
5use crate::config::ArrowKeyBindings;
6use crate::message::MessageTarget;
7use crate::{
8    message::Message,
9    wave_data::{PER_SCROLL_EVENT, SCROLL_EVENTS_PER_PAGE},
10    MoveDir, SystemState,
11};
12
13impl SystemState {
14    pub fn handle_pressed_keys(&self, ctx: &Context, msgs: &mut Vec<Message>) {
15        ctx.input(|i| {
16            i.events.iter().for_each(|event| match event {
17                Event::Key {
18                    key,
19                    repeat: _,
20                    pressed,
21                    modifiers,
22                    physical_key: _,
23                } => match (
24                    key,
25                    pressed,
26                    self.command_prompt.visible,
27                    self.user.variable_name_filter_focused,
28                ) {
29                    (Key::Num0, true, false, false) => {
30                        handle_digit(0, modifiers, msgs);
31                    }
32                    (Key::Num1, true, false, false) => {
33                        handle_digit(1, modifiers, msgs);
34                    }
35                    (Key::Num2, true, false, false) => {
36                        handle_digit(2, modifiers, msgs);
37                    }
38                    (Key::Num3, true, false, false) => {
39                        handle_digit(3, modifiers, msgs);
40                    }
41                    (Key::Num4, true, false, false) => {
42                        handle_digit(4, modifiers, msgs);
43                    }
44                    (Key::Num5, true, false, false) => {
45                        handle_digit(5, modifiers, msgs);
46                    }
47                    (Key::Num6, true, false, false) => {
48                        handle_digit(6, modifiers, msgs);
49                    }
50                    (Key::Num7, true, false, false) => {
51                        handle_digit(7, modifiers, msgs);
52                    }
53                    (Key::Num8, true, false, false) => {
54                        handle_digit(8, modifiers, msgs);
55                    }
56                    (Key::Num9, true, false, false) => {
57                        handle_digit(9, modifiers, msgs);
58                    }
59                    (Key::Home, true, false, false) => msgs.push(Message::ScrollToItem(0)),
60                    (Key::End, true, false, false) => {
61                        if let Some(waves) = &self.user.waves {
62                            if waves.displayed_items.len() > 1 {
63                                msgs.push(Message::ScrollToItem(waves.displayed_items.len() - 1));
64                            }
65                        }
66                    }
67                    (Key::Space, true, false, false) => {
68                        msgs.push(Message::ShowCommandPrompt(Some("".to_string())))
69                    }
70                    (Key::Escape, true, true, false) => msgs.push(Message::ShowCommandPrompt(None)),
71                    (Key::G, true, true, false) => {
72                        if modifiers.command {
73                            msgs.push(Message::ShowCommandPrompt(None))
74                        }
75                    }
76                    (Key::G, true, false, false) => msgs.push(Message::GroupNew {
77                        name: None,
78                        before: None,
79                        items: None,
80                    }),
81                    (Key::Escape, true, false, false) => {
82                        msgs.push(Message::InvalidateCount);
83                        msgs.push(Message::ItemSelectionClear);
84                    }
85                    (Key::Escape, true, _, true) => msgs.push(Message::SetFilterFocused(false)),
86                    (Key::B, true, false, false) => msgs.push(Message::ToggleSidePanel),
87                    (Key::M, true, false, false) => {
88                        if modifiers.alt {
89                            msgs.push(Message::ToggleMenu)
90                        } else if let Some(waves) = self.user.waves.as_ref() {
91                            if let Some(cursor) = waves.cursor.as_ref() {
92                                // Check if a marker already exists at the cursor position
93                                let marker_exists = waves
94                                    .markers
95                                    .values()
96                                    .any(|marker_time| marker_time == cursor);
97                                if !marker_exists {
98                                    msgs.push(Message::AddMarker {
99                                        time: cursor.clone(),
100                                        name: None,
101                                        move_focus: self
102                                            .user
103                                            .config
104                                            .layout
105                                            .move_focus_on_inserted_marker(),
106                                    });
107                                }
108                            }
109                        }
110                    }
111                    (Key::T, true, false, false) => msgs.push(Message::ToggleToolbar),
112                    (Key::F11, true, false, _) => msgs.push(Message::ToggleFullscreen),
113                    (Key::U, true, false, false) => {
114                        if modifiers.shift {
115                            msgs.push(Message::Redo(self.get_count()));
116                        } else {
117                            msgs.push(Message::Undo(self.get_count()));
118                        }
119                    }
120                    (Key::Z, true, false, false) => {
121                        if modifiers.ctrl {
122                            msgs.push(Message::Undo(self.get_count()));
123                        }
124                    }
125                    (Key::Y, true, false, false) => {
126                        if modifiers.ctrl {
127                            msgs.push(Message::Redo(self.get_count()));
128                        }
129                    }
130                    (Key::F, true, false, false) => {
131                        msgs.push(Message::ShowCommandPrompt(Some("item_focus ".to_string())))
132                    }
133                    (Key::S, true, false, false) => {
134                        if modifiers.command {
135                            msgs.push(Message::SaveStateFile(self.user.state_file.clone()));
136                        } else {
137                            msgs.push(Message::GoToStart { viewport_idx: 0 });
138                        }
139                    }
140                    (Key::A, true, false, false) => {
141                        if modifiers.command {
142                            msgs.push(Message::ItemSelectAll);
143                        } else {
144                            msgs.push(Message::ToggleItemSelected(None));
145                        }
146                    }
147                    (Key::E, true, false, false) => msgs.push(Message::GoToEnd { viewport_idx: 0 }),
148                    (Key::R, true, false, false) => msgs.push(Message::ReloadWaveform(
149                        self.user.config.behavior.keep_during_reload,
150                    )),
151                    (Key::H, true, false, false) => msgs.push(Message::MoveCursorToTransition {
152                        next: false,
153                        variable: None,
154                        skip_zero: modifiers.shift,
155                    }),
156                    (Key::L, true, false, false) => msgs.push(Message::MoveCursorToTransition {
157                        next: true,
158                        variable: None,
159                        skip_zero: modifiers.shift,
160                    }),
161                    (Key::Minus, true, false, false) => msgs.push(Message::CanvasZoom {
162                        mouse_ptr: None,
163                        delta: 2.0,
164                        viewport_idx: 0,
165                    }),
166                    (Key::Plus | Key::Equals, true, false, false) => {
167                        msgs.push(Message::CanvasZoom {
168                            mouse_ptr: None,
169                            delta: 0.5,
170                            viewport_idx: 0,
171                        });
172                    }
173                    (Key::PageUp, true, false, false) => msgs.push(Message::CanvasScroll {
174                        delta: Vec2 {
175                            x: 0.,
176                            y: -PER_SCROLL_EVENT * SCROLL_EVENTS_PER_PAGE,
177                        },
178                        viewport_idx: 0,
179                    }),
180                    (Key::PageDown, true, false, false) => msgs.push(Message::CanvasScroll {
181                        delta: Vec2 {
182                            x: 0.,
183                            y: PER_SCROLL_EVENT * SCROLL_EVENTS_PER_PAGE,
184                        },
185                        viewport_idx: 0,
186                    }),
187                    (Key::ArrowRight, true, false, false) => {
188                        msgs.push(match self.user.config.behavior.arrow_key_bindings {
189                            ArrowKeyBindings::Edge => Message::MoveCursorToTransition {
190                                next: true,
191                                variable: None,
192                                skip_zero: modifiers.shift,
193                            },
194                            ArrowKeyBindings::Scroll => Message::CanvasScroll {
195                                delta: Vec2 {
196                                    x: 0.,
197                                    y: -PER_SCROLL_EVENT,
198                                },
199                                viewport_idx: 0,
200                            },
201                        });
202                    }
203                    (Key::ArrowLeft, true, false, false) => {
204                        msgs.push(match self.user.config.behavior.arrow_key_bindings {
205                            ArrowKeyBindings::Edge => Message::MoveCursorToTransition {
206                                next: false,
207                                variable: None,
208                                skip_zero: modifiers.shift,
209                            },
210                            ArrowKeyBindings::Scroll => Message::CanvasScroll {
211                                delta: Vec2 {
212                                    x: 0.,
213                                    y: PER_SCROLL_EVENT,
214                                },
215                                viewport_idx: 0,
216                            },
217                        });
218                    }
219                    (Key::J, true, false, false) => {
220                        if modifiers.alt {
221                            msgs.push(Message::MoveFocus(
222                                MoveDir::Down,
223                                self.get_count(),
224                                modifiers.shift,
225                            ));
226                        } else if modifiers.command {
227                            msgs.push(Message::MoveFocusedItem(MoveDir::Down, self.get_count()));
228                        } else {
229                            msgs.push(Message::VerticalScroll(MoveDir::Down, self.get_count()));
230                        }
231                        msgs.push(Message::InvalidateCount);
232                    }
233                    (Key::K, true, false, false) => {
234                        if modifiers.alt {
235                            msgs.push(Message::MoveFocus(
236                                MoveDir::Up,
237                                self.get_count(),
238                                modifiers.shift,
239                            ));
240                        } else if modifiers.command {
241                            msgs.push(Message::MoveFocusedItem(MoveDir::Up, self.get_count()));
242                        } else {
243                            msgs.push(Message::VerticalScroll(MoveDir::Up, self.get_count()));
244                        }
245                        msgs.push(Message::InvalidateCount);
246                    }
247                    (Key::ArrowDown, true, false, false) => {
248                        if modifiers.alt {
249                            msgs.push(Message::MoveFocus(
250                                MoveDir::Down,
251                                self.get_count(),
252                                modifiers.shift,
253                            ));
254                        } else if modifiers.command {
255                            msgs.push(Message::MoveFocusedItem(MoveDir::Down, self.get_count()));
256                        } else {
257                            msgs.push(Message::VerticalScroll(MoveDir::Down, self.get_count()));
258                        }
259                        msgs.push(Message::InvalidateCount);
260                    }
261                    (Key::ArrowUp, true, false, false) => {
262                        if modifiers.alt {
263                            msgs.push(Message::MoveFocus(
264                                MoveDir::Up,
265                                self.get_count(),
266                                modifiers.shift,
267                            ));
268                        } else if modifiers.command {
269                            msgs.push(Message::MoveFocusedItem(MoveDir::Up, self.get_count()));
270                        } else {
271                            msgs.push(Message::VerticalScroll(MoveDir::Up, self.get_count()));
272                        }
273                        msgs.push(Message::InvalidateCount);
274                    }
275                    (Key::Delete | Key::X, true, false, false) => {
276                        if let Some(waves) = &self.user.waves {
277                            let mut remove_ids = waves
278                                .items_tree
279                                .iter_visible_selected()
280                                .map(|i| i.item_ref)
281                                .collect::<Vec<_>>();
282                            if let Some(node) = waves
283                                .focused_item
284                                .and_then(|focus| waves.items_tree.get_visible(focus))
285                            {
286                                remove_ids.push(node.item_ref)
287                            }
288
289                            msgs.push(Message::RemoveItems(remove_ids));
290                        }
291                    }
292                    (Key::ArrowUp, true, true, false) => msgs.push(Message::SelectPrevCommand),
293                    (Key::P, true, true, false) => {
294                        if modifiers.command {
295                            msgs.push(Message::SelectPrevCommand);
296                        }
297                    }
298                    (Key::ArrowDown, true, true, false) => msgs.push(Message::SelectNextCommand),
299                    (Key::N, true, true, false) => {
300                        if modifiers.command {
301                            msgs.push(Message::SelectNextCommand);
302                        }
303                    }
304                    _ => {}
305                },
306                Event::Copy => msgs.push(Message::VariableValueToClipbord(
307                    MessageTarget::CurrentSelection,
308                )),
309                _ => {}
310            });
311        });
312    }
313
314    pub fn get_count(&self) -> usize {
315        if let Some(count) = &self.user.count {
316            count.parse::<usize>().unwrap_or(1)
317        } else {
318            1
319        }
320    }
321}
322
323fn handle_digit(digit: u8, modifiers: &Modifiers, msgs: &mut Vec<Message>) {
324    if modifiers.alt {
325        msgs.push(Message::AddCount((digit + 48) as char));
326    } else if modifiers.command {
327        msgs.push(Message::MoveMarkerToCursor(digit));
328    } else {
329        msgs.push(Message::GoToMarkerPosition(digit, 0));
330    }
331}