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