use edn_command on all midi pool commands

This commit is contained in:
🪞👃🪞 2025-01-14 17:34:10 +01:00
parent efbabe6248
commit 50b7d8a23d
8 changed files with 194 additions and 188 deletions

View file

@ -4,14 +4,14 @@ use crate::*;
($lt:lifetime: $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<$lt> EdnProvide<$lt, $type> for $State {
fn get <S: AsRef<str>> (&$lt $self, edn: &$lt EdnItem<S>) -> Option<$type> {
Some(match edn.to_ref() { $(EdnItem::Sym($pat) => $expr),*, _ => return None })
Some(match edn.to_ref() { $(EdnItem::Sym($pat) => $expr,)* _ => return None })
}
}
};
($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a> EdnProvide<'a, $type> for $State {
fn get <S: AsRef<str>> (&'a $self, edn: &'a EdnItem<S>) -> Option<$type> {
Some(match edn.to_ref() { $(EdnItem::Sym($pat) => $expr),*, _ => return None })
Some(match edn.to_ref() { $(EdnItem::Sym($pat) => $expr,)* _ => return None })
}
}
};

View file

@ -13,11 +13,8 @@ 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::*;
mod midi_edit_cmd; pub use midi_edit_cmd::*;
mod midi_edit_tui; pub use midi_edit_tui::*;
mod piano_h; pub use self::piano_h::*;
mod midi_edit; pub use midi_edit::*;
mod piano_h; pub use self::piano_h::*;
pub(crate) use ::tek_time::*;
pub(crate) use ::tek_jack::{*, jack::*};

View file

@ -1,4 +1,6 @@
use crate::*;
use self::MidiEditCommand::*;
use KeyCode::*;
pub trait HasEditor {
fn editor (&self) -> &Option<MidiEditor>;
fn editor_mut (&mut self) -> &Option<MidiEditor>;
@ -46,7 +48,7 @@ impl Default for MidiEditor {
mode: PianoHorizontal::new(None),
size: Measure::new(),
keymap: EdnKeymap(
EdnItem::<String>::read_all(include_str!("midi_editor_keys.edn"))
EdnItem::<String>::read_all(include_str!("midi_edit_keys.edn"))
.expect("failed to load keymap for MidiEditor")
)
}
@ -126,3 +128,142 @@ impl MidiViewer for MidiEditor {
fn clip_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> { self.mode.clip_mut() }
fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) }
}
has_size!(<TuiOut>|self: MidiEditor|&self.size);
content!(TuiOut: |self: MidiEditor| {
self.autoscroll();
//self.autozoom();
self.size.of(&self.mode)
});
impl MidiEditor {
pub fn clip_status (&self) -> impl Content<TuiOut> + '_ {
let (color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.name.clone(), clip.length, clip.looped)
} else {
(ItemPalette::from(TuiTheme::g(64)), String::new().into(), 0, false)
};
row!(
FieldV(color, "Edit", format!("{name} ({length})")),
FieldV(color, "Loop", looped.to_string())
)
}
pub fn edit_status (&self) -> impl Content<TuiOut> + '_ {
let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.length)
} else {
(ItemPalette::from(TuiTheme::g(64)), 0)
};
let time_point = self.time_point();
let time_zoom = self.time_zoom().get();
let time_lock = if self.time_lock().get() { "[lock]" } else { " " };
let note_point = format!("{:>3}", self.note_point());
let note_name = format!("{:4}", Note::pitch_to_name(self.note_point()));
let note_len = format!("{:>4}", self.note_len());
Bsp::e(
FieldV(color, "Time", format!("{length}/{time_zoom}+{time_point} {time_lock}")),
FieldV(color, "Note", format!("{note_name} {note_point} {note_len}")),
)
}
}
edn_provide!(bool: |self: MidiEditor|{
":true" => true,
":false" => false
});
edn_provide!(usize: |self: MidiEditor|{
":note-length" => self.note_len(),
":note-point" => self.note_point(),
":time-point" => self.time_point(),
":time-zoom" => self.time_zoom().get(),
});
edn_command!(MidiEditCommand: |state: MidiEditor| {
("note/put" [a: bool] Self::PutNote)
("note/del" [a: bool] Self::PutNote)
("note/pos" [a: usize] Self::SetNoteCursor(a.expect("no note cursor")))
("note/len" [a: usize] Self::SetNoteLength(a.expect("no note length")))
("time/pos" [a: usize] Self::SetTimeCursor(a.expect("no time cursor")))
("time/zoom" [a: usize] Self::SetTimeZoom(a.expect("no time zoom")))
("time/lock" [a: bool] Self::SetTimeLock(a.expect("no time lock")))
});
//impl EdnCommand<MidiEditor> for MidiEditCommand {
//fn from_edn <'a> (state: &MidiEditor, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
//use EdnItem::*;
//match (head, tail) {
//(Key("note/put"), [a]) => Self::PutNote,
//(Key("note/del"), [a]) => Self::AppendNote,
//(Key("note/dur"), [a]) => Self::AppendNote,
//(Key("note/range"), [a]) => Self::AppendNote,
//(Key("note/pos"), [a]) => Self::AppendNote,
//(Key("time/pos"), [a]) => Self::AppendNote,
//(Key("time/zoom"), [a]) => Self::AppendNote,
//(Key("time/lock"), [a]) => Self::AppendNote,
//_ => todo!()
//}
//}
//}
#[derive(Clone, Debug)] pub enum MidiEditCommand {
// TODO: 1-9 seek markers that by default start every 8th of the clip
AppendNote,
PutNote,
SetNoteCursor(usize),
SetNoteLength(usize),
SetNoteScroll(usize),
SetTimeCursor(usize),
SetTimeScroll(usize),
SetTimeZoom(usize),
SetTimeLock(bool),
Show(Option<Arc<RwLock<MidiClip>>>),
}
handle!(TuiIn: |self: MidiEditor, input|MidiEditCommand::execute_with_state(self, input.event()));
keymap!(KEYS_MIDI_EDITOR = |s: MidiEditor, _input: Event| MidiEditCommand {
key(Up) => SetNoteCursor(s.note_point() + 1),
key(Char('w')) => SetNoteCursor(s.note_point() + 1),
key(Down) => SetNoteCursor(s.note_point().saturating_sub(1)),
key(Char('s')) => SetNoteCursor(s.note_point().saturating_sub(1)),
key(Left) => SetTimeCursor(s.time_point().saturating_sub(s.note_len())),
key(Char('a')) => SetTimeCursor(s.time_point().saturating_sub(s.note_len())),
key(Right) => SetTimeCursor((s.time_point() + s.note_len()) % s.clip_length()),
ctrl(alt(key(Up))) => SetNoteScroll(s.note_point() + 3),
ctrl(alt(key(Down))) => SetNoteScroll(s.note_point().saturating_sub(3)),
ctrl(alt(key(Left))) => SetTimeScroll(s.time_point().saturating_sub(s.time_zoom().get())),
ctrl(alt(key(Right))) => SetTimeScroll((s.time_point() + s.time_zoom().get()) % s.clip_length()),
ctrl(key(Up)) => SetNoteScroll(s.note_lo().get() + 1),
ctrl(key(Down)) => SetNoteScroll(s.note_lo().get().saturating_sub(1)),
ctrl(key(Left)) => SetTimeScroll(s.time_start().get().saturating_sub(s.note_len())),
ctrl(key(Right)) => SetTimeScroll(s.time_start().get() + s.note_len()),
alt(key(Up)) => SetNoteCursor(s.note_point() + 3),
alt(key(Down)) => SetNoteCursor(s.note_point().saturating_sub(3)),
alt(key(Left)) => SetTimeCursor(s.time_point().saturating_sub(s.time_zoom().get())),
alt(key(Right)) => SetTimeCursor((s.time_point() + s.time_zoom().get()) % s.clip_length()),
key(Char('d')) => SetTimeCursor((s.time_point() + s.note_len()) % s.clip_length()),
key(Char('z')) => SetTimeLock(!s.time_lock().get()),
key(Char('-')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::next(s.time_zoom().get()) }),
key(Char('_')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::next(s.time_zoom().get()) }),
key(Char('=')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::prev(s.time_zoom().get()) }),
key(Char('+')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::prev(s.time_zoom().get()) }),
key(Enter) => PutNote,
ctrl(key(Enter)) => AppendNote,
key(Char(',')) => SetNoteLength(NoteDuration::prev(s.note_len())),
key(Char('.')) => SetNoteLength(NoteDuration::next(s.note_len())),
key(Char('<')) => SetNoteLength(NoteDuration::prev(s.note_len())),
key(Char('>')) => SetNoteLength(NoteDuration::next(s.note_len())),
//// TODO: kpat!(Char('/')) => // toggle 3plet
//// TODO: kpat!(Char('?')) => // toggle dotted
});
impl Command<MidiEditor> for MidiEditCommand {
fn execute (self, state: &mut MidiEditor) -> Perhaps<Self> {
use MidiEditCommand::*;
match self {
Show(clip) => { state.set_clip(clip.as_ref()); },
PutNote => { state.put_note(false); },
AppendNote => { state.put_note(true); },
SetTimeZoom(x) => { state.time_zoom().set(x); state.redraw(); },
SetTimeLock(x) => { state.time_lock().set(x); },
SetTimeScroll(x) => { state.time_start().set(x); },
SetNoteScroll(x) => { state.note_lo().set(x.min(127)); },
SetNoteLength(x) => { state.set_note_len(x); },
SetTimeCursor(x) => { state.set_time_point(x); },
SetNoteCursor(note) => { state.set_note_point(note.min(127)); },
_ => todo!("{:?}", self)
}
Ok(None)
}
}

