phosphor_app/state/
navigation.rs1use super::*;
4
5impl NavState {
6
7 pub fn toggle_space_menu(&mut self) {
11 self.space_menu.toggle();
12 }
13
14 pub fn space_menu_handle(&mut self, ch: char) -> Option<SpaceAction> {
20 self.space_menu.open = false;
21 match ch {
22 '1' => { self.focus_pane(Pane::Transport); None }
23 '2' => { self.focus_pane(Pane::Tracks); None }
24 '3' => { self.focus_pane(Pane::ClipView); None }
25 'p' => Some(SpaceAction::PlayPause),
26 '0' => Some(SpaceAction::Stop),
27 'r' => Some(SpaceAction::ToggleRecord),
28 'l' => Some(SpaceAction::ToggleLoop),
29 'm' => Some(SpaceAction::ToggleMetronome),
30 '!' => Some(SpaceAction::Panic),
31 'a' => Some(SpaceAction::AddInstrument),
32 's' => Some(SpaceAction::Save),
33 'o' => Some(SpaceAction::Open),
34 'd' => Some(SpaceAction::Delete),
35 'e' => Some(SpaceAction::EditMode),
36 'q' => Some(SpaceAction::Quantize),
37 'w' => Some(SpaceAction::Presets),
38 'v' => Some(SpaceAction::CycleTheme),
39 'n' => Some(SpaceAction::NewTrack),
40 'h' => {
41 self.space_menu.open = true;
42 self.space_menu.section = SpaceMenuSection::Help;
43 self.space_menu.cursor = 0;
44 None
45 }
46 _ => None,
47 }
48 }
49
50 pub fn focus_pane(&mut self, pane: Pane) {
56 if self.focused_pane == Pane::Tracks { self.track_selected = false; self.element_locked = false; }
57 self.focused_pane = pane;
58 tracing::debug!("focused pane: {:?}", pane);
59 }
60
61
62 pub fn focus_next_pane(&mut self) {
63 self.focus_pane(self.focused_pane.next());
64 }
65
66 pub fn move_up(&mut self) {
72 if self.instrument_modal.open { self.instrument_modal.move_up(); return; }
73 if self.space_menu.open { self.space_menu.move_up(); return; }
74 if self.fx_menu.open { self.fx_menu.move_up(); return; }
75 match self.focused_pane {
76 Pane::Transport => {} Pane::Tracks if !self.track_selected => {
78 if self.track_cursor > 0 {
79 self.track_cursor -= 1;
80 if self.track_cursor < self.track_scroll {
81 self.track_scroll = self.track_cursor;
82 }
83 }
84 }
85 Pane::Tracks => {
86 }
89 Pane::ClipView => {
90 match self.clip_view.focus {
91 ClipViewFocus::PianoRoll if self.clip_view.clip_tab == ClipTab::InstConfig => {
92 if self.clip_view.inst_config_cursor > 0 {
93 self.clip_view.inst_config_cursor -= 1;
94 }
95 }
96 ClipViewFocus::PianoRoll if self.clip_view.clip_tab == ClipTab::Settings => {
97 if self.clip_view.piano_roll.settings_cursor > 0 {
98 self.clip_view.piano_roll.settings_cursor -= 1;
99 }
100 }
101 ClipViewFocus::PianoRoll => self.clip_view.piano_roll.move_up(),
102 ClipViewFocus::FxPanel => {
103 if self.clip_view.fx_panel_tab == FxPanelTab::Synth {
104 if self.clip_view.synth_param_cursor > 0 {
105 self.clip_view.synth_param_cursor -= 1;
106 }
107 } else if self.clip_view.fx_cursor > 0 {
108 self.clip_view.fx_cursor -= 1;
109 }
110 }
111 }
112 }
113 }
114 }
115
116
117 pub fn move_down(&mut self) {
118 if self.instrument_modal.open { self.instrument_modal.move_down(); return; }
119 if self.space_menu.open { self.space_menu.move_down(); return; }
120 if self.fx_menu.open { self.fx_menu.move_down(); return; }
121 match self.focused_pane {
122 Pane::Transport => {}
123 Pane::Tracks if !self.track_selected => {
124 if self.track_cursor + 1 < self.tracks.len() {
125 self.track_cursor += 1;
126 if self.track_cursor >= self.track_scroll + MAX_VISIBLE_TRACKS {
127 self.track_scroll = self.track_cursor + 1 - MAX_VISIBLE_TRACKS;
128 }
129 }
130 }
131 Pane::Tracks => {
132 }
134 Pane::ClipView => {
135 match self.clip_view.focus {
136 ClipViewFocus::PianoRoll if self.clip_view.clip_tab == ClipTab::InstConfig => {
137 if self.clip_view.inst_config_cursor + 1 < INST_CONFIG_PARAM_COUNT {
138 self.clip_view.inst_config_cursor += 1;
139 }
140 }
141 ClipViewFocus::PianoRoll if self.clip_view.clip_tab == ClipTab::Settings => {
142 const SETTINGS_COUNT: usize = 3; if self.clip_view.piano_roll.settings_cursor + 1 < SETTINGS_COUNT {
144 self.clip_view.piano_roll.settings_cursor += 1;
145 }
146 }
147 ClipViewFocus::PianoRoll => self.clip_view.piano_roll.move_down(),
148 ClipViewFocus::FxPanel => {
149 if self.clip_view.fx_panel_tab == FxPanelTab::Synth {
150 let max = self.current_track().map(|t| t.synth_params.len()).unwrap_or(0);
151 if self.clip_view.synth_param_cursor + 1 < max {
152 self.clip_view.synth_param_cursor += 1;
153 }
154 } else {
155 let max = self.active_fx_chain_len();
156 if self.clip_view.fx_cursor + 1 < max {
157 self.clip_view.fx_cursor += 1;
158 }
159 }
160 }
161 }
162 }
163 }
164 }
165
166
167 pub fn move_left(&mut self) {
168 if self.focused_pane == Pane::Tracks && self.track_selected {
169 self.track_element = self.track_element.move_left();
170 if let TrackElement::Clip(idx) = self.track_element {
172 self.open_clip_view(self.track_cursor, idx);
173 }
174 } else if self.focused_pane == Pane::ClipView {
175 match self.clip_view.focus {
176 ClipViewFocus::PianoRoll if self.clip_view.clip_tab == ClipTab::InstConfig => {
177 }
179 ClipViewFocus::PianoRoll if self.clip_view.clip_tab == ClipTab::Settings => {
180 self.adjust_setting(-1);
182 }
183 ClipViewFocus::PianoRoll => {
184 self.clip_view.focus = ClipViewFocus::FxPanel;
185 }
186 ClipViewFocus::FxPanel if self.clip_view.fx_panel_tab == FxPanelTab::Synth => {
187 self.adjust_synth_param(-0.05);
189 }
190 _ => {}
191 }
192 }
193 }
194
195
196 pub fn move_right(&mut self) {
197 if self.focused_pane == Pane::Tracks && self.track_selected {
198 let num_clips = self.current_track().map(|t| t.clips.len()).unwrap_or(0);
199 self.track_element = self.track_element.move_right(num_clips);
200 if let TrackElement::Clip(idx) = self.track_element {
202 self.open_clip_view(self.track_cursor, idx);
203 }
204 } else if self.focused_pane == Pane::ClipView {
205 match self.clip_view.focus {
206 ClipViewFocus::PianoRoll if self.clip_view.clip_tab == ClipTab::InstConfig => {
207 }
209 ClipViewFocus::PianoRoll if self.clip_view.clip_tab == ClipTab::Settings => {
210 self.adjust_setting(1);
212 }
213 ClipViewFocus::FxPanel if self.clip_view.fx_panel_tab == FxPanelTab::Synth => {
214 self.adjust_synth_param(0.05);
216 }
217 ClipViewFocus::FxPanel => {
218 self.clip_view.focus = ClipViewFocus::PianoRoll;
219 }
220 _ => {}
221 }
222 }
223 }
224
225 pub fn adjust_setting(&mut self, direction: i32) {
227 match self.clip_view.piano_roll.settings_cursor {
228 0 => {
229 if direction > 0 {
231 self.clip_view.piano_roll.grid = self.clip_view.piano_roll.grid.next();
232 } else {
233 self.clip_view.piano_roll.grid = self.clip_view.piano_roll.grid.prev();
234 }
235 self.clip_view.piano_roll.update_column_count();
236 }
237 1 => {
238 self.clip_view.piano_roll.snap_enabled = !self.clip_view.piano_roll.snap_enabled;
240 }
241 2 => {
242 let v = self.clip_view.piano_roll.default_velocity as i32 + direction * 5;
244 self.clip_view.piano_roll.default_velocity = v.clamp(1, 127) as u8;
245 }
246 _ => {}
247 }
248 }
249
250 pub fn enter(&mut self) -> Option<SpaceAction> {
254 if self.space_menu.open {
256 match self.space_menu.section {
257 SpaceMenuSection::Actions => {
258 if let Some((key, _, _)) = SPACE_ACTIONS.get(self.space_menu.cursor) {
259 if let Some(ch) = key.strip_prefix("spc+").and_then(|s| s.chars().next()) {
261 return self.space_menu_handle(ch);
262 }
263 }
264 self.space_menu.open = false;
265 return None;
266 }
267 SpaceMenuSection::Help => {
268 return None;
270 }
271 }
272 }
273 if self.fx_menu.open {
275 self.fx_menu_select();
276 return None;
277 }
278
279 match self.focused_pane {
280 Pane::Transport => {} Pane::Tracks => {
282 if !self.track_selected {
283 self.track_selected = true;
284 self.track_element = TrackElement::Label;
285 self.show_current_track_controls();
286 } else {
287 self.activate_element();
288 }
289 }
290 Pane::ClipView => {}
291 }
292 None
293 }
294
295
296 pub fn escape(&mut self) {
297 if self.instrument_modal.open {
298 self.instrument_modal.open = false;
299 return;
300 }
301 if self.space_menu.open {
302 self.space_menu.open = false;
303 return;
304 }
305 if self.fx_menu.open {
306 self.fx_menu.open = false;
307 return;
308 }
309 match self.focused_pane {
310 Pane::Transport => {} Pane::Tracks => {
312 if self.element_locked {
313 self.element_locked = false;
315 } else if self.track_selected {
316 self.track_selected = false;
317 self.element_locked = false;
318 self.track_element = TrackElement::Label;
319 self.clip_view_visible = false;
320 self.clip_view_target = None;
321 }
322 }
323 Pane::ClipView => self.focus_pane(Pane::Tracks),
324 }
325 }
326
327 pub fn cycle_tab(&mut self) {
333 if self.focused_pane != Pane::ClipView { return; }
334
335 let has_sequencer = self.current_track().is_some_and(|t| t.sequencer.is_some());
339
340 match (self.clip_view.focus, self.clip_view.fx_panel_tab, self.clip_view.clip_tab) {
341 (ClipViewFocus::FxPanel, FxPanelTab::TrackFx, _) => {
343 self.clip_view.fx_panel_tab = FxPanelTab::Synth;
344 }
345 (ClipViewFocus::FxPanel, FxPanelTab::Synth, _) => {
351 self.clip_view.focus = ClipViewFocus::PianoRoll;
352 if has_sequencer {
353 self.clip_view.clip_tab = ClipTab::Sequencer;
354 self.clip_view.sequencer.focus_band(SeqBand::Grid);
355 } else {
356 self.clip_view.clip_tab = ClipTab::InstConfig;
357 self.clip_view.inst_config_cursor = 0;
358 }
359 }
360 (ClipViewFocus::PianoRoll, _, ClipTab::Sequencer) => {
362 self.clip_view.clip_tab = ClipTab::InstConfig;
363 self.clip_view.inst_config_cursor = 0;
364 }
365 (ClipViewFocus::PianoRoll, _, ClipTab::InstConfig) => {
367 self.clip_view.clip_tab = ClipTab::PianoRoll;
368 self.clip_view.piano_roll.focus = PianoRollFocus::Navigation;
369 self.clip_view.piano_roll.column = 0;
370 }
371 (ClipViewFocus::PianoRoll, _, ClipTab::PianoRoll) => {
373 self.clip_view.clip_tab = ClipTab::Settings;
374 }
375 (ClipViewFocus::PianoRoll, _, ClipTab::Settings) => {
377 self.clip_view.focus = ClipViewFocus::FxPanel;
378 self.clip_view.fx_panel_tab = FxPanelTab::TrackFx;
379 self.clip_view.fx_cursor = 0;
380 }
381 }
382 }
383
384}