mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 20:56:43 +01:00
310 lines
14 KiB
Rust
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()
|
|
//}
|