diff --git a/crates/tek/src/tui/app_transport.rs b/crates/tek/src/tui/app_transport.rs index bc15dddf..baa2ca67 100644 --- a/crates/tek/src/tui/app_transport.rs +++ b/crates/tek/src/tui/app_transport.rs @@ -218,23 +218,16 @@ pub enum TransportCommand { Clock(ClockCommand), } -impl Command for TransportCommand { - fn execute (self, state: &mut TransportTui) -> Perhaps { - Ok(match self { - Self::Focus(cmd) => cmd.execute(state)?.map(Self::Focus), - Self::Clock(cmd) => cmd.execute(state)?.map(Self::Clock), - }) - } -} +command!(|self:TransportCommand,state:TransportTui|match self { + //Self::Focus(cmd) => cmd.execute(state)?.map(Self::Focus), + Self::Clock(cmd) => cmd.execute(state)?.map(Self::Clock), + _ => unreachable!(), +}); -impl Command for FocusCommand { - fn execute (self, state: &mut TransportTui) -> Perhaps> { - if let FocusCommand::Set(to) = self { - state.set_focused(to); - } - Ok(None) - } -} +//command!(|self:TransportFocus,state:TransportTui|{ + //if let FocusCommand::Set(to) = self { state.set_focused(to); } + //Ok(None) +//}); impl InputToCommand for TransportCommand { fn input_to_command (state: &TransportTui, input: &TuiInput) -> Option { @@ -262,34 +255,10 @@ where Pause(Some(0)) }), _ => match state.transport_focused().unwrap() { - TransportFocus::Bpm => match input.event() { - key_pat!(Char(',')) => Clock(SetBpm(state.clock().bpm().get() - 1.0)), - key_pat!(Char('.')) => Clock(SetBpm(state.clock().bpm().get() + 1.0)), - key_pat!(Char('<')) => Clock(SetBpm(state.clock().bpm().get() - 0.001)), - key_pat!(Char('>')) => Clock(SetBpm(state.clock().bpm().get() + 0.001)), - _ => return None, - }, - TransportFocus::Quant => match input.event() { - key_pat!(Char(',')) => Clock(SetQuant(state.clock().quant.prev())), - key_pat!(Char('.')) => Clock(SetQuant(state.clock().quant.next())), - key_pat!(Char('<')) => Clock(SetQuant(state.clock().quant.prev())), - key_pat!(Char('>')) => Clock(SetQuant(state.clock().quant.next())), - _ => return None, - }, - TransportFocus::Sync => match input.event() { - key_pat!(Char(',')) => Clock(SetSync(state.clock().sync.prev())), - key_pat!(Char('.')) => Clock(SetSync(state.clock().sync.next())), - key_pat!(Char('<')) => Clock(SetSync(state.clock().sync.prev())), - key_pat!(Char('>')) => Clock(SetSync(state.clock().sync.next())), - _ => return None, - }, - TransportFocus::Clock => match input.event() { - key_pat!(Char(',')) => todo!("transport seek bar"), - key_pat!(Char('.')) => todo!("transport seek bar"), - key_pat!(Char('<')) => todo!("transport seek beat"), - key_pat!(Char('>')) => todo!("transport seek beat"), - _ => return None, - }, + TransportFocus::Bpm => to_bpm_command(input, state.clock().bpm().get())?, + TransportFocus::Quant => to_quant_command(input, &state.clock().quant)?, + TransportFocus::Sync => to_sync_command(input, &state.clock().sync)?, + TransportFocus::Clock => to_seek_command(input)?, TransportFocus::PlayPause => match input.event() { key_pat!(Enter) => Clock( if state.clock().is_stopped() { @@ -310,3 +279,43 @@ where } }) } + +fn to_bpm_command (input: &TuiInput, bpm: f64) -> Option { + Some(match input.event() { + key_pat!(Char(',')) => Clock(SetBpm(bpm - 1.0)), + key_pat!(Char('.')) => Clock(SetBpm(bpm + 1.0)), + key_pat!(Char('<')) => Clock(SetBpm(bpm - 0.001)), + key_pat!(Char('>')) => Clock(SetBpm(bpm + 0.001)), + _ => return None, + }) +} + +fn to_quant_command (input: &TuiInput, quant: &Quantize) -> Option { + Some(match input.event() { + key_pat!(Char(',')) => Clock(SetQuant(quant.prev())), + key_pat!(Char('.')) => Clock(SetQuant(quant.next())), + key_pat!(Char('<')) => Clock(SetQuant(quant.prev())), + key_pat!(Char('>')) => Clock(SetQuant(quant.next())), + _ => return None, + }) +} + +fn to_sync_command (input: &TuiInput, sync: &LaunchSync) -> Option { + Some(match input.event() { + key_pat!(Char(',')) => Clock(SetSync(sync.prev())), + key_pat!(Char('.')) => Clock(SetSync(sync.next())), + key_pat!(Char('<')) => Clock(SetSync(sync.prev())), + key_pat!(Char('>')) => Clock(SetSync(sync.next())), + _ => return None, + }) +} + +fn to_seek_command (input: &TuiInput) -> Option { + Some(match input.event() { + key_pat!(Char(',')) => todo!("transport seek bar"), + key_pat!(Char('.')) => todo!("transport seek bar"), + key_pat!(Char('<')) => todo!("transport seek beat"), + key_pat!(Char('>')) => todo!("transport seek beat"), + _ => return None, + }) +} diff --git a/crates/tek/src/tui/file_browser.rs b/crates/tek/src/tui/file_browser.rs index 5baee32c..cb42feec 100644 --- a/crates/tek/src/tui/file_browser.rs +++ b/crates/tek/src/tui/file_browser.rs @@ -95,100 +95,94 @@ pub enum FileBrowserCommand { Filter(String), } -impl Command for FileBrowserCommand { - fn execute (self, state: &mut PhraseListModel) -> Perhaps { - let mode = state.phrases_mode_mut(); - match mode { - Some(Import(index, ref mut browser)) => match self { - Cancel => { - *mode = None; - }, - Chdir(cwd) => { - *mode = Some(Import(*index, FileBrowser::new(Some(cwd))?)); - }, - Select(index) => { - browser.index = index; - }, - Confirm => { - if browser.is_file() { - let index = *index; - let path = browser.path(); - *mode = None; - PhrasePoolCommand::Import(index, path).execute(state)?; - } else if browser.is_dir() { - *mode = Some(Import(*index, browser.chdir()?)); - } - }, - _ => todo!(), +command!(|self: FileBrowserCommand, state: PhraseListModel|{ + let mode = state.phrases_mode_mut(); + match mode { + Some(Import(index, ref mut browser)) => match self { + Cancel => { + *mode = None; }, - Some(PhraseListMode::Export(index, ref mut browser)) => match self { - Cancel => { - *mode = None; - }, - Chdir(cwd) => { - *mode = Some(PhraseListMode::Export(*index, FileBrowser::new(Some(cwd))?)); - }, - Select(index) => { - browser.index = index; - }, - _ => unreachable!() + Chdir(cwd) => { + *mode = Some(Import(*index, FileBrowser::new(Some(cwd))?)); }, - _ => unreachable!(), - }; - Ok(None) - } -} + Select(index) => { + browser.index = index; + }, + Confirm => { + if browser.is_file() { + let index = *index; + let path = browser.path(); + *mode = None; + PhrasePoolCommand::Import(index, path).execute(state)?; + } else if browser.is_dir() { + *mode = Some(Import(*index, browser.chdir()?)); + } + }, + _ => todo!(), + }, + Some(PhraseListMode::Export(index, ref mut browser)) => match self { + Cancel => { + *mode = None; + }, + Chdir(cwd) => { + *mode = Some(PhraseListMode::Export(*index, FileBrowser::new(Some(cwd))?)); + }, + Select(index) => { + browser.index = index; + }, + _ => unreachable!() + }, + _ => unreachable!(), + }; + None +}); -impl InputToCommand for FileBrowserCommand { - fn input_to_command (state: &PhraseListModel, from: &TuiInput) -> Option { - if let Some(PhraseListMode::Import(_index, browser)) = state.phrases_mode() { - Some(match from.event() { - key_pat!(Up) => Select( - browser.index.overflowing_sub(1).0.min(browser.len().saturating_sub(1)) - ), - key_pat!(Down) => Select( - browser.index.saturating_add(1) % browser.len() - ), - key_pat!(Right) => Chdir(browser.cwd.clone()), - key_pat!(Left) => Chdir(browser.cwd.clone()), - key_pat!(Enter) => Confirm, - key_pat!(Char(_)) => { todo!() }, - key_pat!(Backspace) => { todo!() }, - key_pat!(Esc) => Self::Cancel, - _ => return None - }) - } else if let Some(PhraseListMode::Export(_index, browser)) = state.phrases_mode() { - Some(match from.event() { - key_pat!(Up) => Select(browser.index.overflowing_sub(1).0.min(browser.len())), - key_pat!(Down) => Select(browser.index.saturating_add(1) % browser.len()), - key_pat!(Right) => Chdir(browser.cwd.clone()), - key_pat!(Left) => Chdir(browser.cwd.clone()), - key_pat!(Enter) => Confirm, - key_pat!(Char(_)) => { todo!() }, - key_pat!(Backspace) => { todo!() }, - key_pat!(Esc) => Self::Cancel, - _ => return None - }) - } else { - unreachable!() - } +input_to_command!(FileBrowserCommand:|state:PhraseListModel,from|{ + if let Some(PhraseListMode::Import(_index, browser)) = state.phrases_mode() { + Some(match from.event() { + key_pat!(Up) => Select( + browser.index.overflowing_sub(1).0.min(browser.len().saturating_sub(1)) + ), + key_pat!(Down) => Select( + browser.index.saturating_add(1) % browser.len() + ), + key_pat!(Right) => Chdir(browser.cwd.clone()), + key_pat!(Left) => Chdir(browser.cwd.clone()), + key_pat!(Enter) => Confirm, + key_pat!(Char(_)) => { todo!() }, + key_pat!(Backspace) => { todo!() }, + key_pat!(Esc) => Self::Cancel, + _ => return None + }) + } else if let Some(PhraseListMode::Export(_index, browser)) = state.phrases_mode() { + Some(match from.event() { + key_pat!(Up) => Select(browser.index.overflowing_sub(1).0.min(browser.len())), + key_pat!(Down) => Select(browser.index.saturating_add(1) % browser.len()), + key_pat!(Right) => Chdir(browser.cwd.clone()), + key_pat!(Left) => Chdir(browser.cwd.clone()), + key_pat!(Enter) => Confirm, + key_pat!(Char(_)) => { todo!() }, + key_pat!(Backspace) => { todo!() }, + key_pat!(Esc) => Self::Cancel, + _ => return None + }) + } else { + unreachable!() } -} +}); -impl InputToCommand for PhraseLengthCommand { - fn input_to_command (state: &PhraseListModel, from: &TuiInput) -> Option { - if let Some(PhraseListMode::Length(_, length, _)) = state.phrases_mode() { - Some(match from.event() { - key_pat!(Up) => Self::Inc, - key_pat!(Down) => Self::Dec, - key_pat!(Right) => Self::Next, - key_pat!(Left) => Self::Prev, - key_pat!(Enter) => Self::Set(*length), - key_pat!(Esc) => Self::Cancel, - _ => return None - }) - } else { - unreachable!() - } +input_to_command!(PhraseLengthCommand:|state:PhraseListModel,from|{ + if let Some(PhraseListMode::Length(_, length, _)) = state.phrases_mode() { + Some(match from.event() { + key_pat!(Up) => Self::Inc, + key_pat!(Down) => Self::Dec, + key_pat!(Right) => Self::Next, + key_pat!(Left) => Self::Prev, + key_pat!(Enter) => Self::Set(*length), + key_pat!(Esc) => Self::Cancel, + _ => return None + }) + } else { + unreachable!() } -} +}); diff --git a/crates/tek/src/tui/phrase_list.rs b/crates/tek/src/tui/phrase_list.rs index dd0f60ca..8ee899fd 100644 --- a/crates/tek/src/tui/phrase_list.rs +++ b/crates/tek/src/tui/phrase_list.rs @@ -50,68 +50,64 @@ pub enum PhrasesCommand { Export(Browse), } -impl Command for PhrasesCommand { - fn execute (self, state: &mut PhraseListModel) -> Perhaps { - use PhrasesCommand::*; - Ok(match self { - Phrase(command) => command.execute(state)?.map(Phrase), - Rename(command) => match command { - PhraseRenameCommand::Begin => { - let length = state.phrases()[state.phrase_index()].read().unwrap().length; - *state.phrases_mode_mut() = Some( - PhraseListMode::Length(state.phrase_index(), length, PhraseLengthFocus::Bar) - ); - None - }, - _ => command.execute(state)?.map(Rename) - }, - Length(command) => match command { - PhraseLengthCommand::Begin => { - let name = state.phrases()[state.phrase_index()].read().unwrap().name.clone(); - *state.phrases_mode_mut() = Some( - PhraseListMode::Rename(state.phrase_index(), name) - ); - None - }, - _ => command.execute(state)?.map(Length) - }, - Import(command) => match command { - FileBrowserCommand::Begin => { - *state.phrases_mode_mut() = Some( - PhraseListMode::Import(state.phrase_index(), FileBrowser::new(None)?) - ); - None - }, - _ => command.execute(state)?.map(Import) - }, - Export(command) => match command { - FileBrowserCommand::Begin => { - *state.phrases_mode_mut() = Some( - PhraseListMode::Export(state.phrase_index(), FileBrowser::new(None)?) - ); - None - }, - _ => command.execute(state)?.map(Export) - }, - Select(phrase) => { - state.set_phrase_index(phrase); +command!(|self:PhrasesCommand, state:PhraseListModel|{ + use PhrasesCommand::*; + match self { + Phrase(command) => command.execute(state)?.map(Phrase), + Rename(command) => match command { + PhraseRenameCommand::Begin => { + let length = state.phrases()[state.phrase_index()].read().unwrap().length; + *state.phrases_mode_mut() = Some( + PhraseListMode::Length(state.phrase_index(), length, PhraseLengthFocus::Bar) + ); None }, - }) + _ => command.execute(state)?.map(Rename) + }, + Length(command) => match command { + PhraseLengthCommand::Begin => { + let name = state.phrases()[state.phrase_index()].read().unwrap().name.clone(); + *state.phrases_mode_mut() = Some( + PhraseListMode::Rename(state.phrase_index(), name) + ); + None + }, + _ => command.execute(state)?.map(Length) + }, + Import(command) => match command { + FileBrowserCommand::Begin => { + *state.phrases_mode_mut() = Some( + PhraseListMode::Import(state.phrase_index(), FileBrowser::new(None)?) + ); + None + }, + _ => command.execute(state)?.map(Import) + }, + Export(command) => match command { + FileBrowserCommand::Begin => { + *state.phrases_mode_mut() = Some( + PhraseListMode::Export(state.phrase_index(), FileBrowser::new(None)?) + ); + None + }, + _ => command.execute(state)?.map(Export) + }, + Select(phrase) => { + state.set_phrase_index(phrase); + None + }, } -} +}); -impl InputToCommand for PhrasesCommand { - fn input_to_command (state: &PhraseListModel, input: &TuiInput) -> Option { - Some(match state.phrases_mode() { - Some(PhraseListMode::Rename(..)) => Self::Rename(Rename::input_to_command(state, input)?), - Some(PhraseListMode::Length(..)) => Self::Length(Length::input_to_command(state, input)?), - Some(PhraseListMode::Import(..)) => Self::Import(Browse::input_to_command(state, input)?), - Some(PhraseListMode::Export(..)) => Self::Export(Browse::input_to_command(state, input)?), - _ => to_phrases_command(state, input)? - }) - } -} +input_to_command!(PhrasesCommand:|state:PhraseListModel,input|{ + Some(match state.phrases_mode() { + Some(PhraseListMode::Rename(..)) => Self::Rename(Rename::input_to_command(state, input)?), + Some(PhraseListMode::Length(..)) => Self::Length(Length::input_to_command(state, input)?), + Some(PhraseListMode::Import(..)) => Self::Import(Browse::input_to_command(state, input)?), + Some(PhraseListMode::Export(..)) => Self::Export(Browse::input_to_command(state, input)?), + _ => to_phrases_command(state, input)? + }) +}); fn to_phrases_command (state: &PhraseListModel, input: &TuiInput) -> Option { use KeyCode::{Up, Down, Delete, Char};