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) => {
61 if modifiers.command {
62 msgs.push(Message::HideCommandPrompt);
63 }
64 }
65 (Key::H, true, false, false) => msgs.push(Message::MoveCursorToTransition {
66 next: false,
67 variable: None,
68 skip_zero: modifiers.shift,
69 }),
70 (Key::J, true, false, false) => {
71 if modifiers.alt {
72 msgs.push(Message::MoveFocus(
73 MoveDir::Down,
74 self.get_count(),
75 modifiers.shift,
76 ));
77 } else if modifiers.command {
78 msgs.push(Message::MoveFocusedItem(MoveDir::Down, self.get_count()));
79 } else {
80 msgs.push(Message::VerticalScroll(MoveDir::Down, self.get_count()));
81 }
82 msgs.push(Message::InvalidateCount);
83 }
84 (Key::K, true, false, false) => {
85 if modifiers.alt {
86 msgs.push(Message::MoveFocus(
87 MoveDir::Up,
88 self.get_count(),
89 modifiers.shift,
90 ));
91 } else if modifiers.command {
92 msgs.push(Message::MoveFocusedItem(MoveDir::Up, self.get_count()));
93 } else {
94 msgs.push(Message::VerticalScroll(MoveDir::Up, self.get_count()));
95 }
96 msgs.push(Message::InvalidateCount);
97 }
98 (Key::L, true, false, false) => msgs.push(Message::MoveCursorToTransition {
99 next: true,
100 variable: None,
101 skip_zero: modifiers.shift,
102 }),
103 (Key::N, true, true, false) => {
104 if modifiers.command {
105 msgs.push(Message::SelectNextCommand);
106 }
107 }
108 (Key::P, true, true, false) => {
109 if modifiers.command {
110 msgs.push(Message::SelectPrevCommand);
111 }
112 }
113 (Key::F11, true, false, _) => msgs.push(Message::ToggleFullscreen),
114 (Key::ArrowRight, true, false, false) => {
115 msgs.push(match self.user.config.behavior.arrow_key_bindings {
116 ArrowKeyBindings::Edge => Message::MoveCursorToTransition {
117 next: true,
118 variable: None,
119 skip_zero: modifiers.shift,
120 },
121 ArrowKeyBindings::Scroll => Message::CanvasScroll {
122 delta: Vec2 {
123 x: 0.,
124 y: -PER_SCROLL_EVENT,
125 },
126 viewport_idx: 0,
127 },
128 });
129 }
130 (Key::ArrowLeft, true, false, false) => {
131 msgs.push(match self.user.config.behavior.arrow_key_bindings {
132 ArrowKeyBindings::Edge => Message::MoveCursorToTransition {
133 next: false,
134 variable: None,
135 skip_zero: modifiers.shift,
136 },
137 ArrowKeyBindings::Scroll => Message::CanvasScroll {
138 delta: Vec2 {
139 x: 0.,
140 y: PER_SCROLL_EVENT,
141 },
142 viewport_idx: 0,
143 },
144 });
145 }
146 (Key::ArrowDown, true, true, false) => msgs.push(Message::SelectNextCommand),
147 (Key::ArrowDown, true, false, false) => {
148 if modifiers.alt {
149 msgs.push(Message::MoveFocus(
150 MoveDir::Down,
151 self.get_count(),
152 modifiers.shift,
153 ));
154 } else if modifiers.command {
155 msgs.push(Message::MoveFocusedItem(MoveDir::Down, self.get_count()));
156 } else {
157 msgs.push(Message::VerticalScroll(MoveDir::Down, self.get_count()));
158 }
159 msgs.push(Message::InvalidateCount);
160 }
161 (Key::ArrowUp, true, true, false) => msgs.push(Message::SelectPrevCommand),
162 (Key::ArrowUp, true, false, false) => {
163 if modifiers.alt {
164 msgs.push(Message::MoveFocus(
165 MoveDir::Up,
166 self.get_count(),
167 modifiers.shift,
168 ));
169 } else if modifiers.command {
170 msgs.push(Message::MoveFocusedItem(MoveDir::Up, self.get_count()));
171 } else {
172 msgs.push(Message::VerticalScroll(MoveDir::Up, self.get_count()));
173 }
174 msgs.push(Message::InvalidateCount);
175 }
176 _ => {}
177 },
178 Event::Copy => msgs.push(Message::VariableValueToClipbord(
179 MessageTarget::CurrentSelection,
180 )),
181 _ => {}
182 });
183 });
184 }
185
186 pub fn get_count(&self) -> usize {
187 self.user
188 .count
189 .as_deref()
190 .map(str::trim)
191 .and_then(|s| s.parse::<usize>().ok())
192 .unwrap_or(1)
193 }
194}
195
196fn handle_digit(digit: u8, modifiers: &Modifiers, msgs: &mut Vec<Message>) {
197 if modifiers.alt {
198 if let Some(c) = std::char::from_digit(digit as u32, 10) {
200 msgs.push(Message::AddCount(c));
201 }
202 } else if modifiers.command {
203 msgs.push(Message::MoveMarkerToCursor(digit));
204 } else {
205 msgs.push(Message::GoToMarkerPosition(digit, 0));
206 }
207}
208
209fn key_to_digit(key: &Key) -> Option<u8> {
210 match key {
211 Key::Num0 => Some(0),
212 Key::Num1 => Some(1),
213 Key::Num2 => Some(2),
214 Key::Num3 => Some(3),
215 Key::Num4 => Some(4),
216 Key::Num5 => Some(5),
217 Key::Num6 => Some(6),
218 Key::Num7 => Some(7),
219 Key::Num8 => Some(8),
220 Key::Num9 => Some(9),
221 _ => None,
222 }
223}