use crate::*; #[derive(Clone)] pub enum ArrangerAppCommand { Focus(FocusCommand), Transport(TransportCommand), Phrases(PhrasePoolCommand), Editor(PhraseEditorCommand), Arrangement(ArrangementCommand), EditPhrase(Option>>), } /// Handle top-level events in standalone arranger. impl Handle for ArrangerApp { fn handle (&mut self, i: &TuiInput) -> Perhaps { if let Some(entered) = self.entered() { use ArrangerAppFocus::*; if let Some(true) = match entered { Transport => self.transport.as_mut().map(|t|t.handle(i)).transpose()?.flatten(), Arrangement => self.arrangement.handle(i)?, PhrasePool => self.phrases.write().unwrap().handle(i)?, PhraseEditor => self.editor.handle(i)?, } { return Ok(Some(true)) } } ArrangerAppCommand::execute_with_state(self, i) } } impl InputToCommand> for ArrangerAppCommand { fn input_to_command (state: &ArrangerApp, input: &TuiInput) -> Option { use FocusCommand::*; use ArrangerAppCommand::*; match input.event() { key!(KeyCode::Tab) => Some(Focus(Next)), key!(Shift-KeyCode::Tab) => Some(Focus(Prev)), key!(KeyCode::BackTab) => Some(Focus(Prev)), key!(Shift-KeyCode::BackTab) => Some(Focus(Prev)), key!(KeyCode::Up) => Some(Focus(Up)), key!(KeyCode::Down) => Some(Focus(Down)), key!(KeyCode::Left) => Some(Focus(Left)), key!(KeyCode::Right) => Some(Focus(Right)), key!(KeyCode::Enter) => Some(Focus(Enter)), key!(KeyCode::Esc) => Some(Focus(Exit)), key!(KeyCode::Char(' ')) => Some(Transport(TransportCommand::PlayToggle)), _ => match state.focused() { ArrangerAppFocus::Transport => state.transport.as_ref() .map(|t|TransportCommand::input_to_command(&*t.read().unwrap(), input) .map(Transport)) .flatten(), ArrangerAppFocus::PhrasePool => { let phrases = state.phrases.read().unwrap(); match input.event() { key!(KeyCode::Char('e')) => Some(EditPhrase(Some(phrases.phrase().clone()))), _ => PhrasePoolCommand::input_to_command(&*phrases, input) .map(Phrases) } }, ArrangerAppFocus::PhraseEditor => PhraseEditorCommand::input_to_command(&state.editor, input) .map(Editor), ArrangerAppFocus::Arrangement => match input.event() { key!(KeyCode::Char('e')) => Some(EditPhrase(state.arrangement.phrase())), _ => ArrangementCommand::input_to_command(&state.arrangement, &input) .map(Arrangement) } } } } } /// Handle events for arrangement. impl Handle for Arrangement { fn handle (&mut self, from: &TuiInput) -> Perhaps { ArrangementCommand::execute_with_state(self, from) } } impl InputToCommand> for ArrangementCommand { fn input_to_command (state: &Arrangement, input: &TuiInput) -> Option { use ArrangementCommand::*; match input.event() { key!(KeyCode::Char('`')) => Some(ToggleViewMode), key!(KeyCode::Delete) => Some(Delete), key!(KeyCode::Enter) => Some(Activate), key!(KeyCode::Char('.')) => Some(Increment), key!(KeyCode::Char(',')) => Some(Decrement), key!(KeyCode::Char('+')) => Some(ZoomIn), key!(KeyCode::Char('=')) => Some(ZoomOut), key!(KeyCode::Char('_')) => Some(ZoomOut), key!(KeyCode::Char('-')) => Some(ZoomOut), key!(KeyCode::Char('<')) => Some(MoveBack), key!(KeyCode::Char('>')) => Some(MoveForward), key!(KeyCode::Char('c')) => Some(RandomColor), key!(KeyCode::Char('s')) => Some(Put), key!(KeyCode::Char('g')) => Some(Get), key!(KeyCode::Char('e')) => Some(Edit(state.phrase())), key!(Ctrl-KeyCode::Char('a')) => Some(AddScene), key!(Ctrl-KeyCode::Char('t')) => Some(AddTrack), key!(KeyCode::Char('l')) => Some(ToggleLoop), key!(KeyCode::Up) => Some(GoUp), key!(KeyCode::Down) => Some(GoDown), key!(KeyCode::Left) => Some(GoLeft), key!(KeyCode::Right) => Some(GoRight), _ => None } } } //impl ArrangerApp { ///// Helper for event passthru to focused component //fn handle_focused (&mut self, from: &TuiInput) -> Perhaps { //match self.focused() { //ArrangerAppFocus::Transport => self.transport.handle(from), //ArrangerAppFocus::PhrasePool => self.handle_pool(from), //ArrangerAppFocus::PhraseEditor => self.editor.handle(from), //ArrangerAppFocus::Arrangement => self.handle_arrangement(from) //.and_then(|result|{self.show_phrase();Ok(result)}), //} //} ///// Helper for phrase event passthru when phrase pool is focused //fn handle_pool (&mut self, from: &TuiInput) -> Perhaps { //match from.event() { //key!(KeyCode::Char('<')) => { //self.phrases_split = self.phrases_split.saturating_sub(1).max(12); //}, //key!(KeyCode::Char('>')) => { //self.phrases_split = self.phrases_split + 1; //}, //_ => return self.phrases.handle(from) //} //Ok(Some(true)) //} ///// Helper for phrase event passthru when arrangement is focused //fn handle_arrangement (&mut self, from: &TuiInput) -> Perhaps { //let mut handle_phrase = ||{ //let result = self.phrases.handle(from); //self.arrangement.phrase_put(); //result //}; //match from.event() { //key!(KeyCode::Char('a')) => return handle_phrase(), //key!(KeyCode::Char('i')) => return handle_phrase(), //key!(KeyCode::Char('d')) => return handle_phrase(), //key!(KeyCode::Char('<')) => if self.arrangement.selected == ArrangementFocus::Mix { //self.arrangement_split = self.arrangement_split.saturating_sub(1).max(12); //} else { //return self.arrangement.handle(from) //}, //key!(KeyCode::Char('>')) => if self.arrangement.selected == ArrangementFocus::Mix { //self.arrangement_split = self.arrangement_split + 1; //} else { //return self.arrangement.handle(from) //}, //_ => return self.arrangement.handle(from) //} //self.show_phrase(); //Ok(Some(true)) //} //} #[derive(Clone)] pub enum ArrangementCommand { New, Load, Save, ToggleViewMode, Delete, Activate, Increment, Decrement, ZoomIn, ZoomOut, MoveBack, MoveForward, RandomColor, Put, Get, AddScene, AddTrack, ToggleLoop, GoUp, GoDown, GoLeft, GoRight, Edit(Option>>), } impl Command> for ArrangerAppCommand { fn execute (self, state: &mut ArrangerApp) -> Perhaps { let undo = match self { Self::Focus(cmd) => { delegate(cmd, Self::Focus, state) }, Self::Phrases(cmd) => { delegate(cmd, Self::Phrases, &mut*state.phrases.write().unwrap()) }, Self::Editor(cmd) => { delegate(cmd, Self::Editor, &mut state.editor) }, Self::Arrangement(cmd) => { delegate(cmd, Self::Arrangement, &mut state.arrangement) }, Self::Transport(cmd) => if let Some(ref transport) = state.transport { delegate(cmd, Self::Transport, &mut*transport.write().unwrap()) } else { Ok(None) }, Self::EditPhrase(phrase) => { state.editor.phrase = phrase.clone(); state.focus(ArrangerAppFocus::PhraseEditor); state.focus_enter(); Ok(None) } }?; state.show_phrase(); state.update_status(); return Ok(undo); } } impl Command> for ArrangementCommand { fn execute (self, state: &mut Arrangement) -> Perhaps { use ArrangementCommand::*; match self { New => todo!(), Load => todo!(), Save => todo!(), Edit(phrase) => { state.phrase = phrase.clone() }, ToggleViewMode => { state.mode.to_next(); }, Delete => { state.delete(); }, Activate => { state.activate(); }, Increment => { state.increment(); }, Decrement => { state.decrement(); }, ZoomIn => { state.zoom_in(); }, ZoomOut => { state.zoom_out(); }, MoveBack => { state.move_back(); }, MoveForward => { state.move_forward(); }, RandomColor => { state.randomize_color(); }, Put => { state.phrase_put(); }, Get => { state.phrase_get(); }, AddScene => { state.scene_add(None, None)?; }, AddTrack => { state.track_add(None, None)?; }, ToggleLoop => { state.toggle_loop() }, GoUp => { state.go_up() }, GoDown => { state.go_down() }, GoLeft => { state.go_left() }, GoRight => { state.go_right() }, }; Ok(None) } }