mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
swap wasd/arrows in arranger; simplify arranger commands
This commit is contained in:
parent
512e466af1
commit
41761f6793
3 changed files with 146 additions and 141 deletions
|
|
@ -1,11 +1,9 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use ClockCommand::{Play, Pause};
|
use ClockCommand::{Play, Pause};
|
||||||
use KeyCode::{Char, Delete, Tab};
|
use KeyCode::{Char, Delete, Tab, Up, Down, Left, Right};
|
||||||
|
|
||||||
#[derive(Clone, Debug)] pub enum ArrangerCommand {
|
#[derive(Clone, Debug)] pub enum ArrangerCommand {
|
||||||
Undo,
|
History(isize),
|
||||||
Redo,
|
|
||||||
Clear,
|
|
||||||
Color(ItemPalette),
|
Color(ItemPalette),
|
||||||
Clock(ClockCommand),
|
Clock(ClockCommand),
|
||||||
Scene(ArrangerSceneCommand),
|
Scene(ArrangerSceneCommand),
|
||||||
|
|
@ -15,12 +13,8 @@ use KeyCode::{Char, Delete, Tab};
|
||||||
Zoom(usize),
|
Zoom(usize),
|
||||||
Phrases(PhrasesCommand),
|
Phrases(PhrasesCommand),
|
||||||
Editor(PhraseCommand),
|
Editor(PhraseCommand),
|
||||||
ShowPool(bool),
|
|
||||||
GetClip(usize, usize),
|
|
||||||
PutClip(usize, usize, Option<Arc<RwLock<Phrase>>>),
|
|
||||||
EnqueueClip(usize, usize),
|
|
||||||
EnqueueScene(usize),
|
|
||||||
StopAll,
|
StopAll,
|
||||||
|
Clear,
|
||||||
}
|
}
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ArrangerTrackCommand {
|
pub enum ArrangerTrackCommand {
|
||||||
|
|
@ -34,6 +28,7 @@ pub enum ArrangerTrackCommand {
|
||||||
}
|
}
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ArrangerSceneCommand {
|
pub enum ArrangerSceneCommand {
|
||||||
|
Enqueue(usize),
|
||||||
Add,
|
Add,
|
||||||
Delete(usize),
|
Delete(usize),
|
||||||
Swap(usize, usize),
|
Swap(usize, usize),
|
||||||
|
|
@ -43,119 +38,127 @@ pub enum ArrangerSceneCommand {
|
||||||
}
|
}
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ArrangerClipCommand {
|
pub enum ArrangerClipCommand {
|
||||||
Play,
|
|
||||||
Get(usize, usize),
|
Get(usize, usize),
|
||||||
Set(usize, usize, Option<Arc<RwLock<Phrase>>>),
|
Put(usize, usize, Option<Arc<RwLock<Phrase>>>),
|
||||||
|
Enqueue(usize, usize),
|
||||||
Edit(Option<Arc<RwLock<Phrase>>>),
|
Edit(Option<Arc<RwLock<Phrase>>>),
|
||||||
SetLoop(bool),
|
SetLoop(usize, usize, bool),
|
||||||
SetColor(ItemPalette),
|
SetColor(usize, usize, ItemPalette),
|
||||||
}
|
}
|
||||||
|
|
||||||
input_to_command!(ArrangerCommand: <Tui>|state: ArrangerTui, input|{
|
input_to_command!(ArrangerCommand: <Tui>|state: ArrangerTui, input|match input.event() {
|
||||||
use ArrangerSelection as Selected;
|
// TODO: u: undo
|
||||||
use ArrangerSceneCommand as Scene;
|
key_pat!(Char('u')) => { todo!("undo") },
|
||||||
use ArrangerTrackCommand as Track;
|
// TODO: Shift-U: redo
|
||||||
use ArrangerClipCommand as Clip;
|
key_pat!(Char('U')) => { todo!("redo") },
|
||||||
// WSAD navigation, Q launches, E edits, PgUp/Down pool, Arrows editor
|
// TODO: k: toggle on-screen keyboard
|
||||||
match input.event() {
|
key_pat!(Ctrl-Char('k')) => { todo!("keyboard") },
|
||||||
// TODO: u: undo
|
// Transport: Play/pause
|
||||||
key_pat!(Char('u')) => { todo!("undo") },
|
key_pat!(Char(' ')) =>
|
||||||
// TODO: Shift-U: redo
|
Self::Clock(if state.clock().is_stopped() { Play(None) } else { Pause(None) }),
|
||||||
key_pat!(Char('U')) => { todo!("redo") },
|
// Transport: Play from start or rewind to start
|
||||||
// TODO: k: toggle on-screen keyboard
|
key_pat!(Shift-Char(' ')) =>
|
||||||
key_pat!(Ctrl-Char('k')) => { todo!("keyboard") },
|
Self::Clock(if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }),
|
||||||
// Transport: Play/pause
|
key_pat!(Char('e')) =>
|
||||||
key_pat!(Char(' ')) =>
|
Self::Editor(PhraseCommand::Show(Some(state.phrases.phrase().clone()))),
|
||||||
Self::Clock(if state.clock().is_stopped() { Play(None) } else { Pause(None) }),
|
key_pat!(Ctrl-Left) =>
|
||||||
// Transport: Play from start or rewind to start
|
Self::Scene(ArrangerSceneCommand::Add),
|
||||||
key_pat!(Shift-Char(' ')) =>
|
key_pat!(Ctrl-Char('t')) =>
|
||||||
Self::Clock(if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }),
|
Self::Track(ArrangerTrackCommand::Add),
|
||||||
key_pat!(Char('e')) =>
|
// Tab: Toggle visibility of phrase pool column
|
||||||
Self::Editor(PhraseCommand::Show(Some(state.phrases.phrase().clone()))),
|
key_pat!(Tab) =>
|
||||||
key_pat!(Char('l')) =>
|
Self::Phrases(PhrasesCommand::Show(!state.show_pool)),
|
||||||
Self::Clip(ArrangerClipCommand::SetLoop(false)),
|
_ => to_arrangement_command(state, input).or_else(||{
|
||||||
key_pat!(Ctrl-Char('a')) =>
|
if let Some(command) = PhraseCommand::input_to_command(&state.editor, input) {
|
||||||
Self::Scene(ArrangerSceneCommand::Add),
|
|
||||||
key_pat!(Ctrl-Char('t')) =>
|
|
||||||
Self::Track(ArrangerTrackCommand::Add),
|
|
||||||
key_pat!(Char('0')) => match state.selected() {
|
|
||||||
Selected::Mix => Self::StopAll,
|
|
||||||
Selected::Track(_t) => return None,
|
|
||||||
Selected::Scene(_s) => return None,
|
|
||||||
Selected::Clip(_t, _s) => return None,
|
|
||||||
},
|
|
||||||
// Tab: Toggle visibility of phrase pool column
|
|
||||||
key_pat!(Tab) =>
|
|
||||||
Self::ShowPool(!state.show_pool),
|
|
||||||
_ => {
|
|
||||||
let t_len = state.tracks.len();
|
|
||||||
let s_len = state.scenes.len();
|
|
||||||
match state.selected() {
|
|
||||||
Selected::Clip(t, s) => match input.event() {
|
|
||||||
key_pat!(Char('w')) => Some(Self::Select(if s > 0 { Selected::Clip(t, s - 1) } else { Selected::Track(t) })),
|
|
||||||
key_pat!(Char('s')) => Some(Self::Select(Selected::Clip(t, (s + 1).min(s_len.saturating_sub(1))))),
|
|
||||||
key_pat!(Char('a')) => Some(Self::Select(if t > 0 { Selected::Clip(t - 1, s) } else { Selected::Scene(s) })),
|
|
||||||
key_pat!(Char('d')) => Some(Self::Select(Selected::Clip((t + 1).min(t_len.saturating_sub(1)), s))),
|
|
||||||
key_pat!(Char(',')) => Some(Self::Clip(Clip::Set(t, s, None))),
|
|
||||||
key_pat!(Char('.')) => Some(Self::Clip(Clip::Set(t, s, None))),
|
|
||||||
key_pat!(Char('<')) => Some(Self::Clip(Clip::Set(t, s, None))),
|
|
||||||
key_pat!(Char('>')) => Some(Self::Clip(Clip::Set(t, s, None))),
|
|
||||||
key_pat!(Char('g')) => Some(Self::Phrases(PhrasesCommand::Select(0))),
|
|
||||||
key_pat!(Char('p')) => Some(Self::PutClip(t, s, Some(state.phrases.phrase().clone()))),
|
|
||||||
key_pat!(Char('q')) => Some(Self::EnqueueClip(t, s)),
|
|
||||||
key_pat!(Delete) => Some(Self::Clip(Clip::Set(t, s, None))),
|
|
||||||
//key_pat!(Char('c')) => Cmd::Clip(Clip::Color(t, s, ItemPalette::random())),
|
|
||||||
//key_pat!(Char('g')) => Cmd::Clip(Clip(Clip::Get(t, s))),
|
|
||||||
//key_pat!(Char('s')) => Cmd::Clip(Clip(Clip::Set(t, s))),
|
|
||||||
_ => None
|
|
||||||
},
|
|
||||||
Selected::Scene(s) => match input.event() {
|
|
||||||
key_pat!(Char('w')) => Some(Self::Select(if s > 0 { Selected::Scene(s - 1) } else { Selected::Mix })),
|
|
||||||
key_pat!(Char('s')) => Some(Self::Select(Selected::Scene((s + 1).min(s_len.saturating_sub(1))))),
|
|
||||||
key_pat!(Char('d')) => Some(Self::Select(Selected::Clip(0, s))),
|
|
||||||
key_pat!(Char(',')) => Some(Self::Scene(Scene::Swap(s, s - 1))),
|
|
||||||
key_pat!(Char('.')) => Some(Self::Scene(Scene::Swap(s, s + 1))),
|
|
||||||
key_pat!(Char('<')) => Some(Self::Scene(Scene::Swap(s, s - 1))),
|
|
||||||
key_pat!(Char('>')) => Some(Self::Scene(Scene::Swap(s, s + 1))),
|
|
||||||
key_pat!(Char('c')) => Some(Self::Scene(Scene::SetColor(s, ItemPalette::random()))),
|
|
||||||
key_pat!(Char('q')) => Some(Self::EnqueueScene(s)),
|
|
||||||
key_pat!(Delete) => Some(Self::Scene(Scene::Delete(s))),
|
|
||||||
//key_pat!(Char('c')) => Cmd::Track(Scene::Color(s, ItemPalette::random())),
|
|
||||||
_ => None
|
|
||||||
},
|
|
||||||
Selected::Track(t) => match input.event() {
|
|
||||||
key_pat!(Char('s')) => Some(Self::Select(Selected::Clip(t, 0))),
|
|
||||||
key_pat!(Char('a')) => Some(Self::Select(if t > 0 { Selected::Track(t - 1) } else { Selected::Mix })),
|
|
||||||
key_pat!(Char('d')) => Some(Self::Select(Selected::Track((t + 1).min(t_len.saturating_sub(1))))),
|
|
||||||
key_pat!(Char('c')) => Some(Self::Track(Track::SetColor(t, ItemPalette::random()))),
|
|
||||||
key_pat!(Char(',')) => Some(Self::Track(Track::Swap(t, t - 1))),
|
|
||||||
key_pat!(Char('.')) => Some(Self::Track(Track::Swap(t, t + 1))),
|
|
||||||
key_pat!(Char('<')) => Some(Self::Track(Track::Swap(t, t - 1))),
|
|
||||||
key_pat!(Char('>')) => Some(Self::Track(Track::Swap(t, t + 1))),
|
|
||||||
key_pat!(Delete) => Some(Self::Track(Track::Delete(t))),
|
|
||||||
//key_pat!(Char('c')) => Cmd::Track(Track::Color(t, ItemPalette::random())),
|
|
||||||
_ => return None
|
|
||||||
},
|
|
||||||
Selected::Mix => match input.event() {
|
|
||||||
// 0: Enqueue phrase 0 (stop all)
|
|
||||||
key_pat!(Char('0')) => Some(Self::StopAll),
|
|
||||||
key_pat!(Char('s')) => Some(Self::Select(Selected::Scene(0))),
|
|
||||||
key_pat!(Char('d')) => Some(Self::Select(Selected::Track(0))),
|
|
||||||
key_pat!(Delete) => Some(Self::Clear),
|
|
||||||
key_pat!(Char('c')) => Some(Self::Color(ItemPalette::random())),
|
|
||||||
_ => None
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}.or_else(||if let Some(command) = PhraseCommand::input_to_command(&state.editor, input) {
|
|
||||||
Some(Self::Editor(command))
|
Some(Self::Editor(command))
|
||||||
} else if let Some(command) = PhrasesCommand::input_to_command(&state.phrases, input) {
|
} else if let Some(command) = PhrasesCommand::input_to_command(&state.phrases, input) {
|
||||||
Some(Self::Phrases(command))
|
Some(Self::Phrases(command))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
})?
|
}
|
||||||
}
|
})?
|
||||||
});
|
});
|
||||||
command!(|self:ArrangerCommand,state:ArrangerTui|match self {
|
fn to_arrangement_command (state: &ArrangerTui, input: &TuiInput) -> Option<ArrangerCommand> {
|
||||||
|
use ArrangerCommand as Cmd;
|
||||||
|
use ArrangerSelection as Selected;
|
||||||
|
use ArrangerSceneCommand as Scene;
|
||||||
|
use ArrangerTrackCommand as Track;
|
||||||
|
use ArrangerClipCommand as Clip;
|
||||||
|
let t_len = state.tracks.len();
|
||||||
|
let s_len = state.scenes.len();
|
||||||
|
match state.selected() {
|
||||||
|
Selected::Clip(t, s) => match input.event() {
|
||||||
|
key_pat!(Char('g')) => Some(Cmd::Phrases(PhrasesCommand::Select(0))),
|
||||||
|
key_pat!(Char('q')) => Some(Cmd::Clip(Clip::Enqueue(t, s))),
|
||||||
|
key_pat!(Char(',')) => Some(Cmd::Clip(Clip::Put(t, s, None))),
|
||||||
|
key_pat!(Char('.')) => Some(Cmd::Clip(Clip::Put(t, s, None))),
|
||||||
|
key_pat!(Char('<')) => Some(Cmd::Clip(Clip::Put(t, s, None))),
|
||||||
|
key_pat!(Char('>')) => Some(Cmd::Clip(Clip::Put(t, s, None))),
|
||||||
|
key_pat!(Char('p')) => Some(Cmd::Clip(Clip::Put(t, s, Some(state.phrases.phrase().clone())))),
|
||||||
|
key_pat!(Char('l')) => Some(Cmd::Clip(ArrangerClipCommand::SetLoop(t, s, false))),
|
||||||
|
key_pat!(Delete) => Some(Cmd::Clip(Clip::Put(t, s, None))),
|
||||||
|
|
||||||
|
key_pat!(Up) => Some(Cmd::Select(
|
||||||
|
if s > 0 { Selected::Clip(t, s - 1) } else { Selected::Track(t) })),
|
||||||
|
key_pat!(Down) => Some(Cmd::Select(
|
||||||
|
Selected::Clip(t, (s + 1).min(s_len.saturating_sub(1))))),
|
||||||
|
key_pat!(Left) => Some(Cmd::Select(
|
||||||
|
if t > 0 { Selected::Clip(t - 1, s) } else { Selected::Scene(s) })),
|
||||||
|
key_pat!(Right) => Some(Cmd::Select(
|
||||||
|
Selected::Clip((t + 1).min(t_len.saturating_sub(1)), s))),
|
||||||
|
|
||||||
|
_ => None
|
||||||
|
},
|
||||||
|
Selected::Scene(s) => match input.event() {
|
||||||
|
key_pat!(Char(',')) => Some(Cmd::Scene(Scene::Swap(s, s - 1))),
|
||||||
|
key_pat!(Char('.')) => Some(Cmd::Scene(Scene::Swap(s, s + 1))),
|
||||||
|
key_pat!(Char('<')) => Some(Cmd::Scene(Scene::Swap(s, s - 1))),
|
||||||
|
key_pat!(Char('>')) => Some(Cmd::Scene(Scene::Swap(s, s + 1))),
|
||||||
|
key_pat!(Char('q')) => Some(Cmd::Scene(Scene::Enqueue(s))),
|
||||||
|
key_pat!(Delete) => Some(Cmd::Scene(Scene::Delete(s))),
|
||||||
|
key_pat!(Char('c')) => Some(Cmd::Scene(Scene::SetColor(s, ItemPalette::random()))),
|
||||||
|
|
||||||
|
key_pat!(Up) => Some(
|
||||||
|
Cmd::Select(if s > 0 { Selected::Scene(s - 1) } else { Selected::Mix })),
|
||||||
|
key_pat!(Down) => Some(
|
||||||
|
Cmd::Select(Selected::Scene((s + 1).min(s_len.saturating_sub(1))))),
|
||||||
|
key_pat!(Right) => Some(
|
||||||
|
Cmd::Select(Selected::Clip(0, s))),
|
||||||
|
|
||||||
|
_ => None
|
||||||
|
},
|
||||||
|
Selected::Track(t) => match input.event() {
|
||||||
|
key_pat!(Char(',')) => Some(Cmd::Track(Track::Swap(t, t - 1))),
|
||||||
|
key_pat!(Char('.')) => Some(Cmd::Track(Track::Swap(t, t + 1))),
|
||||||
|
key_pat!(Char('<')) => Some(Cmd::Track(Track::Swap(t, t - 1))),
|
||||||
|
key_pat!(Char('>')) => Some(Cmd::Track(Track::Swap(t, t + 1))),
|
||||||
|
key_pat!(Delete) => Some(Cmd::Track(Track::Delete(t))),
|
||||||
|
key_pat!(Char('c')) => Some(Cmd::Track(Track::SetColor(t, ItemPalette::random()))),
|
||||||
|
|
||||||
|
key_pat!(Down) => Some(
|
||||||
|
Cmd::Select(Selected::Clip(t, 0))),
|
||||||
|
key_pat!(Left) => Some(
|
||||||
|
Cmd::Select(if t > 0 { Selected::Track(t - 1) } else { Selected::Mix })),
|
||||||
|
key_pat!(Right) => Some(
|
||||||
|
Cmd::Select(Selected::Track((t + 1).min(t_len.saturating_sub(1))))),
|
||||||
|
|
||||||
|
_ => None
|
||||||
|
},
|
||||||
|
Selected::Mix => match input.event() {
|
||||||
|
key_pat!(Delete) => Some(Cmd::Clear),
|
||||||
|
key_pat!(Char('0')) => Some(Cmd::StopAll),
|
||||||
|
key_pat!(Char('c')) => Some(Cmd::Color(ItemPalette::random())),
|
||||||
|
|
||||||
|
key_pat!(Down) => Some(
|
||||||
|
Cmd::Select(Selected::Scene(0))),
|
||||||
|
key_pat!(Right) => Some(
|
||||||
|
Cmd::Select(Selected::Track(0))),
|
||||||
|
|
||||||
|
_ => None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command!(|self: ArrangerCommand, state: ArrangerTui|match self {
|
||||||
Self::Scene(cmd) => cmd.execute(state)?.map(Self::Scene),
|
Self::Scene(cmd) => cmd.execute(state)?.map(Self::Scene),
|
||||||
Self::Track(cmd) => cmd.execute(state)?.map(Self::Track),
|
Self::Track(cmd) => cmd.execute(state)?.map(Self::Track),
|
||||||
Self::Clip(cmd) => cmd.execute(state)?.map(Self::Clip),
|
Self::Clip(cmd) => cmd.execute(state)?.map(Self::Clip),
|
||||||
|
|
@ -171,10 +174,6 @@ command!(|self:ArrangerCommand,state:ArrangerTui|match self {
|
||||||
state.color = palette;
|
state.color = palette;
|
||||||
Some(Self::Color(old))
|
Some(Self::Color(old))
|
||||||
},
|
},
|
||||||
Self::ShowPool(show) => {
|
|
||||||
state.show_pool = show;
|
|
||||||
None
|
|
||||||
},
|
|
||||||
Self::Phrases(cmd) => {
|
Self::Phrases(cmd) => {
|
||||||
let mut default = |cmd: PhrasesCommand|{
|
let mut default = |cmd: PhrasesCommand|{
|
||||||
cmd.execute(&mut state.phrases).map(|x|x.map(Self::Phrases))
|
cmd.execute(&mut state.phrases).map(|x|x.map(Self::Phrases))
|
||||||
|
|
@ -186,7 +185,7 @@ command!(|self:ArrangerCommand,state:ArrangerTui|match self {
|
||||||
state.editor.set_phrase(Some(state.phrases.phrase()));
|
state.editor.set_phrase(Some(state.phrases.phrase()));
|
||||||
undo
|
undo
|
||||||
},
|
},
|
||||||
// update color in all places simultaneously
|
// reload phrase in editor to update color
|
||||||
PhrasesCommand::Phrase(PhrasePoolCommand::SetColor(index, _)) => {
|
PhrasesCommand::Phrase(PhrasePoolCommand::SetColor(index, _)) => {
|
||||||
let undo = default(cmd)?;
|
let undo = default(cmd)?;
|
||||||
state.editor.set_phrase(Some(state.phrases.phrase()));
|
state.editor.set_phrase(Some(state.phrases.phrase()));
|
||||||
|
|
@ -195,31 +194,14 @@ command!(|self:ArrangerCommand,state:ArrangerTui|match self {
|
||||||
_ => default(cmd)?
|
_ => default(cmd)?
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Self::Undo => { todo!() },
|
Self::History(_) => { todo!() },
|
||||||
Self::Redo => { todo!() },
|
|
||||||
Self::Clear => { todo!() },
|
|
||||||
Self::GetClip(track, scene) => { todo!() },
|
|
||||||
Self::PutClip(track, scene, phrase) => {
|
|
||||||
let old = state.scenes[scene].clips[track].clone();
|
|
||||||
state.scenes[scene].clips[track] = phrase;
|
|
||||||
Some(Self::PutClip(track, scene, old))
|
|
||||||
},
|
|
||||||
Self::EnqueueClip(track, scene) => {
|
|
||||||
state.tracks[track].player.enqueue_next(state.scenes[scene].clips[track].as_ref());
|
|
||||||
None
|
|
||||||
},
|
|
||||||
Self::EnqueueScene(scene) => {
|
|
||||||
for track in 0..state.tracks.len() {
|
|
||||||
state.tracks[track].player.enqueue_next(state.scenes[scene].clips[track].as_ref());
|
|
||||||
}
|
|
||||||
None
|
|
||||||
},
|
|
||||||
Self::StopAll => {
|
Self::StopAll => {
|
||||||
for track in 0..state.tracks.len() {
|
for track in 0..state.tracks.len() {
|
||||||
state.tracks[track].player.enqueue_next(None);
|
state.tracks[track].player.enqueue_next(None);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
Self::Clear => { todo!() },
|
||||||
});
|
});
|
||||||
command!(|self: ArrangerTrackCommand, state: ArrangerTui|match self {
|
command!(|self: ArrangerTrackCommand, state: ArrangerTui|match self {
|
||||||
Self::SetColor(index, color) => {
|
Self::SetColor(index, color) => {
|
||||||
|
|
@ -233,7 +215,7 @@ command!(|self: ArrangerTrackCommand, state: ArrangerTui|match self {
|
||||||
},
|
},
|
||||||
_ => None
|
_ => None
|
||||||
});
|
});
|
||||||
command!(|self:ArrangerSceneCommand,state:ArrangerTui|match self {
|
command!(|self: ArrangerSceneCommand, state: ArrangerTui|match self {
|
||||||
Self::Delete(index) => {
|
Self::Delete(index) => {
|
||||||
state.scene_del(index);
|
state.scene_del(index);
|
||||||
None
|
None
|
||||||
|
|
@ -243,8 +225,24 @@ command!(|self:ArrangerSceneCommand,state:ArrangerTui|match self {
|
||||||
state.scenes[index].color = color;
|
state.scenes[index].color = color;
|
||||||
Some(Self::SetColor(index, old))
|
Some(Self::SetColor(index, old))
|
||||||
},
|
},
|
||||||
|
Self::Enqueue(scene) => {
|
||||||
|
for track in 0..state.tracks.len() {
|
||||||
|
state.tracks[track].player.enqueue_next(state.scenes[scene].clips[track].as_ref());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
},
|
||||||
_ => None
|
_ => None
|
||||||
});
|
});
|
||||||
command!(|self:ArrangerClipCommand, _state:ArrangerTui|match self {
|
command!(|self: ArrangerClipCommand, state: ArrangerTui|match self {
|
||||||
|
Self::Get(track, scene) => { todo!() },
|
||||||
|
Self::Put(track, scene, phrase) => {
|
||||||
|
let old = state.scenes[scene].clips[track].clone();
|
||||||
|
state.scenes[scene].clips[track] = phrase;
|
||||||
|
Some(Self::Put(track, scene, old))
|
||||||
|
},
|
||||||
|
Self::Enqueue(track, scene) => {
|
||||||
|
state.tracks[track].player.enqueue_next(state.scenes[scene].clips[track].as_ref());
|
||||||
|
None
|
||||||
|
},
|
||||||
_ => None
|
_ => None
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PhraseListModel {
|
pub struct PhraseListModel {
|
||||||
|
pub(crate) visible: bool,
|
||||||
/// Collection of phrases
|
/// Collection of phrases
|
||||||
pub(crate) phrases: Vec<Arc<RwLock<Phrase>>>,
|
pub(crate) phrases: Vec<Arc<RwLock<Phrase>>>,
|
||||||
/// Selected phrase
|
/// Selected phrase
|
||||||
|
|
@ -35,6 +36,7 @@ pub enum PhraseListMode {
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum PhrasesCommand {
|
pub enum PhrasesCommand {
|
||||||
|
Show(bool),
|
||||||
/// Update the contents of the phrase pool
|
/// Update the contents of the phrase pool
|
||||||
Phrase(Pool),
|
Phrase(Pool),
|
||||||
/// Select a phrase from the phrase pool
|
/// Select a phrase from the phrase pool
|
||||||
|
|
@ -52,7 +54,10 @@ pub enum PhrasesCommand {
|
||||||
command!(|self:PhrasesCommand, state:PhraseListModel|{
|
command!(|self:PhrasesCommand, state:PhraseListModel|{
|
||||||
use PhrasesCommand::*;
|
use PhrasesCommand::*;
|
||||||
match self {
|
match self {
|
||||||
Phrase(command) => command.execute(state)?.map(Phrase),
|
Show(visible) => {
|
||||||
|
state.visible = visible;
|
||||||
|
Some(Self::Show(!visible))
|
||||||
|
}
|
||||||
Rename(command) => match command {
|
Rename(command) => match command {
|
||||||
PhraseRenameCommand::Begin => {
|
PhraseRenameCommand::Begin => {
|
||||||
let length = state.phrases()[state.phrase_index()].read().unwrap().length;
|
let length = state.phrases()[state.phrase_index()].read().unwrap().length;
|
||||||
|
|
@ -95,6 +100,7 @@ command!(|self:PhrasesCommand, state:PhraseListModel|{
|
||||||
state.set_phrase_index(phrase);
|
state.set_phrase_index(phrase);
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
Phrase(command) => command.execute(state)?.map(Phrase),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -158,6 +164,7 @@ fn to_phrases_command (state: &PhraseListModel, input: &TuiInput) -> Option<Phra
|
||||||
impl Default for PhraseListModel {
|
impl Default for PhraseListModel {
|
||||||
fn default () -> Self {
|
fn default () -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
visible: true,
|
||||||
phrases: vec![RwLock::new(Phrase::default()).into()],
|
phrases: vec![RwLock::new(Phrase::default()).into()],
|
||||||
phrase: 0.into(),
|
phrase: 0.into(),
|
||||||
scroll: 0,
|
scroll: 0,
|
||||||
|
|
|
||||||
|
|
@ -119,10 +119,10 @@ impl ArrangerStatus {
|
||||||
Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!([
|
Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!([
|
||||||
single("SPACE", "play/pause"),
|
single("SPACE", "play/pause"),
|
||||||
single(" Ctrl", " scroll"),
|
single(" Ctrl", " scroll"),
|
||||||
single(" wsad", " cell"),
|
single(" ▲▼▶◀", " cell"),
|
||||||
double(("p", "put"), ("g", "get")),
|
double(("p", "put"), ("g", "get")),
|
||||||
double(("q", "enqueue"), ("e", "edit")),
|
double(("q", "enqueue"), ("e", "edit")),
|
||||||
single(" ▲▼▶◀", " note"),
|
single(" wsad", " note"),
|
||||||
double(("a", "append"), ("s", "set"),),
|
double(("a", "append"), ("s", "set"),),
|
||||||
double((",.", "length"), ("<>", "triplet"),),
|
double((",.", "length"), ("<>", "triplet"),),
|
||||||
double(("[]", "phrase"), ("{}", "order"),),
|
double(("[]", "phrase"), ("{}", "order"),),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue