1use egui::{Context, Event, Key, Modifiers};
3use emath::Vec2;
4
5use crate::config::ArrowKeyBindings;
6use crate::message::MessageTarget;
7use crate::{MoveDir, SystemState, message::Message, wave_data::PER_SCROLL_EVENT};
8
9impl SystemState {
10 pub fn handle_pressed_keys(&self, ctx: &Context, msgs: &mut Vec<Message>) {
11 if !(self.command_prompt.visible
12 || self.user.variable_name_filter_focused
13 || self.time_edit_focused)
14 {
15 self.user.config.shortcuts.process(ctx, msgs, self);
16 }
17 ctx.input(|i| {
18 i.events.iter().for_each(|event| match event {
19 Event::Key {
20 key,
21 repeat: _,
22 pressed,
23 modifiers,
24 physical_key: _,
25 } => match (
26 key,
27 pressed,
28 self.command_prompt.visible,
29 self.user.variable_name_filter_focused || self.time_edit_focused,
30 ) {
31 (k, true, false, false)
33 if matches!(
34 k,
35 Key::Num0
36 | Key::Num1
37 | Key::Num2
38 | Key::Num3
39 | Key::Num4
40 | Key::Num5
41 | Key::Num6
42 | Key::Num7
43 | Key::Num8
44 | Key::Num9
45 ) =>
46 {
47 if let Some(d) = key_to_digit(k) {
48 handle_digit(d, modifiers, msgs);
49 }
50 }
51 (Key::Escape, true, true, false) => msgs.push(Message::HideCommandPrompt),
52 (Key::Escape, true, false, false) => {
53 msgs.push(Message::InvalidateCount);
54 msgs.push(Message::ItemSelectionClear);
55 }
56 (Key::Escape, true, _, true) => {
57 msgs.push(Message::SetTimeEditFocused(false));
58 msgs.push(Message::SetFilterFocused(false));
59 }
60 (Key::G, true, true, false) if modifiers.command => {
61 msgs.push(Message::HideCommandPrompt);
62 }
63 (Key::H, true, false, false) => msgs.push(Message::MoveCursorToTransition {
64 next: false,
65 variable: None,
66 skip_zero: modifiers.shift,
67 }),
68 (Key::J, true, false, false) => {
69 if modifiers.alt {
70 msgs.push(Message::MoveFocus(
71 MoveDir::Down,
72 self.get_count(),
73 modifiers.shift,
74 ));
75 } else if modifiers.command {
76 msgs.push(Message::MoveFocusedItem(MoveDir::Down, self.get_count()));
77 } else {
78 msgs.push(Message::VerticalScroll(MoveDir::Down, self.get_count()));
79 }
80 msgs.push(Message::InvalidateCount);
81 }
82 (Key::K, true, false, false) => {
83 if modifiers.alt {
84 msgs.push(Message::MoveFocus(
85 MoveDir::Up,
86 self.get_count(),
87 modifiers.shift,
88 ));
89 } else if modifiers.command {
90 msgs.push(Message::MoveFocusedItem(MoveDir::Up, self.get_count()));
91 } else {
92 msgs.push(Message::VerticalScroll(MoveDir::Up, self.get_count()));
93 }
94 msgs.push(Message::InvalidateCount);
95 }
96 (Key::L, true, false, false) => msgs.push(Message::MoveCursorToTransition {
97 next: true,
98 variable: None,
99 skip_zero: modifiers.shift,
100 }),
101 (Key::N, true, true, false) if modifiers.command => {
102 msgs.push(Message::SelectNextCommand);
103 }
104 (Key::P, true, true, false) if modifiers.command => {
105 msgs.push(Message::SelectPrevCommand);
106 }
107 (Key::F11, true, false, _) => msgs.push(Message::ToggleFullscreen),
108 (Key::ArrowRight, true, false, false) => {
109 msgs.push(match self.user.config.behavior.arrow_key_bindings() {
110 ArrowKeyBindings::Edge => Message::MoveCursorToTransition {
111 next: true,
112 variable: None,
113 skip_zero: modifiers.shift,
114 },
115 ArrowKeyBindings::Scroll => Message::CanvasScroll {
116 delta: Vec2 {
117 x: 0.,
118 y: -PER_SCROLL_EVENT,
119 },
120 viewport_idx: 0,
121 },
122 });
123 }
124 (Key::ArrowLeft, true, false, false) => {
125 msgs.push(match self.user.config.behavior.arrow_key_bindings() {
126 ArrowKeyBindings::Edge => Message::MoveCursorToTransition {
127 next: false,
128 variable: None,
129 skip_zero: modifiers.shift,
130 },
131 ArrowKeyBindings::Scroll => Message::CanvasScroll {
132 delta: Vec2 {
133 x: 0.,
134 y: PER_SCROLL_EVENT,
135 },
136 viewport_idx: 0,
137 },
138 });
139 }
140 (Key::ArrowDown, true, true, false) => msgs.push(Message::SelectNextCommand),
141 (Key::ArrowDown, true, false, false) => {
142 if modifiers.alt {
143 msgs.push(Message::MoveFocus(
144 MoveDir::Down,
145 self.get_count(),
146 modifiers.shift,
147 ));
148 } else if modifiers.command {
149 msgs.push(Message::MoveFocusedItem(MoveDir::Down, self.get_count()));
150 } else {
151 msgs.push(Message::VerticalScroll(MoveDir::Down, self.get_count()));
152 }
153 msgs.push(Message::InvalidateCount);
154 }
155 (Key::ArrowUp, true, true, false) => msgs.push(Message::SelectPrevCommand),
156 (Key::ArrowUp, true, false, false) => {
157 if modifiers.alt {
158 msgs.push(Message::MoveFocus(
159 MoveDir::Up,
160 self.get_count(),
161 modifiers.shift,
162 ));
163 } else if modifiers.command {
164 msgs.push(Message::MoveFocusedItem(MoveDir::Up, self.get_count()));
165 } else {
166 msgs.push(Message::VerticalScroll(MoveDir::Up, self.get_count()));
167 }
168 msgs.push(Message::InvalidateCount);
169 }
170 _ => {}
171 },
172 Event::Copy => msgs.push(Message::VariableValueToClipbord(
173 MessageTarget::CurrentSelection,
174 )),
175 _ => {}
176 });
177 });
178 }
179
180 pub fn get_count(&self) -> usize {
181 self.user
182 .count
183 .as_deref()
184 .map(str::trim)
185 .and_then(|s| s.parse::<usize>().ok())
186 .unwrap_or(1)
187 }
188}
189
190fn handle_digit(digit: u8, modifiers: &Modifiers, msgs: &mut Vec<Message>) {
191 if modifiers.alt {
192 if let Some(c) = std::char::from_digit(u32::from(digit), 10) {
194 msgs.push(Message::AddCount(c));
195 }
196 } else if modifiers.command {
197 msgs.push(Message::MoveMarkerToCursor(digit));
198 } else {
199 msgs.push(Message::GoToMarkerPosition(digit, 0));
200 }
201}
202
203fn key_to_digit(key: &Key) -> Option<u8> {
204 match key {
205 Key::Num0 => Some(0),
206 Key::Num1 => Some(1),
207 Key::Num2 => Some(2),
208 Key::Num3 => Some(3),
209 Key::Num4 => Some(4),
210 Key::Num5 => Some(5),
211 Key::Num6 => Some(6),
212 Key::Num7 => Some(7),
213 Key::Num8 => Some(8),
214 Key::Num9 => Some(9),
215 _ => None,
216 }
217}