diff --git a/midi/src/lib.rs b/midi/src/lib.rs index 7293dea4..d9d9393c 100644 --- a/midi/src/lib.rs +++ b/midi/src/lib.rs @@ -10,7 +10,6 @@ mod midi_point; pub use midi_point::*; mod midi_view; pub use midi_view::*; mod midi_pool; pub use midi_pool::*; -mod midi_pool_tui; pub use midi_pool_tui::*; mod midi_pool_cmd; pub use midi_pool_cmd::*; mod midi_edit; pub use midi_edit::*; diff --git a/midi/src/midi_pool.rs b/midi/src/midi_pool.rs index 250d49a2..1fafb047 100644 --- a/midi/src/midi_pool.rs +++ b/midi/src/midi_pool.rs @@ -68,10 +68,10 @@ impl MidiPool { pub(crate) fn set_clip_index (&self, value: usize) { self.clip.store(value, Relaxed); } - pub(crate) fn clips_mode (&self) -> &Option { + pub(crate) fn mode (&self) -> &Option { &self.mode } - pub(crate) fn clips_mode_mut (&mut self) -> &mut Option { + pub(crate) fn mode_mut (&mut self) -> &mut Option { &mut self.mode } pub fn file_picker (&self) -> Option<&FileBrowser> { @@ -81,6 +81,35 @@ impl MidiPool { _ => None } } + pub fn begin_clip_length (&mut self) { + let length = self.clips()[self.clip_index()].read().unwrap().length; + *self.mode_mut() = Some(PoolMode::Length( + self.clip_index(), + length, + ClipLengthFocus::Bar + )); + } + pub fn begin_clip_rename (&mut self) { + let name = self.clips()[self.clip_index()].read().unwrap().name.clone(); + *self.mode_mut() = Some(PoolMode::Rename( + self.clip_index(), + name + )); + } + pub fn begin_import (&mut self) -> Usually<()> { + *self.mode_mut() = Some(PoolMode::Import( + self.clip_index(), + FileBrowser::new(None)? + )); + Ok(()) + } + pub fn begin_export (&mut self) -> Usually<()> { + *self.mode_mut() = Some(PoolMode::Export( + self.clip_index(), + FileBrowser::new(None)? + )); + Ok(()) + } } /// Displays and edits clip length. #[derive(Clone)] @@ -135,3 +164,43 @@ impl ClipLengthFocus { *self = match self { Self::Bar => Self::Tick, Self::Beat => Self::Bar, Self::Tick => Self::Beat, } } } +pub struct PoolView<'a>(pub bool, pub &'a MidiPool); +content!(TuiOut: |self: PoolView<'a>| { + let Self(compact, model) = self; + let MidiPool { clips, mode, .. } = self.1; + let color = self.1.clip().map(|c|c.read().unwrap().color).unwrap_or_else(||TuiTheme::g(32).into()); + let on_bg = |x|x;//Bsp::b(Repeat(" "), Tui::bg(color.darkest.rgb, x)); + let border = |x|x;//Outer(Style::default().fg(color.dark.rgb).bg(color.darkest.rgb)).enclose(x); + let iter = | |model.clips().clone().into_iter(); + Tui::bg(Color::Reset, Fixed::y(clips.read().unwrap().len() as u16, on_bg(border(Map::new(iter, move|clip, i|{ + let item_height = 1; + let item_offset = i as u16 * item_height; + let selected = i == model.clip_index(); + let MidiClip { ref name, color, length, .. } = *clip.read().unwrap(); + let bg = if selected { color.light.rgb } else { color.base.rgb }; + let fg = color.lightest.rgb; + let name = if *compact { format!(" {i:>3}") } else { format!(" {i:>3} {name}") }; + let length = if *compact { String::default() } else { format!("{length} ") }; + Fixed::y(1, map_south(item_offset, item_height, Tui::bg(bg, lay!( + Fill::x(Align::w(Tui::fg(fg, Tui::bold(selected, name)))), + Fill::x(Align::e(Tui::fg(fg, Tui::bold(selected, length)))), + Fill::x(Align::w(When::new(selected, Tui::bold(true, Tui::fg(TuiTheme::g(255), "▶"))))), + Fill::x(Align::e(When::new(selected, Tui::bold(true, Tui::fg(TuiTheme::g(255), "◀"))))), + )))) + }))))) +}); +content!(TuiOut: |self: ClipLength| { + let bars = ||self.bars_string(); + let beats = ||self.beats_string(); + let ticks = ||self.ticks_string(); + match self.focus { + None => + row!(" ", bars(), ".", beats(), ".", ticks()), + Some(ClipLengthFocus::Bar) => + row!("[", bars(), "]", beats(), ".", ticks()), + Some(ClipLengthFocus::Beat) => + row!(" ", bars(), "[", beats(), "]", ticks()), + Some(ClipLengthFocus::Tick) => + row!(" ", bars(), ".", beats(), "[", ticks()), + } +}); diff --git a/midi/src/midi_pool_cmd.rs b/midi/src/midi_pool_cmd.rs index 70919214..d99641b3 100644 --- a/midi/src/midi_pool_cmd.rs +++ b/midi/src/midi_pool_cmd.rs @@ -1,5 +1,32 @@ use crate::*; use KeyCode::*; +impl PoolCommand { + const KEYS_POOL: &str = include_str!("midi_pool_keys.edn"); + const KEYS_CLIP: &str = include_str!("midi_pool_keys_clip.edn"); + const KEYS_RENAME: &str = include_str!("midi_pool_keys_rename.edn"); + const KEYS_LENGTH: &str = include_str!("midi_pool_keys_length.edn"); + const KEYS_FILE: &str = include_str!("midi_pool_keys_file.edn"); + pub fn from_tui_event (state: &MidiPool, input: &Event) -> Usually> { + use EdnItem::*; + let edns: Vec> = EdnItem::read_all(match state.mode() { + Some(PoolMode::Rename(..)) => Self::KEYS_RENAME, + Some(PoolMode::Length(..)) => Self::KEYS_LENGTH, + Some(PoolMode::Import(..)) | Some(PoolMode::Export(..)) => Self::KEYS_FILE, + _ => Self::KEYS_CLIP + })?; + for item in edns { + match item { + Exp(e) => match e.as_slice() { + [Sym(key), Key(command), args @ ..] => { + } + _ => panic!("invalid config") + } + _ => panic!("invalid config") + } + } + Ok(None) + } +} edn_provide!(bool: |self: MidiPool| {}); edn_provide!(usize: |self: MidiPool| {}); edn_provide!(MidiClip: |self: MidiPool| {}); @@ -7,9 +34,8 @@ edn_provide!(PathBuf: |self: MidiPool| {}); edn_provide!(Arc: |self: MidiPool| {}); edn_provide!(ItemColor: |self: MidiPool| {}); #[derive(Clone, PartialEq, Debug)] pub enum PoolCommand { + /// Toggle visibility of pool Show(bool), - /// Update the contents of the clip pool - Clip(PoolClipCommand), /// Select a clip from the clip pool Select(usize), /// Rename a clip @@ -20,15 +46,33 @@ edn_provide!(ItemColor: |self: MidiPool| {}); Import(FileBrowserCommand), /// Export to file Export(FileBrowserCommand), + /// Update the contents of the clip pool + Clip(PoolClipCommand), } edn_command!(PoolCommand: |state: MidiPool| { ("show" [a: bool] Self::Show(a.expect("no flag"))) ("select" [i: usize] Self::Select(i.expect("no index"))) - ("clip" [a, ..b] Self::Clip(PoolClipCommand::from_edn(state, &a.to_ref(), b))) ("rename" [a, ..b] Self::Rename(ClipRenameCommand::from_edn(state, &a.to_ref(), b))) ("length" [a, ..b] Self::Length(ClipLengthCommand::from_edn(state, &a.to_ref(), b))) ("import" [a, ..b] Self::Import(FileBrowserCommand::from_edn(state, &a.to_ref(), b))) ("export" [a, ..b] Self::Export(FileBrowserCommand::from_edn(state, &a.to_ref(), b))) + ("clip" [a, ..b] Self::Clip(PoolClipCommand::from_edn(state, &a.to_ref(), b))) +}); +command!(|self: PoolCommand, state: MidiPool|{ + use PoolCommand::*; + match self { + Rename(ClipRenameCommand::Begin) => { state.begin_clip_rename(); None } + Rename(command) => command.delegate(state, Rename)?, + Length(ClipLengthCommand::Begin) => { state.begin_clip_length(); None }, + Length(command) => command.delegate(state, Length)?, + Import(FileBrowserCommand::Begin) => { state.begin_import()?; None }, + Import(command) => command.delegate(state, Import)?, + Export(FileBrowserCommand::Begin) => { state.begin_export()?; None }, + Export(command) => command.delegate(state, Export)?, + Clip(command) => command.execute(state)?.map(Clip), + Show(visible) => { state.visible = visible; Some(Self::Show(!visible)) }, + Select(clip) => { state.set_clip_index(clip); None }, + } }); #[derive(Clone, Debug, PartialEq)] pub enum PoolClipCommand { Add(usize, MidiClip), @@ -41,53 +85,15 @@ edn_command!(PoolCommand: |state: MidiPool| { SetColor(usize, ItemColor), } edn_command!(PoolClipCommand: |state: MidiPool| { - ("add" [i: usize, c: MidiClip] Self::Add(i.expect("no index"), c.expect("no clip"))) - ("delete" [i: usize] Self::Delete(i.expect("no index"))) - ("swap" [a: usize, b: usize] Self::Swap(a.expect("no index"), b.expect("no index"))) - ("import" [i: usize, p: PathBuf] Self::Import(i.expect("no index"), p.expect("no path"))) - ("export" [i: usize, p: PathBuf] Self::Export(i.expect("no index"), p.expect("no path"))) - ("set-name" [i: usize, n: Arc] Self::SetName(i.expect("no index"), n.expect("no name"))) - ("set-length" [i: usize, l: usize] Self::SetLength(i.expect("no index"), l.expect("no length"))) + ("add" [i: usize, c: MidiClip] Self::Add(i.expect("no index"), c.expect("no clip"))) + ("delete" [i: usize] Self::Delete(i.expect("no index"))) + ("swap" [a: usize, b: usize] Self::Swap(a.expect("no index"), b.expect("no index"))) + ("import" [i: usize, p: PathBuf] Self::Import(i.expect("no index"), p.expect("no path"))) + ("export" [i: usize, p: PathBuf] Self::Export(i.expect("no index"), p.expect("no path"))) + ("set-name" [i: usize, n: Arc] Self::SetName(i.expect("no index"), n.expect("no name"))) + ("set-length" [i: usize, l: usize] Self::SetLength(i.expect("no index"), l.expect("no length"))) ("set-color" [i: usize, c: ItemColor] Self::SetColor(i.expect("no index"), c.expect("no color"))) }); -#[derive(Clone, Debug, PartialEq)] pub enum ClipRenameCommand { - Begin, - Cancel, - Confirm, - Set(Arc), -} -edn_command!(ClipRenameCommand: |state: MidiPool| { - ("begin" [] Self::Begin) - ("cancel" [] Self::Cancel) - ("confirm" [] Self::Confirm) - ("set" [n: Arc] Self::Set(n.expect("no name"))) -}); -#[derive(Copy, Clone, Debug, PartialEq)] pub enum ClipLengthCommand { - Begin, - Cancel, - Set(usize), - Next, - Prev, - Inc, - Dec, -} -edn_command!(ClipLengthCommand: |state: MidiPool| { - ("begin" [] Self::Begin) - ("cancel" [] Self::Cancel) - ("next" [] Self::Next) - ("prev" [] Self::Prev) - ("inc" [] Self::Inc) - ("dec" [] Self::Dec) - ("set" [l: usize] Self::Set(l.expect("no length"))) -}); -edn_command!(FileBrowserCommand: |state: MidiPool| { - ("begin" [] Self::Begin) - ("cancel" [] Self::Cancel) - ("confirm" [] Self::Confirm) - ("select" [i: usize] Self::Select(i.expect("no index"))) - ("chdir" [p: PathBuf] Self::Chdir(p.expect("no path"))) - ("filter" [f: Arc] Self::Filter(f.expect("no filter"))) -}); impl Command for PoolClipCommand { fn execute (self, model: &mut T) -> Perhaps { use PoolClipCommand::*; @@ -153,65 +159,144 @@ impl Command for PoolClipCommand { }) } } -command!(|self: PoolCommand, state: MidiPool|{ - use PoolCommand::*; - match self { - Show(visible) => { - state.visible = visible; - Some(Self::Show(!visible)) +#[derive(Clone, Debug, PartialEq)] pub enum ClipRenameCommand { + Begin, + Cancel, + Confirm, + Set(Arc), +} +edn_command!(ClipRenameCommand: |state: MidiPool| { + ("begin" [] Self::Begin) + ("cancel" [] Self::Cancel) + ("confirm" [] Self::Confirm) + ("set" [n: Arc] Self::Set(n.expect("no name"))) +}); +command!(|self: ClipRenameCommand, state: MidiPool|{ + use ClipRenameCommand::*; + if let Some( + PoolMode::Rename(clip, ref mut old_name) + ) = state.mode_mut().clone() { + match self { + Set(s) => { + state.clips()[clip].write().unwrap().name = s; + return Ok(Some(Self::Set(old_name.clone().into()))) + }, + Confirm => { + let old_name = old_name.clone(); + *state.mode_mut() = None; + return Ok(Some(Self::Set(old_name))) + }, + Cancel => { + state.clips()[clip].write().unwrap().name = old_name.clone().into(); + return Ok(None) + }, + _ => unreachable!() } - Rename(command) => match command { - ClipRenameCommand::Begin => { - let length = state.clips()[state.clip_index()].read().unwrap().length; - *state.clips_mode_mut() = Some( - PoolMode::Length(state.clip_index(), length, ClipLengthFocus::Bar) - ); - None - }, - _ => command.execute(state)?.map(Rename) - }, - Length(command) => match command { - ClipLengthCommand::Begin => { - let name = state.clips()[state.clip_index()].read().unwrap().name.clone(); - *state.clips_mode_mut() = Some( - PoolMode::Rename(state.clip_index(), name) - ); - None - }, - _ => command.execute(state)?.map(Length) - }, - Import(command) => match command { - FileBrowserCommand::Begin => { - *state.clips_mode_mut() = Some( - PoolMode::Import(state.clip_index(), FileBrowser::new(None)?) - ); - None - }, - _ => command.execute(state)?.map(Import) - }, - Export(command) => match command { - FileBrowserCommand::Begin => { - *state.clips_mode_mut() = Some( - PoolMode::Export(state.clip_index(), FileBrowser::new(None)?) - ); - None - }, - _ => command.execute(state)?.map(Export) - }, - Select(clip) => { - state.set_clip_index(clip); - None - }, - Clip(command) => command.execute(state)?.map(Clip), + } else { + unreachable!() } }); -input_to_command!(PoolCommand: |state: MidiPool, input: Event|match state.clips_mode() { +#[derive(Copy, Clone, Debug, PartialEq)] pub enum ClipLengthCommand { + Begin, + Cancel, + Set(usize), + Next, + Prev, + Inc, + Dec, +} +edn_command!(ClipLengthCommand: |state: MidiPool| { + ("begin" [] Self::Begin) + ("cancel" [] Self::Cancel) + ("next" [] Self::Next) + ("prev" [] Self::Prev) + ("inc" [] Self::Inc) + ("dec" [] Self::Dec) + ("set" [l: usize] Self::Set(l.expect("no length"))) +}); +command!(|self: ClipLengthCommand, state: MidiPool|{ + use ClipLengthCommand::*; + use ClipLengthFocus::*; + if let Some( + PoolMode::Length(clip, ref mut length, ref mut focus) + ) = state.mode_mut().clone() { + match self { + Cancel => { *state.mode_mut() = None; }, + Prev => { focus.prev() }, + Next => { focus.next() }, + Inc => match focus { + Bar => { *length += 4 * PPQ }, + Beat => { *length += PPQ }, + Tick => { *length += 1 }, + }, + Dec => 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 old_length = None; + { + let clip = state.clips()[clip].clone();//.write().unwrap(); + old_length = Some(clip.read().unwrap().length); + clip.write().unwrap().length = length; + } + *state.mode_mut() = None; + return Ok(old_length.map(Self::Set)) + }, + _ => unreachable!() + } + } else { + unreachable!(); + } + None +}); +edn_command!(FileBrowserCommand: |state: MidiPool| { + ("begin" [] Self::Begin) + ("cancel" [] Self::Cancel) + ("confirm" [] Self::Confirm) + ("select" [i: usize] Self::Select(i.expect("no index"))) + ("chdir" [p: PathBuf] Self::Chdir(p.expect("no path"))) + ("filter" [f: Arc] Self::Filter(f.expect("no filter"))) +}); +command!(|self: FileBrowserCommand, state: MidiPool|{ + use PoolMode::*; + use FileBrowserCommand::*; + let mode = &mut state.mode; + 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; + PoolClipCommand::Import(index, path).execute(state)?; + } else if browser.is_dir() { + *mode = Some(Import(*index, browser.chdir()?)); + }, + _ => todo!(), + }, + Some(Export(index, ref mut browser)) => match self { + Cancel => { *mode = None; }, + Chdir(cwd) => { *mode = Some(Export(*index, FileBrowser::new(Some(cwd))?)); }, + Select(index) => { browser.index = index; }, + _ => unreachable!() + }, + _ => unreachable!(), + }; + None +}); +/////////////////////////////////////////////////////////////////////////////////////////////////// +input_to_command!(PoolCommand: |state: MidiPool, input: Event|match state.mode() { Some(PoolMode::Rename(..)) => Self::Rename(ClipRenameCommand::input_to_command(state, input)?), Some(PoolMode::Length(..)) => Self::Length(ClipLengthCommand::input_to_command(state, input)?), Some(PoolMode::Import(..)) => Self::Import(FileBrowserCommand::input_to_command(state, input)?), Some(PoolMode::Export(..)) => Self::Export(FileBrowserCommand::input_to_command(state, input)?), _ => to_clips_command(state, input)? }); +/////////////////////////////////////////////////////////////////////////////////////////////////// fn to_clips_command (state: &MidiPool, input: &Event) -> Option { use KeyCode::{Up, Down, Delete, Char}; use PoolCommand as Cmd; @@ -261,46 +346,17 @@ fn to_clips_command (state: &MidiPool, input: &Event) -> Option { _ => return None }) } -command!(|self: FileBrowserCommand, state: MidiPool|{ - use PoolMode::*; - use FileBrowserCommand::*; - let mode = &mut state.mode; - 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; - PoolClipCommand::Import(index, path).execute(state)?; - } else if browser.is_dir() { - *mode = Some(Import(*index, browser.chdir()?)); - }, - _ => todo!(), - }, - Some(Export(index, ref mut browser)) => match self { - Cancel => { *mode = None; }, - Chdir(cwd) => { *mode = Some(Export(*index, FileBrowser::new(Some(cwd))?)); }, - Select(index) => { browser.index = index; }, - _ => unreachable!() - }, - _ => unreachable!(), - }; - None -}); +/////////////////////////////////////////////////////////////////////////////////////////////////// input_to_command!(FileBrowserCommand: |state: MidiPool, input: Event|{ use FileBrowserCommand::*; use KeyCode::{Up, Down, Left, Right, Enter, Esc, Backspace, Char}; if let Some(PoolMode::Import(_index, browser)) = &state.mode { match input { - kpat!(Up) => Select(browser.index.overflowing_sub(1).0 - .min(browser.len().saturating_sub(1))), - kpat!(Down) => Select(browser.index.saturating_add(1) - % browser.len()), - kpat!(Right) => Chdir(browser.cwd.clone()), - kpat!(Left) => Chdir(browser.cwd.clone()), + kpat!(Up) => Select(browser.index.overflowing_sub(1).0.min(browser.len().saturating_sub(1))), + kpat!(Down) => Select(browser.index.saturating_add(1)% browser.len()), + kpat!(Right) => Chdir(browser.cwd.clone()), + kpat!(Left) => Chdir(browser.cwd.clone()), + kpat!(Enter) => Confirm, kpat!(Char(_)) => { todo!() }, kpat!(Backspace) => { todo!() }, @@ -309,12 +365,11 @@ input_to_command!(FileBrowserCommand: |state: MidiPool, input: Event|{ } } else if let Some(PoolMode::Export(_index, browser)) = &state.mode { match input { - kpat!(Up) => Select(browser.index.overflowing_sub(1).0 - .min(browser.len())), - kpat!(Down) => Select(browser.index.saturating_add(1) - % browser.len()), - kpat!(Right) => Chdir(browser.cwd.clone()), - kpat!(Left) => Chdir(browser.cwd.clone()), + kpat!(Up) => Select(browser.index.overflowing_sub(1).0.min(browser.len())), + kpat!(Down) => Select(browser.index.saturating_add(1) % browser.len()), + kpat!(Right) => Chdir(browser.cwd.clone()), + kpat!(Left) => Chdir(browser.cwd.clone()), + kpat!(Enter) => Confirm, kpat!(Char(_)) => { todo!() }, kpat!(Backspace) => { todo!() }, @@ -325,42 +380,9 @@ input_to_command!(FileBrowserCommand: |state: MidiPool, input: Event|{ unreachable!() } }); -command!(|self: ClipLengthCommand,state:MidiPool|{ - use ClipLengthCommand::*; - use ClipLengthFocus::*; - match state.clips_mode_mut().clone() { - Some(PoolMode::Length(clip, ref mut length, ref mut focus)) => match self { - Cancel => { *state.clips_mode_mut() = 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 old_length = None; - { - let mut clip = state.clips()[clip].clone();//.write().unwrap(); - old_length = Some(clip.read().unwrap().length); - clip.write().unwrap().length = length; - } - *state.clips_mode_mut() = None; - return Ok(old_length.map(Self::Set)) - }, - _ => unreachable!() - }, - _ => unreachable!() - }; - None -}); +/////////////////////////////////////////////////////////////////////////////////////////////////// input_to_command!(ClipLengthCommand: |state: MidiPool, input: Event|{ - if let Some(PoolMode::Length(_, length, _)) = state.clips_mode() { + if let Some(PoolMode::Length(_, length, _)) = state.mode() { match input { kpat!(Up) => Self::Inc, kpat!(Down) => Self::Dec, @@ -374,10 +396,11 @@ input_to_command!(ClipLengthCommand: |state: MidiPool, input: Event|{ unreachable!() } }); +/////////////////////////////////////////////////////////////////////////////////////////////////// impl InputToCommand for ClipRenameCommand { fn input_to_command (state: &MidiPool, input: &Event) -> Option { use KeyCode::{Char, Backspace, Enter, Esc}; - if let Some(PoolMode::Rename(_, ref old_name)) = state.clips_mode() { + if let Some(PoolMode::Rename(_, ref old_name)) = state.mode() { Some(match input { kpat!(Char(c)) => { let mut new_name = old_name.clone().to_string(); @@ -398,27 +421,4 @@ impl InputToCommand for ClipRenameCommand { } } } -impl Command for ClipRenameCommand { - fn execute (self, state: &mut MidiPool) -> Perhaps { - use ClipRenameCommand::*; - match state.clips_mode_mut().clone() { - Some(PoolMode::Rename(clip, ref mut old_name)) => match self { - Set(s) => { - state.clips()[clip].write().unwrap().name = s; - return Ok(Some(Self::Set(old_name.clone().into()))) - }, - Confirm => { - let old_name = old_name.clone(); - *state.clips_mode_mut() = None; - return Ok(Some(Self::Set(old_name))) - }, - Cancel => { - state.clips()[clip].write().unwrap().name = old_name.clone().into(); - }, - _ => unreachable!() - }, - _ => unreachable!() - }; - Ok(None) - } -} +/////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/midi/src/midi_pool_keys_clip.edn b/midi/src/midi_pool_keys_clip.edn new file mode 100644 index 00000000..e69de29b diff --git a/midi/src/midi_pool_keys_file.edn b/midi/src/midi_pool_keys_file.edn new file mode 100644 index 00000000..e69de29b diff --git a/midi/src/midi_pool_keys_length.edn b/midi/src/midi_pool_keys_length.edn new file mode 100644 index 00000000..e69de29b diff --git a/midi/src/midi_pool_keys_rename.edn b/midi/src/midi_pool_keys_rename.edn new file mode 100644 index 00000000..e69de29b diff --git a/midi/src/midi_pool_tui.rs b/midi/src/midi_pool_tui.rs deleted file mode 100644 index 6256f73c..00000000 --- a/midi/src/midi_pool_tui.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::*; -pub struct PoolView<'a>(pub bool, pub &'a MidiPool); -content!(TuiOut: |self: PoolView<'a>| { - let Self(compact, model) = self; - let MidiPool { clips, mode, .. } = self.1; - let color = self.1.clip().map(|c|c.read().unwrap().color).unwrap_or_else(||TuiTheme::g(32).into()); - let on_bg = |x|x;//Bsp::b(Repeat(" "), Tui::bg(color.darkest.rgb, x)); - let border = |x|x;//Outer(Style::default().fg(color.dark.rgb).bg(color.darkest.rgb)).enclose(x); - let iter = | |model.clips().clone().into_iter(); - Tui::bg(Color::Reset, Fixed::y(clips.read().unwrap().len() as u16, on_bg(border(Map::new(iter, move|clip, i|{ - let item_height = 1; - let item_offset = i as u16 * item_height; - let selected = i == model.clip_index(); - let MidiClip { ref name, color, length, .. } = *clip.read().unwrap(); - let bg = if selected { color.light.rgb } else { color.base.rgb }; - let fg = color.lightest.rgb; - let name = if *compact { format!(" {i:>3}") } else { format!(" {i:>3} {name}") }; - let length = if *compact { String::default() } else { format!("{length} ") }; - Fixed::y(1, map_south(item_offset, item_height, Tui::bg(bg, lay!( - Fill::x(Align::w(Tui::fg(fg, Tui::bold(selected, name)))), - Fill::x(Align::e(Tui::fg(fg, Tui::bold(selected, length)))), - Fill::x(Align::w(When::new(selected, Tui::bold(true, Tui::fg(TuiTheme::g(255), "▶"))))), - Fill::x(Align::e(When::new(selected, Tui::bold(true, Tui::fg(TuiTheme::g(255), "◀"))))), - )))) - }))))) -}); -content!(TuiOut: |self: ClipLength| { - let bars = ||self.bars_string(); - let beats = ||self.beats_string(); - let ticks = ||self.ticks_string(); - match self.focus { - None => - row!(" ", bars(), ".", beats(), ".", ticks()), - Some(ClipLengthFocus::Bar) => - row!("[", bars(), "]", beats(), ".", ticks()), - Some(ClipLengthFocus::Beat) => - row!(" ", bars(), "[", beats(), "]", ticks()), - Some(ClipLengthFocus::Tick) => - row!(" ", bars(), ".", beats(), "[", ticks()), - } -});