use crate::*; #[derive(Clone, Debug, PartialEq)] pub enum TransportCommand { Focus(FocusCommand), Clock(ClockCommand), Playhead(PlayheadCommand), } #[derive(Clone, Debug, PartialEq)] pub enum SequencerCommand { Focus(FocusCommand), Undo, Redo, Clear, Clock(ClockCommand), Playhead(PlayheadCommand), Phrases(PhrasesCommand), Editor(PhraseCommand), } #[derive(Clone, Debug)] pub enum ArrangerCommand { Focus(FocusCommand), Undo, Redo, Clear, Clock(ClockCommand), Playhead(PlayheadCommand), Scene(ArrangerSceneCommand), Track(ArrangerTrackCommand), Clip(ArrangerClipCommand), Select(ArrangerSelection), Zoom(usize), Phrases(PhrasePoolCommand), Editor(PhraseCommand), EditPhrase(Option>>), } #[derive(Clone, PartialEq, Debug)] pub enum PhrasesCommand { Select(usize), Edit(PhrasePoolCommand), Rename(PhraseRenameCommand), Length(PhraseLengthCommand), } #[derive(Clone, Debug, PartialEq)] pub enum PhraseRenameCommand { Begin, Set(String), Confirm, Cancel, } #[derive(Copy, Clone, Debug, PartialEq)] pub enum PhraseLengthCommand { Begin, Next, Prev, Inc, Dec, Set(usize), Cancel, } #[derive(Clone, PartialEq, Debug)] pub enum PhraseCommand { // TODO: 1-9 seek markers that by default start every 8th of the phrase ToggleDirection, EnterEditMode, ExitEditMode, NoteAppend, NoteSet, NoteCursorSet(usize), NoteLengthSet(usize), NoteScrollSet(usize), TimeCursorSet(usize), TimeScrollSet(usize), TimeZoomSet(usize), } impl Command for TransportCommand { fn execute (self, state: &mut T) -> Perhaps { use TransportCommand::{Focus, Clock, Playhead}; use FocusCommand::{Next, Prev}; use ClockCommand::{SetBpm, SetQuant, SetSync}; Ok(Some(match self { Focus(Next) => { todo!() } Focus(Prev) => { todo!() }, Focus(_) => { unimplemented!() }, Clock(SetBpm(bpm)) => Clock(SetBpm(state.bpm().set(bpm))), Clock(SetQuant(quant)) => Clock(SetQuant(state.quant().set(quant))), Clock(SetSync(sync)) => Clock(SetSync(state.sync().set(sync))), _ => return Ok(None) })) } } impl Command for SequencerCommand where T: FocusGrid + PhrasesControl + PhraseControl + ClockApi + PlayheadApi { fn execute (self, state: T) -> Perhaps { use SequencerCommand::*; match self { Focus(cmd) => delegate(cmd, Focus, state), Phrases(cmd) => delegate(cmd, Phrases, state), Editor(cmd) => delegate(cmd, Editor, state), Clock(cmd) => delegate(cmd, Clock, state), Playhead(cmd) => delegate(cmd, Playhead, state) } } } impl Command for ArrangerCommand where T: FocusGrid + ArrangerControl + HasPhrases + PhraseControl + ClockApi + PlayheadApi { fn execute (self, state: &mut T) -> Perhaps { use ArrangerCommand::*; match self { Focus(cmd) => { delegate(cmd, Focus, &mut state) }, Scene(cmd) => { delegate(cmd, Scene, &mut state) }, Track(cmd) => { delegate(cmd, Track, &mut state) }, Clip(cmd) => { delegate(cmd, Clip, &mut state) }, Phrases(cmd) => { delegate(cmd, Phrases, &mut state) }, Editor(cmd) => { delegate(cmd, Editor, &mut state) }, Clock(cmd) => { delegate(cmd, Clock, &mut state) }, Playhead(cmd) => { delegate(cmd, Playhead, &mut state) }, Zoom(zoom) => { todo!(); }, Select(selected) => { state.selected = selected; Ok(None) }, EditPhrase(phrase) => { state.editor.phrase = phrase.clone(); state.focus(ArrangerFocus::PhraseEditor); state.focus_enter(); Ok(None) } } } } impl Command for ArrangerSceneCommand { fn execute (self, state: &mut T) -> Perhaps { todo!(); Ok(None) } } impl Command for ArrangerTrackCommand { fn execute (self, state: &mut T) -> Perhaps { todo!(); Ok(None) } } impl Command for ArrangerClipCommand { fn execute (self, state: &mut T) -> Perhaps { todo!(); Ok(None) } } impl Command for PhrasesCommand { fn execute (self, state: &mut T) -> Perhaps { use PhraseRenameCommand as Rename; use PhraseLengthCommand as Length; match self { Self::Select(phrase) => { state.phrase = phrase }, Self::Edit(command) => { return Ok(command.execute(&mut state)?.map(Self::Edit)) } Self::Rename(command) => match command { Rename::Begin => self.phrases_rename_begin(), _ => return Ok(command.execute(state)?.map(Self::Rename)), }, Self::Length(command) => match command { Length::Begin => self.phrases_length_begin(), _ => return Ok(command.execute(state)?.map(Self::Length)), }, } Ok(None) } } impl Command for PhraseLengthCommand { fn execute (self, state: &mut T) -> Perhaps { use PhraseLengthFocus::*; use PhraseLengthCommand::*; if let Some(PhrasesMode::Length(phrase, ref mut length, ref mut focus)) = state.mode { match self { Self::Cancel => { state.mode = None; }, Self::Prev => { focus.prev() }, Self::Next => { focus.next() }, Self::Inc => match focus { Bar => { *length += 4 * PPQ }, Beat => { *length += PPQ }, Tick => { *length += 1 }, }, Self::Dec => match focus { Bar => { *length = length.saturating_sub(4 * PPQ) }, Beat => { *length = length.saturating_sub(PPQ) }, Tick => { *length = length.saturating_sub(1) }, }, Self::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))) }, _ => unreachable!() } Ok(None) } else if self == Begin { self.phrases_length_begin(); Ok(None) } else { unreachable!() } } } impl Command for PhraseRenameCommand { fn execute (self, state: &mut T) -> Perhaps { use PhraseRenameCommand::*; if let Some(PhrasesMode::Rename(phrase, ref mut old_name)) = state.mode { match self { Set(s) => { state.phrases[phrase].write().unwrap().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(); }, _ => unreachable!() }; Ok(None) } else if self == Begin { self.phrases_rename_begin(); Ok(None) } else { unreachable!() } } } impl Command for PhraseCommand { //fn translate (self, state: &PhraseTui) -> Self { //use PhraseCommand::*; //match self { //GoUp => match state.entered { true => NoteCursorInc, false => NoteScrollInc, }, //GoDown => match state.entered { true => NoteCursorDec, false => NoteScrollDec, }, //GoLeft => match state.entered { true => TimeCursorDec, false => TimeScrollDec, }, //GoRight => match state.entered { true => TimeCursorInc, false => TimeScrollInc, }, //_ => self //} //} fn execute (self, state: &mut T) -> Perhaps { use PhraseCommand::*; match self.translate(state) { ToggleDirection => { state.mode = !state.mode; }, EnterEditMode => { state.entered = true; }, ExitEditMode => { state.entered = false; }, TimeZoomOut => { let scale = state.time_axis().read().unwrap().scale; state.time_axis().write().unwrap().scale = next_note_length(scale) }, TimeZoomIn => { let scale = state.time_axis().read().unwrap().scale; state.time_axis().write().unwrap().scale = prev_note_length(scale) }, TimeCursorDec => { let scale = state.time_axis().read().unwrap().scale; state.time_axis().write().unwrap().point_dec(scale); }, TimeCursorInc => { let scale = state.time_axis().read().unwrap().scale; state.time_axis().write().unwrap().point_inc(scale); }, TimeScrollDec => { let scale = state.time_axis().read().unwrap().scale; state.time_axis().write().unwrap().start_dec(scale); }, TimeScrollInc => { let scale = state.time_axis().read().unwrap().scale; state.time_axis().write().unwrap().start_inc(scale); }, NoteCursorDec => { let mut axis = state.note_axis().write().unwrap(); axis.point_inc(1); if let Some(point) = axis.point { if point > 73 { axis.point = Some(73); } } }, NoteCursorInc => { let mut axis = state.note_axis().write().unwrap(); axis.point_dec(1); if let Some(point) = axis.point { if point < axis.start { axis.start = (point / 2) * 2; } } }, NoteScrollDec => { state.note_axis().write().unwrap().start_inc(1); }, NoteScrollInc => { state.note_axis().write().unwrap().start_dec(1); }, NoteLengthDec => { *state.note_len_mut() = prev_note_length(state.note_len()) }, NoteLengthInc => { *state.note_len_mut() = next_note_length(state.note_len()) }, NotePageUp => { let mut axis = state.note_axis().write().unwrap(); axis.start_dec(3); axis.point_dec(3); }, NotePageDown => { let mut axis = state.note_axis().write().unwrap(); axis.start_inc(3); axis.point_inc(3); }, NoteAppend => { if state.entered { state.put(); state.time_cursor_advance(); } }, NoteSet => { if state.entered { state.put(); } }, _ => unreachable!() } Ok(None) } }