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 | self.user.variable_name_filter_focused) {
12 self.user.config.shortcuts.process(ctx, msgs, self);
13 }
14 ctx.input(|i| {
15 i.events.iter().for_each(|event| match event {
16 Event::Key {
17 key,
18 repeat: _,
19 pressed,
20 modifiers,
21 physical_key: _,
22 } => match (
23 key,
24 pressed,
25 self.command_prompt.visible,
26 self.user.variable_name_filter_focused,
27 ) {
28 (k, true, false, false)
30 if matches!(
31 k,
32 Key::Num0
33 | Key::Num1
34 | Key::Num2
35 | Key::Num3
36 | Key::Num4
37 | Key::Num5
38 | Key::Num6
39 | Key::Num7
40 | Key::Num8
41 | Key::Num9
42 ) =>
43 {
44 if let Some(d) = key_to_digit(k) {
45 handle_digit(d, modifiers, msgs);
46 }
47 }
48 (Key::Escape, true, true, false) => msgs.push(Message::HideCommandPrompt),
49 (Key::Escape, true, false, false) => {
50 msgs.push(Message::InvalidateCount);
51 msgs.push(Message::ItemSelectionClear);
52 }
53 (Key::Escape, true, _, true) => msgs.push(Message::SetFilterFocused(false)),
54 (Key::G, true, true, false) => {
55 if modifiers.command {
56 msgs.push(Message::HideCommandPrompt);
57 }
58 }
59 (Key::H, true, false, false) => msgs.push(Message::MoveCursorToTransition {
60 next: false,
61 variable: None,
62 skip_zero: modifiers.shift,
63 }),
64 (Key::J, true, false, false) => {
65 if modifiers.alt {
66 msgs.push(Message::MoveFocus(
67 MoveDir::Down,
68 self.get_count(),
69 modifiers.shift,
70 ));
71 } else if modifiers.command {
72 msgs.push(Message::MoveFocusedItem(MoveDir::Down, self.get_count()));
73 } else {
74 msgs.push(Message::VerticalScroll(MoveDir::Down, self.get_count()));
75 }
76 msgs.push(Message::InvalidateCount);
77 }
78 (Key::K, true, false, false) => {
79 if modifiers.alt {
80 msgs.push(Message::MoveFocus(
81 MoveDir::Up,
82 self.get_count(),
83 modifiers.shift,
84 ));
85 } else if modifiers.command {
86 msgs.push(Message::MoveFocusedItem(MoveDir::Up, self.get_count()));
87 } else {
88 msgs.push(Message::VerticalScroll(MoveDir::Up, self.get_count()));
89 }
90 msgs.push(Message::InvalidateCount);
91 }
92 (Key::L, true, false, false) => msgs.push(Message::MoveCursorToTransition {
93 next: true,
94 variable: None,
95 skip_zero: modifiers.shift,
96 }),
97 (Key::N, true, true, false) => {
98 if modifiers.command {
99 msgs.push(Message::SelectNextCommand);
100 }
101 }
102 (Key::P, true, true, false) => {
103 if modifiers.command {
104 msgs.push(Message::SelectPrevCommand);
105 }
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(digit as u32, 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}