View file

@ -1,106 +0,0 @@
use crate::*;
use self::MidiEditCommand::*;
use KeyCode::*;
edn_provide!(bool: |self: MidiEditor|{
":true" => true,
":false" => false
});
edn_provide!(usize: |self: MidiEditor|{
":note-length" => self.note_len(),
":note-point" => self.note_point(),
":time-point" => self.time_point(),
":time-zoom" => self.time_zoom().get(),
});
edn_command!(MidiEditCommand: |state: MidiEditor| {
("note/put" [a: bool] Self::PutNote)
("note/del" [a: bool] Self::PutNote)
("note/pos" [a: usize] Self::SetNoteCursor(a.expect("no note cursor")))
("note/len" [a: usize] Self::SetNoteLength(a.expect("no note length")))
("time/pos" [a: usize] Self::SetTimeCursor(a.expect("no time cursor")))
("time/zoom" [a: usize] Self::SetTimeZoom(a.expect("no time zoom")))
("time/lock" [a: bool] Self::SetTimeLock(a.expect("no time lock")))
});
//impl EdnCommand<MidiEditor> for MidiEditCommand {
//fn from_edn <'a> (state: &MidiEditor, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
//use EdnItem::*;
//match (head, tail) {
//(Key("note/put"), [a]) => Self::PutNote,
//(Key("note/del"), [a]) => Self::AppendNote,
//(Key("note/dur"), [a]) => Self::AppendNote,
//(Key("note/range"), [a]) => Self::AppendNote,
//(Key("note/pos"), [a]) => Self::AppendNote,
//(Key("time/pos"), [a]) => Self::AppendNote,
//(Key("time/zoom"), [a]) => Self::AppendNote,
//(Key("time/lock"), [a]) => Self::AppendNote,
//_ => todo!()
//}
//}
//}
#[derive(Clone, Debug)] pub enum MidiEditCommand {
// TODO: 1-9 seek markers that by default start every 8th of the clip
AppendNote,
PutNote,
SetNoteCursor(usize),
SetNoteLength(usize),
SetNoteScroll(usize),
SetTimeCursor(usize),
SetTimeScroll(usize),
SetTimeZoom(usize),
SetTimeLock(bool),
Show(Option<Arc<RwLock<MidiClip>>>),
}
handle!(TuiIn: |self: MidiEditor, input|MidiEditCommand::execute_with_state(self, input.event()));
keymap!(KEYS_MIDI_EDITOR = |s: MidiEditor, _input: Event| MidiEditCommand {
key(Up) => SetNoteCursor(s.note_point() + 1),
key(Char('w')) => SetNoteCursor(s.note_point() + 1),
key(Down) => SetNoteCursor(s.note_point().saturating_sub(1)),
key(Char('s')) => SetNoteCursor(s.note_point().saturating_sub(1)),
key(Left) => SetTimeCursor(s.time_point().saturating_sub(s.note_len())),
key(Char('a')) => SetTimeCursor(s.time_point().saturating_sub(s.note_len())),
key(Right) => SetTimeCursor((s.time_point() + s.note_len()) % s.clip_length()),
ctrl(alt(key(Up))) => SetNoteScroll(s.note_point() + 3),
ctrl(alt(key(Down))) => SetNoteScroll(s.note_point().saturating_sub(3)),
ctrl(alt(key(Left))) => SetTimeScroll(s.time_point().saturating_sub(s.time_zoom().get())),
ctrl(alt(key(Right))) => SetTimeScroll((s.time_point() + s.time_zoom().get()) % s.clip_length()),
ctrl(key(Up)) => SetNoteScroll(s.note_lo().get() + 1),
ctrl(key(Down)) => SetNoteScroll(s.note_lo().get().saturating_sub(1)),
ctrl(key(Left)) => SetTimeScroll(s.time_start().get().saturating_sub(s.note_len())),
ctrl(key(Right)) => SetTimeScroll(s.time_start().get() + s.note_len()),
alt(key(Up)) => SetNoteCursor(s.note_point() + 3),
alt(key(Down)) => SetNoteCursor(s.note_point().saturating_sub(3)),
alt(key(Left)) => SetTimeCursor(s.time_point().saturating_sub(s.time_zoom().get())),
alt(key(Right)) => SetTimeCursor((s.time_point() + s.time_zoom().get()) % s.clip_length()),
key(Char('d')) => SetTimeCursor((s.time_point() + s.note_len()) % s.clip_length()),
key(Char('z')) => SetTimeLock(!s.time_lock().get()),
key(Char('-')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::next(s.time_zoom().get()) }),
key(Char('_')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::next(s.time_zoom().get()) }),
key(Char('=')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::prev(s.time_zoom().get()) }),
key(Char('+')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::prev(s.time_zoom().get()) }),
key(Enter) => PutNote,
ctrl(key(Enter)) => AppendNote,
key(Char(',')) => SetNoteLength(NoteDuration::prev(s.note_len())),
key(Char('.')) => SetNoteLength(NoteDuration::next(s.note_len())),
key(Char('<')) => SetNoteLength(NoteDuration::prev(s.note_len())),
key(Char('>')) => SetNoteLength(NoteDuration::next(s.note_len())),
//// TODO: kpat!(Char('/')) => // toggle 3plet
//// TODO: kpat!(Char('?')) => // toggle dotted
});
impl Command<MidiEditor> for MidiEditCommand {
fn execute (self, state: &mut MidiEditor) -> Perhaps<Self> {
use MidiEditCommand::*;
match self {
Show(clip) => { state.set_clip(clip.as_ref()); },
PutNote => { state.put_note(false); },
AppendNote => { state.put_note(true); },
SetTimeZoom(x) => { state.time_zoom().set(x); state.redraw(); },
SetTimeLock(x) => { state.time_lock().set(x); },
SetTimeScroll(x) => { state.time_start().set(x); },
SetNoteScroll(x) => { state.note_lo().set(x.min(127)); },
SetNoteLength(x) => { state.set_note_len(x); },
SetTimeCursor(x) => { state.set_time_point(x); },
SetNoteCursor(note) => { state.set_note_point(note.min(127)); },
_ => todo!("{:?}", self)
}
Ok(None)
}
}

