use crate::*; #[derive(Clone, PartialEq)] pub enum SequencerCommand { FocusNext, FocusPrev, FocusUp, FocusDown, FocusLeft, FocusRight, Transport(TransportCommand), Phrases(PhrasePoolCommand), Editor(PhraseEditorCommand), } #[derive(Clone, PartialEq)] pub enum PhrasePoolCommand { Prev, Next, MoveUp, MoveDown, Delete, Append, Insert, Duplicate, RandomColor, Edit, Import, Export, Rename(PhraseRenameCommand), Length(PhraseLengthCommand), } #[derive(Clone, PartialEq)] pub enum PhraseRenameCommand { Begin, Backspace, Append(char), Set(String), Confirm, Cancel, } #[derive(Clone, PartialEq)] pub enum PhraseLengthCommand { Begin, Next, Prev, Inc, Dec, Set(usize), Confirm, Cancel, } #[derive(Clone, PartialEq)] pub enum PhraseEditorCommand { // TODO: 1-9 seek markers that by default start every 8th of the phrase ToggleDirection, EnterEditMode, ExitEditMode, NoteLengthDec, NoteLengthInc, TimeZoomIn, TimeZoomOut, NoteAppend, NoteSet, NotePageUp, NotePageDown, GoUp, GoDown, GoLeft, GoRight, } impl Command> for SequencerCommand { fn run (&self, state: &mut Sequencer) -> Perhaps { use SequencerCommand::*; match self { FocusNext => { state.focus_next(); }, FocusPrev => { state.focus_prev(); }, FocusUp => { state.focus_up(); }, FocusDown => { state.focus_down(); }, FocusLeft => { state.focus_left(); }, FocusRight => { state.focus_right(); }, Transport(command) => if let Some(ref transport) = state.transport { return command.run(&mut*transport.write().unwrap()).map(|x|x.map(Transport)) }, Phrases(command) => { return command.run(&mut*state.phrases.write().unwrap()).map(|x|x.map(Phrases)) }, Editor(command) => { return command.run(&mut state.editor).map(|x|x.map(Editor)) }, } Ok(None) } } impl Command> for PhrasePoolCommand { fn run (&self, state: &mut PhrasePool) -> Perhaps { use PhrasePoolCommand::*; use PhraseRenameCommand as Rename; use PhraseLengthCommand as Length; match self { Prev => { state.select_prev() }, Next => { state.select_next() }, Delete => { state.delete_selected() }, Append => { state.append_new(None, None) }, Insert => { state.insert_new(None, None) }, Duplicate => { state.insert_dup() }, Edit => { todo!(); } RandomColor => { state.randomize_color() }, MoveUp => { state.move_up() }, MoveDown => { state.move_down() }, Rename(Rename::Begin) => { state.begin_rename() }, Rename(_) => { unreachable!() }, Length(Length::Begin) => { state.begin_length() }, Length(_) => { unreachable!() }, Import => todo!(), Export => todo!(), } Ok(None) } } impl Command> for PhraseRenameCommand { fn run (&self, state: &mut PhrasePool) -> Perhaps { use PhraseRenameCommand::*; if let Some(PhrasePoolMode::Rename(phrase, ref mut old_name)) = state.mode { match self { Begin => { unreachable!(); }, Backspace => { let mut phrase = state.phrases[phrase].write().unwrap(); let old_name = phrase.name.clone(); phrase.name.pop(); return Ok(Some(Self::Set(old_name))) }, Append(c) => { let mut phrase = state.phrases[phrase].write().unwrap(); let old_name = phrase.name.clone(); phrase.name.push(*c); return Ok(Some(Self::Set(old_name))) }, Set(s) => { let mut phrase = state.phrases[phrase].write().unwrap(); phrase.name = s.into(); return Ok(Some(Self::Set(old_name.clone()))) }, Confirm => { let old_name = old_name.clone(); state.mode = None; return Ok(Some(Self::Set(old_name))) }, Cancel => { let mut phrase = state.phrases[phrase].write().unwrap(); phrase.name = old_name.clone(); } }; Ok(None) } else if *self == Begin { todo!() } else { unreachable!() } } } impl Command> for PhraseLengthCommand { fn run (&self, state: &mut PhrasePool) -> Perhaps { use PhraseLengthCommand::*; if let Some(PhrasePoolMode::Length(phrase, ref mut length, ref mut focus)) = state.mode { match self { Begin => { unreachable!(); }, Cancel => { state.mode = None; }, Confirm => { return Self::Set(*length).run(state) }, Prev => { focus.prev() }, Next => { focus.next() }, Inc => { use PhraseLengthFocus::*; match focus { Bar => { *length += 4 * PPQ }, Beat => { *length += PPQ }, Tick => { *length += 1 }, } }, Dec => { use PhraseLengthFocus::*; match focus { Bar => { *length = length.saturating_sub(4 * PPQ) }, Beat => { *length = length.saturating_sub(PPQ) }, Tick => { *length = length.saturating_sub(1) }, } }, Set(length) => { let mut phrase = state.phrases[phrase].write().unwrap(); let old_length = phrase.length; phrase.length = *length; state.mode = None; return Ok(Some(Self::Set(old_length))) }, } Ok(None) } else if *self == Begin { todo!() } else { unreachable!() } } } impl Command> for PhraseEditorCommand { fn run (&self, state: &mut PhraseEditor) -> Perhaps { use PhraseEditorCommand::*; match self { ToggleDirection => { state.mode = !state.mode; }, EnterEditMode => { state.entered = true; }, ExitEditMode => { state.entered = false; }, TimeZoomOut => { state.time_zoom_out() }, TimeZoomIn => { state.time_zoom_in() }, NoteLengthDec => { state.note_length_dec() }, NoteLengthInc => { state.note_length_inc() }, NotePageUp => { state.note_page_up() }, NotePageDown => { state.note_page_down() }, NoteAppend => if state.entered { state.put(); state.time_cursor_advance(); }, NoteSet => if state.entered { state.put(); }, GoUp => match state.entered { true => state.note_cursor_inc(), false => state.note_scroll_inc(), }, GoDown => match state.entered { true => state.note_cursor_dec(), false => state.note_scroll_dec(), }, GoLeft => match state.entered { true => state.time_cursor_dec(), false => state.time_scroll_dec(), }, GoRight => match state.entered { true => state.time_cursor_inc(), false => state.time_scroll_inc(), }, } Ok(None) } }