tek/crates/tek_tui/src/tui_arranger_cmd.rs

310 lines
14 KiB
Rust

use crate::*;
#[derive(Clone)]
pub enum ArrangerViewCommand {
Focus(FocusCommand),
Arrangement(ArrangementEditorCommand),
Transport(TransportViewCommand),
Phrases(PhrasePoolViewCommand),
Editor(PhraseEditorCommand),
EditPhrase(Option<Arc<RwLock<Phrase>>>),
}
#[derive(Clone)]
pub enum ArrangementEditorCommand {
Edit(ArrangementCommand),
Select(ArrangementEditorFocus),
Zoom(usize),
}
/// Handle top-level events in standalone arranger.
impl Handle<Tui> for ArrangerView<Tui> {
fn handle (&mut self, i: &TuiInput) -> Perhaps<bool> {
ArrangerViewCommand::execute_with_state(self, i)
}
}
impl InputToCommand<Tui, ArrangerView<Tui>> for ArrangerViewCommand {
fn input_to_command (view: &ArrangerView<Tui>, input: &TuiInput) -> Option<Self> {
use FocusCommand::*;
use ArrangerViewCommand::*;
Some(match input.event() {
key!(KeyCode::Tab) => Focus(Next),
key!(Shift-KeyCode::Tab) => Focus(Prev),
key!(KeyCode::BackTab) => Focus(Prev),
key!(Shift-KeyCode::BackTab) => Focus(Prev),
key!(KeyCode::Up) => Focus(Up),
key!(KeyCode::Down) => Focus(Down),
key!(KeyCode::Left) => Focus(Left),
key!(KeyCode::Right) => Focus(Right),
key!(KeyCode::Enter) => Focus(Enter),
key!(KeyCode::Esc) => Focus(Exit),
key!(KeyCode::Char(' ')) => {
Transport(TransportViewCommand::Transport(TransportCommand::Play(None)))
},
_ => match view.focused() {
ArrangerViewFocus::Transport => Transport(
TransportViewCommand::input_to_command(&view.sequencer.transport, input)?
),
ArrangerViewFocus::PhraseEditor => Editor(
PhraseEditorCommand::input_to_command(&view.sequencer.editor, input)?
),
ArrangerViewFocus::PhrasePool => match input.event() {
key!(KeyCode::Char('e')) => EditPhrase(
Some(view.sequencer.phrases.phrase().clone())
),
_ => Phrases(
PhrasePoolViewCommand::input_to_command(&view.sequencer.phrases, input)?
)
},
ArrangerViewFocus::Arrangement => match input.event() {
key!(KeyCode::Char('e')) => EditPhrase(
view.selected_phrase()
),
_ => Arrangement(
ArrangementEditorCommand::input_to_command(&view, &input)?
)
}
}
})
}
}
impl<E: Engine> Command<ArrangerView<E>> for ArrangerViewCommand {
fn execute (self, view: &mut ArrangerView<E>) -> Perhaps<Self> {
let undo = match self {
Self::Focus(cmd) =>
delegate(cmd, Self::Focus, view),
Self::Phrases(cmd) =>
delegate(cmd, Self::Phrases, &mut view.sequencer.phrases),
Self::Editor(cmd) =>
delegate(cmd, Self::Editor, &mut view.sequencer.editor),
Self::Transport(cmd) =>
delegate(cmd, Self::Transport, &mut view.sequencer.transport),
Self::Arrangement(cmd) =>
delegate(cmd, Self::Arrangement, &mut view),
Self::EditPhrase(phrase) => {
view.sequencer.editor.phrase = phrase.clone();
view.focus(ArrangerViewFocus::PhraseEditor);
view.focus_enter();
Ok(None)
}
}?;
view.show_phrase();
view.update_status();
return Ok(undo);
}
}
impl InputToCommand<Tui, ArrangerView<Tui>> for ArrangementEditorCommand {
fn input_to_command (state: &ArrangerView<Tui>, input: &TuiInput) -> Option<Self> {
use ArrangementEditorCommand as Cmd;
use ArrangementCommand as Edit;
use ArrangementEditorFocus as Focus;
use ArrangementTrackCommand as Track;
use ArrangementClipCommand as Clip;
use ArrangementSceneCommand as Scene;
Some(match input.event() {
// FIXME: boundary conditions
key!(KeyCode::Up) => match state.selected {
ArrangementEditorFocus::Mix => return None,
ArrangementEditorFocus::Track(t) => return None,
ArrangementEditorFocus::Scene(s) => Cmd::Select(Focus::Scene(s - 1)),
ArrangementEditorFocus::Clip(t, s) => Cmd::Select(Focus::Clip(t, s - 1)),
},
key!(KeyCode::Down) => match state.selected {
ArrangementEditorFocus::Mix => Cmd::Select(Focus::Scene(0)),
ArrangementEditorFocus::Track(t) => Cmd::Select(Focus::Clip(t, 0)),
ArrangementEditorFocus::Scene(s) => Cmd::Select(Focus::Scene(s + 1)),
ArrangementEditorFocus::Clip(t, s) => Cmd::Select(Focus::Clip(t, s + 1)),
},
key!(KeyCode::Left) => match state.selected {
ArrangementEditorFocus::Mix => return None,
ArrangementEditorFocus::Track(t) => Cmd::Select(Focus::Track(t - 1)),
ArrangementEditorFocus::Scene(s) => return None,
ArrangementEditorFocus::Clip(t, s) => Cmd::Select(Focus::Clip(t - 1, s)),
},
key!(KeyCode::Right) => match state.selected {
ArrangementEditorFocus::Mix => return None,
ArrangementEditorFocus::Track(t) => Cmd::Select(Focus::Track(t + 1)),
ArrangementEditorFocus::Scene(s) => Cmd::Select(Focus::Clip(0, s)),
ArrangementEditorFocus::Clip(t, s) => Cmd::Select(Focus::Clip(t, s - 1)),
},
key!(KeyCode::Char('+')) => Cmd::Zoom(0),
key!(KeyCode::Char('=')) => Cmd::Zoom(0),
key!(KeyCode::Char('_')) => Cmd::Zoom(0),
key!(KeyCode::Char('-')) => Cmd::Zoom(0),
key!(KeyCode::Char('`')) => { todo!("toggle view mode") },
key!(KeyCode::Char(',')) => match state.selected {
ArrangementEditorFocus::Mix => Cmd::Zoom(0),
ArrangementEditorFocus::Track(t) => Cmd::Edit(Edit::Track(Track::Swap(0, 0))),
ArrangementEditorFocus::Scene(s) => Cmd::Edit(Edit::Scene(Scene::Swap(0, 0))),
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Char('.')) => match state.selected {
ArrangementEditorFocus::Mix => Cmd::Zoom(0),
ArrangementEditorFocus::Track(t) => Cmd::Edit(Edit::Track(Track::Swap(0, 0))),
ArrangementEditorFocus::Scene(s) => Cmd::Edit(Edit::Scene(Scene::Swap(0, 0))),
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Char('<')) => match state.selected {
ArrangementEditorFocus::Mix => Cmd::Zoom(0),
ArrangementEditorFocus::Track(t) => Cmd::Edit(Edit::Track(Track::Swap(0, 0))),
ArrangementEditorFocus::Scene(s) => Cmd::Edit(Edit::Scene(Scene::Swap(0, 0))),
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Char('>')) => match state.selected {
ArrangementEditorFocus::Mix => Cmd::Zoom(0),
ArrangementEditorFocus::Track(t) => Cmd::Edit(Edit::Track(Track::Swap(0, 0))),
ArrangementEditorFocus::Scene(s) => Cmd::Edit(Edit::Scene(Scene::Swap(0, 0))),
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Enter) => match state.selected {
ArrangementEditorFocus::Mix => return None,
ArrangementEditorFocus::Track(t) => return None,
ArrangementEditorFocus::Scene(s) => Cmd::Edit(Edit::Scene(Scene::Play(s))),
ArrangementEditorFocus::Clip(t, s) => return None,
},
key!(KeyCode::Delete) => match state.selected {
ArrangementEditorFocus::Mix => Cmd::Edit(Edit::Clear),
ArrangementEditorFocus::Track(t) => Cmd::Edit(Edit::Track(Track::Delete(t))),
ArrangementEditorFocus::Scene(s) => Cmd::Edit(Edit::Scene(Scene::Delete(s))),
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Char('c')) => Cmd::Edit(Edit::Clip(Clip::RandomColor)),
key!(KeyCode::Char('s')) => match state.selected {
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Set(t, s, None))),
_ => return None,
},
key!(KeyCode::Char('g')) => match state.selected {
ArrangementEditorFocus::Clip(t, s) => Cmd::Edit(Edit::Clip(Clip::Get(t, s))),
_ => return None,
},
key!(Ctrl-KeyCode::Char('a')) => Cmd::Edit(Edit::Scene(Scene::Add)),
key!(Ctrl-KeyCode::Char('t')) => Cmd::Edit(Edit::Track(Track::Add)),
key!(KeyCode::Char('l')) => Cmd::Edit(Edit::Clip(Clip::SetLoop(false))),
_ => return None
})
}
}
impl<E: Engine> Command<ArrangerView<E>> for ArrangementEditorCommand {
fn execute (self, view: &mut ArrangerView<E>) -> Perhaps<Self> {
match self {
Self::Zoom(zoom) => {
todo!();
},
Self::Select(selected) => {
view.selected = selected;
},
Self::Edit(command) => {
return Ok(command.execute(&mut view.model)?.map(Self::Edit))
},
}
Ok(None)
}
}
//pub fn phrase_next (&mut self) {
//if let ArrangementEditorFocus::Clip(track, scene) = self.selected {
//if let Some(ref mut phrase) = self.model.scenes[scene].clips[track] {
//let phrases = self.model.phrases.read().unwrap();
//let index = phrases.index_of(&*phrase.read().unwrap());
//if let Some(index) = index {
//if index < phrases.len().saturating_sub(1) {
//*phrase = phrases[index + 1].clone();
//}
//}
//}
//}
//}
//pub fn phrase_prev (&mut self) {
//if let ArrangementEditorFocus::Clip(track, scene) = self.selected {
//if let Some(ref mut phrase) = self.model.scenes[scene].clips[track] {
//let phrases = self.model.phrases.read().unwrap();
//let index = phrases.index_of(&*phrase.read().unwrap());
//if let Some(index) = index {
//if index > 0 {
//*phrase = phrases[index - 1].clone();
//}
//}
//}
//}
//}
//pub fn phrase_get (&mut self) {
//if let ArrangementEditorFocus::Clip(track, scene) = self.selected {
//if let Some(phrase) = &self.model.scenes[scene].clips[track] {
//let mut phrases = self.model.phrases.write().unwrap();
//if let Some(index) = &*phrases.index_of(&*phrase.read().unwrap()) {
//self.model.phrase = index;
//}
//}
//}
//}
///// Focus the editor with the current phrase
//pub fn edit_phrase (&mut self) {
//if self.arrangement.selected.is_clip() && self.arrangement.phrase().is_none() {
//self.sequencer.phrases.append_new(None, Some(self.next_color().into()));
//self.arrangement.phrase_put();
//}
//self.show_phrase();
//self.focus(ArrangerViewFocus::PhraseEditor);
//self.sequencer.editor.entered = true;
//}
//pub fn next_color (&self) -> ItemColor {
//if let ArrangementEditorFocus::Clip(track, scene) = self.arrangement.selected {
//let track_color = self.arrangement.model.tracks[track].color;
//let scene_color = self.arrangement.model.scenes[scene].color;
//track_color.mix(scene_color, 0.5).mix(ItemColor::random(), 0.25)
//} else {
//panic!("could not compute next color")
//}
//}
//pub fn phrase_del (&mut self) {
//let track_index = self.selected.track();
//let scene_index = self.selected.scene();
//track_index
//.and_then(|index|self.model.tracks.get_mut(index).map(|track|(index, track)))
//.map(|(track_index, _)|scene_index
//.and_then(|index|self.model.scenes.get_mut(index))
//.map(|scene|scene.clips[track_index] = None));
//}
//pub fn phrase_put (&mut self) {
//if let ArrangementEditorFocus::Clip(track, scene) = self.selected {
//self.model.scenes[scene].clips[track] = self.selected_phrase().clone();
//}
//}
//pub fn selected_scene (&self) -> Option<&ArrangementScene> {
//self.selected.scene().map(|s|self.model.scenes.get(s)).flatten()
//}
//pub fn selected_scene_mut (&mut self) -> Option<&mut ArrangementScene> {
//self.selected.scene().map(|s|self.model.scenes.get_mut(s)).flatten()
//}
//pub fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>> {
//self.selected_scene()?.clips.get(self.selected.track()?)?.clone()
//}