View file

@ -1,37 +0,0 @@
use crate::*;
has_size!(<TuiOut>|self: MidiEditor|&self.size);
content!(TuiOut: |self: MidiEditor| {
self.autoscroll();
//self.autozoom();
self.size.of(&self.mode)
});
impl MidiEditor {
pub fn clip_status (&self) -> impl Content<TuiOut> + '_ {
let (color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.name.clone(), clip.length, clip.looped)
} else {
(ItemPalette::from(TuiTheme::g(64)), String::new().into(), 0, false)
};
row!(
FieldV(color, "Edit", format!("{name} ({length})")),
FieldV(color, "Loop", looped.to_string())
)
}
pub fn edit_status (&self) -> impl Content<TuiOut> + '_ {
let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.length)
} else {
(ItemPalette::from(TuiTheme::g(64)), 0)
};
let time_point = self.time_point();
let time_zoom = self.time_zoom().get();
let time_lock = if self.time_lock().get() { "[lock]" } else { " " };
let note_point = format!("{:>3}", self.note_point());
let note_name = format!("{:4}", Note::pitch_to_name(self.note_point()));
let note_len = format!("{:>4}", self.note_len());
Bsp::e(
FieldV(color, "Time", format!("{length}/{time_zoom}+{time_point} {time_lock}")),
FieldV(color, "Note", format!("{note_name} {note_point} {note_len}")),
)
}
}

View file

@ -1,14 +1,8 @@
use crate::*;
pub trait MidiPlayerApi: MidiRecordApi + MidiPlaybackApi + Send + Sync {}
impl MidiPlayerApi for MidiPlayer {}
pub trait HasPlayer {
fn player (&self) -> &impl MidiPlayerApi;
fn player_mut (&mut self) -> &mut impl MidiPlayerApi;
}
#[macro_export] macro_rules! has_player {
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? HasPlayer for $Struct $(<$($L),*$($T),*>)? {
@ -17,7 +11,8 @@ pub trait HasPlayer {
}
}
}
pub trait MidiPlayerApi: MidiRecordApi + MidiPlaybackApi + Send + Sync {}
impl MidiPlayerApi for MidiPlayer {}
/// Contains state for playing a clip
pub struct MidiPlayer {
/// State of clock and playhead
@ -83,17 +78,11 @@ impl MidiPlayer {
..Default::default()
})
}
pub fn play_status (&self) -> impl Content<TuiOut> {
ClipSelected::play_clip(self)
}
pub fn next_status (&self) -> impl Content<TuiOut> {
ClipSelected::next_clip(self)
}
}
impl std::fmt::Debug for MidiPlayer {
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
f.debug_struct("MidiPlayer")
.field("clock", &self.clock)
.field("clock", &self.clock)
.field("play_clip", &self.play_clip)
.field("next_clip", &self.next_clip)
.finish()
@ -129,7 +118,6 @@ impl HasMidiOuts for MidiPlayer {
fn midi_outs_mut (&mut self) -> &mut Vec<JackPort<MidiOut>> { &mut self.midi_outs }
fn midi_note (&mut self) -> &mut Vec<u8> { &mut self.note_buf }
}
/// Hosts the JACK callback for a single MIDI player
pub struct PlayerAudio<'a, T: MidiPlayerApi>(
/// Player
@ -139,7 +127,6 @@ pub struct PlayerAudio<'a, T: MidiPlayerApi>(
/// Note chunk buffer
pub &'a mut Vec<Vec<Vec<u8>>>,
);
/// JACK process callback for a sequencer's clip player/recorder.
impl<T: MidiPlayerApi> Audio for PlayerAudio<'_, T> {
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
@ -166,7 +153,6 @@ impl<T: MidiPlayerApi> Audio for PlayerAudio<'_, T> {
Control::Continue
}
}
impl MidiRecordApi for MidiPlayer {
fn recording (&self) -> bool {
self.recording
@ -190,13 +176,11 @@ impl MidiRecordApi for MidiPlayer {
&self.notes_in
}
}
impl MidiPlaybackApi for MidiPlayer {
fn notes_out (&self) -> &Arc<RwLock<[bool; 128]>> {
&self.notes_out
}
}
impl HasPlayClip for MidiPlayer {
fn reset (&self) -> bool {
self.reset

View file

@ -1,5 +1,11 @@
use crate::*;
use KeyCode::*;
edn_provide!(bool: |self: MidiPool| {});
edn_provide!(usize: |self: MidiPool| {});
edn_provide!(MidiClip: |self: MidiPool| {});
edn_provide!(PathBuf: |self: MidiPool| {});
edn_provide!(Arc<str>: |self: MidiPool| {});
edn_provide!(ItemColor: |self: MidiPool| {});
#[derive(Clone, PartialEq, Debug)] pub enum PoolCommand {
Show(bool),
/// Update the contents of the clip pool
@ -20,7 +26,7 @@ edn_command!(PoolCommand: |state: MidiPool| {
("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(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)))
});
@ -44,6 +50,44 @@ edn_command!(PoolClipCommand: |state: MidiPool| {
("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<str>),
}
edn_command!(ClipRenameCommand: |state: MidiPool| {
("begin" [] Self::Begin)
("cancel" [] Self::Cancel)
("confirm" [] Self::Confirm)
("set" [n: Arc<str>] 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<str>] Self::Filter(f.expect("no filter")))
});
impl<T: HasClips> Command<T> for PoolClipCommand {
fn execute (self, model: &mut T) -> Perhaps<Self> {
use PoolClipCommand::*;
@ -281,16 +325,6 @@ input_to_command!(FileBrowserCommand: |state: MidiPool, input: Event|{
unreachable!()
}
});
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ClipLengthCommand {
Begin,
Cancel,
Set(usize),
Next,
Prev,
Inc,
Dec,
}
command!(|self: ClipLengthCommand,state:MidiPool|{
use ClipLengthCommand::*;
use ClipLengthFocus::*;
@ -364,13 +398,6 @@ impl InputToCommand<Event, MidiPool> for ClipRenameCommand {
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ClipRenameCommand {
Begin,
Cancel,
Confirm,
Set(Arc<str>),
}
impl Command<MidiPool> for ClipRenameCommand {
fn execute (self, state: &mut MidiPool) -> Perhaps<Self> {
use ClipRenameCommand::*;