diff --git a/crates/tek/src/tui.rs b/crates/tek/src/tui.rs index 8b4851d0..7d108d84 100644 --- a/crates/tek/src/tui.rs +++ b/crates/tek/src/tui.rs @@ -41,7 +41,7 @@ mod phrase_editor; pub(crate) use phrase_editor::*; mod piano_horizontal; pub(crate) use piano_horizontal::*; mod phrase_length; pub(crate) use phrase_length::*; mod phrase_rename; pub(crate) use phrase_rename::*; -mod phrase_list; pub(crate) use phrase_list::*; +mod pool; pub(crate) use pool::*; mod port_select; //////////////////////////////////////////////////////// diff --git a/crates/tek/src/tui/app_arranger.rs b/crates/tek/src/tui/app_arranger.rs index de5372ad..09e62459 100644 --- a/crates/tek/src/tui/app_arranger.rs +++ b/crates/tek/src/tui/app_arranger.rs @@ -3,7 +3,7 @@ use crate::*; pub struct ArrangerTui { jack: Arc>, pub clock: ClockModel, - pub phrases: PhraseListModel, + pub phrases: PoolModel, pub tracks: Vec, pub scenes: Vec, pub splits: [u16;2], @@ -97,7 +97,7 @@ render!(|self: ArrangerTui|{ let transport = TransportView::from((self, Some(ItemPalette::from(TuiTheme::g(96))), true)); let with_transport = |x|col!([row!(![&play, &transport]), &x]); let pool_size = if self.show_pool { self.splits[1] } else { 0 }; - let with_pool = |x|Split::left(false, pool_size, PhraseListView(&self.phrases), x); + let with_pool = |x|Split::left(false, pool_size, PoolView(&self.phrases), x); let status = ArrangerStatus::from(self); let with_editbar = |x|Tui::split_n(false, 3, PhraseEditStatus(&self.editor), x); let with_status = |x|Tui::split_n(false, 2, status, x); diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index c566d672..01e6db86 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -8,7 +8,7 @@ use PhrasePoolCommand::*; pub struct SequencerTui { _jack: Arc>, pub(crate) clock: ClockModel, - pub(crate) phrases: PhraseListModel, + pub(crate) phrases: PoolModel, pub(crate) player: PhrasePlayerModel, pub(crate) editor: PhraseEditorModel, pub(crate) size: Measure, @@ -26,7 +26,7 @@ from_jack!(|jack|SequencerTui { ))); Self { _jack: jack.clone(), - phrases: PhraseListModel::from(&phrase), + phrases: PoolModel::from(&phrase), editor: PhraseEditorModel::from(&phrase), player: PhrasePlayerModel::from((&clock, &phrase)), size: Measure::new(), @@ -42,7 +42,7 @@ render!(|self: SequencerTui|{ let w = self.size.w(); let phrase_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; let pool_w = if self.show_pool { phrase_w } else { 0 }; - let pool = Fill::h(Align::e(PhraseListView(&self.phrases))); + let pool = Fill::h(Align::e(PoolView(&self.phrases))); let with_pool = move|x|Tui::split_w(false, pool_w, pool, x); let status = SequencerStatus::from(self); let with_status = |x|Tui::split_n(false, if self.status { 2 } else { 0 }, status, x); diff --git a/crates/tek/src/tui/arranger_command.rs b/crates/tek/src/tui/arranger_command.rs index 07801a73..6d79b798 100644 --- a/crates/tek/src/tui/arranger_command.rs +++ b/crates/tek/src/tui/arranger_command.rs @@ -47,10 +47,8 @@ pub enum ArrangerClipCommand { } input_to_command!(ArrangerCommand: |state: ArrangerTui, input|match input.event() { - // TODO: u: undo - key_pat!(Char('u')) => { todo!("undo") }, - // TODO: Shift-U: redo - key_pat!(Char('U')) => { todo!("redo") }, + key_pat!(Char('u')) => Self::History(-1), + key_pat!(Char('U')) => Self::History(1), // TODO: k: toggle on-screen keyboard key_pat!(Ctrl-Char('k')) => { todo!("keyboard") }, // Transport: Play/pause diff --git a/crates/tek/src/tui/phrase_length.rs b/crates/tek/src/tui/phrase_length.rs index e6d0129f..08ced486 100644 --- a/crates/tek/src/tui/phrase_length.rs +++ b/crates/tek/src/tui/phrase_length.rs @@ -1,5 +1,5 @@ use crate::*; -use super::phrase_list::{PhraseListModel, PhraseListMode}; +use super::pool::{PoolModel, PoolMode}; use PhraseLengthFocus::*; use PhraseLengthCommand::*; /// Displays and edits phrase length. @@ -88,9 +88,9 @@ pub enum PhraseLengthCommand { Inc, Dec, } -command!(|self:PhraseLengthCommand,state:PhraseListModel|{ +command!(|self:PhraseLengthCommand,state:PoolModel|{ match state.phrases_mode_mut().clone() { - Some(PhraseListMode::Length(phrase, ref mut length, ref mut focus)) => match self { + Some(PoolMode::Length(phrase, ref mut length, ref mut focus)) => match self { Cancel => { *state.phrases_mode_mut() = None; }, Prev => { focus.prev() }, Next => { focus.next() }, @@ -118,8 +118,8 @@ command!(|self:PhraseLengthCommand,state:PhraseListModel|{ }; None }); -input_to_command!(PhraseLengthCommand:|state:PhraseListModel,from|{ - if let Some(PhraseListMode::Length(_, length, _)) = state.phrases_mode() { +input_to_command!(PhraseLengthCommand:|state:PoolModel,from|{ + if let Some(PoolMode::Length(_, length, _)) = state.phrases_mode() { match from.event() { key_pat!(Up) => Self::Inc, key_pat!(Down) => Self::Dec, diff --git a/crates/tek/src/tui/phrase_rename.rs b/crates/tek/src/tui/phrase_rename.rs index 42233cfc..1b792f5b 100644 --- a/crates/tek/src/tui/phrase_rename.rs +++ b/crates/tek/src/tui/phrase_rename.rs @@ -8,11 +8,11 @@ pub enum PhraseRenameCommand { Set(String), } -impl Command for PhraseRenameCommand { - fn execute (self, state: &mut PhraseListModel) -> Perhaps { +impl Command for PhraseRenameCommand { + fn execute (self, state: &mut PoolModel) -> Perhaps { use PhraseRenameCommand::*; match state.phrases_mode_mut().clone() { - Some(PhraseListMode::Rename(phrase, ref mut old_name)) => match self { + Some(PoolMode::Rename(phrase, ref mut old_name)) => match self { Set(s) => { state.phrases()[phrase].write().unwrap().name = s.into(); return Ok(Some(Self::Set(old_name.clone()))) @@ -33,10 +33,10 @@ impl Command for PhraseRenameCommand { } } -impl InputToCommand for PhraseRenameCommand { - fn input_to_command (state: &PhraseListModel, from: &TuiInput) -> Option { +impl InputToCommand for PhraseRenameCommand { + fn input_to_command (state: &PoolModel, from: &TuiInput) -> Option { use KeyCode::{Char, Backspace, Enter, Esc}; - if let Some(PhraseListMode::Rename(_, ref old_name)) = state.phrases_mode() { + if let Some(PoolMode::Rename(_, ref old_name)) = state.phrases_mode() { Some(match from.event() { key_pat!(Char(c)) => { let mut new_name = old_name.clone(); diff --git a/crates/tek/src/tui/phrase_list.rs b/crates/tek/src/tui/pool.rs similarity index 84% rename from crates/tek/src/tui/phrase_list.rs rename to crates/tek/src/tui/pool.rs index 85c69ea3..53901fea 100644 --- a/crates/tek/src/tui/phrase_list.rs +++ b/crates/tek/src/tui/pool.rs @@ -7,14 +7,14 @@ use crate::{ }; #[derive(Debug)] -pub struct PhraseListModel { +pub struct PoolModel { pub(crate) visible: bool, /// Collection of phrases pub(crate) phrases: Vec>>, /// Selected phrase pub(crate) phrase: AtomicUsize, /// Mode switch - pub(crate) mode: Option, + pub(crate) mode: Option, /// Rendered size size: Measure, /// Scroll offset @@ -23,7 +23,7 @@ pub struct PhraseListModel { /// Modes for phrase pool #[derive(Debug, Clone)] -pub enum PhraseListMode { +pub enum PoolMode { /// Renaming a pattern Rename(usize, String), /// Editing the length of a pattern @@ -51,7 +51,7 @@ pub enum PhrasesCommand { Export(Browse), } -command!(|self:PhrasesCommand, state:PhraseListModel|{ +command!(|self:PhrasesCommand, state: PoolModel|{ use PhrasesCommand::*; match self { Show(visible) => { @@ -62,7 +62,7 @@ command!(|self:PhrasesCommand, state:PhraseListModel|{ 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) + PoolMode::Length(state.phrase_index(), length, PhraseLengthFocus::Bar) ); None }, @@ -72,7 +72,7 @@ command!(|self:PhrasesCommand, state:PhraseListModel|{ PhraseLengthCommand::Begin => { let name = state.phrases()[state.phrase_index()].read().unwrap().name.clone(); *state.phrases_mode_mut() = Some( - PhraseListMode::Rename(state.phrase_index(), name) + PoolMode::Rename(state.phrase_index(), name) ); None }, @@ -81,7 +81,7 @@ command!(|self:PhrasesCommand, state:PhraseListModel|{ Import(command) => match command { FileBrowserCommand::Begin => { *state.phrases_mode_mut() = Some( - PhraseListMode::Import(state.phrase_index(), FileBrowser::new(None)?) + PoolMode::Import(state.phrase_index(), FileBrowser::new(None)?) ); None }, @@ -90,7 +90,7 @@ command!(|self:PhrasesCommand, state:PhraseListModel|{ Export(command) => match command { FileBrowserCommand::Begin => { *state.phrases_mode_mut() = Some( - PhraseListMode::Export(state.phrase_index(), FileBrowser::new(None)?) + PoolMode::Export(state.phrase_index(), FileBrowser::new(None)?) ); None }, @@ -104,15 +104,15 @@ command!(|self:PhrasesCommand, state:PhraseListModel|{ } }); -input_to_command!(PhrasesCommand:|state:PhraseListModel,input|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)?), +input_to_command!(PhrasesCommand:|state: PoolModel,input|match state.phrases_mode() { + Some(PoolMode::Rename(..)) => Self::Rename(Rename::input_to_command(state, input)?), + Some(PoolMode::Length(..)) => Self::Length(Length::input_to_command(state, input)?), + Some(PoolMode::Import(..)) => Self::Import(Browse::input_to_command(state, input)?), + Some(PoolMode::Export(..)) => Self::Export(Browse::input_to_command(state, input)?), _ => to_phrases_command(state, input)? }); -fn to_phrases_command (state: &PhraseListModel, input: &TuiInput) -> Option { +fn to_phrases_command (state: &PoolModel, input: &TuiInput) -> Option { use KeyCode::{Up, Down, Delete, Char}; use PhrasesCommand as Cmd; let index = state.phrase_index(); @@ -161,7 +161,7 @@ fn to_phrases_command (state: &PhraseListModel, input: &TuiInput) -> Option return None }) } -impl Default for PhraseListModel { +impl Default for PoolModel { fn default () -> Self { Self { visible: true, @@ -173,32 +173,32 @@ impl Default for PhraseListModel { } } } -from!(|phrase:&Arc>|PhraseListModel = { +from!(|phrase:&Arc>|PoolModel = { let mut model = Self::default(); model.phrases.push(phrase.clone()); model.phrase.store(1, Relaxed); model }); -has_phrases!(|self:PhraseListModel|self.phrases); -has_phrase!(|self:PhraseListModel|self.phrases[self.phrase_index()]); -impl PhraseListModel { +has_phrases!(|self: PoolModel|self.phrases); +has_phrase!(|self: PoolModel|self.phrases[self.phrase_index()]); +impl PoolModel { pub(crate) fn phrase_index (&self) -> usize { self.phrase.load(Relaxed) } pub(crate) fn set_phrase_index (&self, value: usize) { self.phrase.store(value, Relaxed); } - pub(crate) fn phrases_mode (&self) -> &Option { + pub(crate) fn phrases_mode (&self) -> &Option { &self.mode } - pub(crate) fn phrases_mode_mut (&mut self) -> &mut Option { + pub(crate) fn phrases_mode_mut (&mut self) -> &mut Option { &mut self.mode } } -pub struct PhraseListView<'a>(pub(crate) &'a PhraseListModel); +pub struct PoolView<'a>(pub(crate) &'a PoolModel); // TODO: Display phrases always in order of appearance -render!(|self: PhraseListView<'a>|{ - let PhraseListModel { phrases, mode, .. } = self.0; +render!(|self: PoolView<'a>|{ + let PoolModel { phrases, mode, .. } = self.0; let bg = TuiTheme::g(32); let title_color = TuiTheme::ti1(); let upper_left = "Pool:"; @@ -208,13 +208,13 @@ render!(|self: PhraseListView<'a>|{ add(&Fill::wh(Outer(Style::default().fg(color.base.rgb).bg(bg))))?; //add(&Lozenge(Style::default().bg(border_bg).fg(border_color)))?; add(&Tui::inset_xy(0, 1, Fill::wh(col!(move|add|match mode { - Some(PhraseListMode::Import(_, ref file_picker)) => add(file_picker), - Some(PhraseListMode::Export(_, ref file_picker)) => add(file_picker), + Some(PoolMode::Import(_, ref file_picker)) => add(file_picker), + Some(PoolMode::Export(_, ref file_picker)) => add(file_picker), _ => Ok(for (i, phrase) in phrases.iter().enumerate() { add(&lay!(|add|{ let Phrase { ref name, color, length, .. } = *phrase.read().unwrap(); let mut length = PhraseLength::new(length, None); - if let Some(PhraseListMode::Length(phrase, new_length, focus)) = mode { + if let Some(PoolMode::Length(phrase, new_length, focus)) = mode { if i == *phrase { length.pulses = *new_length; length.focus = Some(*focus); @@ -227,7 +227,7 @@ render!(|self: PhraseListView<'a>|{ })), Tui::bold(true, { let mut row2 = format!(" {name}"); - if let Some(PhraseListMode::Rename(phrase, _)) = mode { + if let Some(PoolMode::Rename(phrase, _)) = mode { if i == *phrase { row2 = format!("{row2}▄"); } @@ -305,8 +305,8 @@ impl PhraseSelector { Self { title: " Next|", time, name, color, } } } -command!(|self: FileBrowserCommand, state: PhraseListModel|{ - use PhraseListMode::*; +command!(|self: FileBrowserCommand, state: PoolModel|{ + use PoolMode::*; use FileBrowserCommand::*; let mode = &mut state.mode; match mode { @@ -334,11 +334,11 @@ command!(|self: FileBrowserCommand, state: PhraseListModel|{ }; None }); -input_to_command!(FileBrowserCommand:|state:PhraseListModel,from|{ +input_to_command!(FileBrowserCommand:|state: PoolModel,from|{ use FileBrowserCommand::*; use KeyCode::{Up, Down, Left, Right, Enter, Esc, Backspace, Char}; - if let Some(PhraseListMode::Import(_index, browser)) = &state.mode { + if let Some(PoolMode::Import(_index, browser)) = &state.mode { match from.event() { key_pat!(Up) => Select(browser.index.overflowing_sub(1).0 .min(browser.len().saturating_sub(1))), @@ -352,7 +352,7 @@ input_to_command!(FileBrowserCommand:|state:PhraseListModel,from|{ key_pat!(Esc) => Cancel, _ => return None } - } else if let Some(PhraseListMode::Export(_index, browser)) = &state.mode { + } else if let Some(PoolMode::Export(_index, browser)) = &state.mode { match from.event() { key_pat!(Up) => Select(browser.index.overflowing_sub(1).0 .min(browser.len())),