tek/crates/tek_tui/src/tui_command.rs
2024-11-18 22:05:11 +01:00

354 lines
12 KiB
Rust

use crate::*;
#[derive(Clone, Debug, PartialEq)]
pub enum TransportCommand {
Focus(FocusCommand),
Clock(ClockCommand),
Playhead(PlayheadCommand),
}
#[derive(Clone, Debug, PartialEq)]
pub enum SequencerCommand {
Focus(FocusCommand),
Undo,
Redo,
Clear,
Clock(ClockCommand),
Playhead(PlayheadCommand),
Phrases(PhrasesCommand),
Editor(PhraseCommand),
}
#[derive(Clone, Debug)]
pub enum ArrangerCommand {
Focus(FocusCommand),
Undo,
Redo,
Clear,
Clock(ClockCommand),
Playhead(PlayheadCommand),
Scene(ArrangerSceneCommand),
Track(ArrangerTrackCommand),
Clip(ArrangerClipCommand),
Select(ArrangerSelection),
Zoom(usize),
Phrases(PhrasePoolCommand),
Editor(PhraseCommand),
EditPhrase(Option<Arc<RwLock<Phrase>>>),
}
#[derive(Clone, PartialEq, Debug)]
pub enum PhrasesCommand {
Select(usize),
Edit(PhrasePoolCommand),
Rename(PhraseRenameCommand),
Length(PhraseLengthCommand),
}
#[derive(Clone, Debug, PartialEq)]
pub enum PhraseRenameCommand {
Begin,
Set(String),
Confirm,
Cancel,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum PhraseLengthCommand {
Begin,
Next,
Prev,
Inc,
Dec,
Set(usize),
Cancel,
}
#[derive(Clone, PartialEq, Debug)]
pub enum PhraseCommand {
// TODO: 1-9 seek markers that by default start every 8th of the phrase
ToggleDirection,
EnterEditMode,
ExitEditMode,
NoteAppend,
NoteSet,
NoteCursorSet(usize),
NoteLengthSet(usize),
NoteScrollSet(usize),
TimeCursorSet(usize),
TimeScrollSet(usize),
TimeZoomSet(usize),
}
impl<T: TransportControl> Command<T> for TransportCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use TransportCommand::{Focus, Clock, Playhead};
use FocusCommand::{Next, Prev};
use ClockCommand::{SetBpm, SetQuant, SetSync};
Ok(Some(match self {
Focus(Next) => { todo!() }
Focus(Prev) => { todo!() },
Focus(_) => { unimplemented!() },
Clock(SetBpm(bpm)) => Clock(SetBpm(state.bpm().set(bpm))),
Clock(SetQuant(quant)) => Clock(SetQuant(state.quant().set(quant))),
Clock(SetSync(sync)) => Clock(SetSync(state.sync().set(sync))),
_ => return Ok(None)
}))
}
}
impl<T> Command<T> for SequencerCommand
where
T: FocusGrid + PhrasesControl + PhraseControl + ClockApi + PlayheadApi
{
fn execute (self, state: T) -> Perhaps<Self> {
use SequencerCommand::*;
match self {
Focus(cmd) => delegate(cmd, Focus, state),
Phrases(cmd) => delegate(cmd, Phrases, state),
Editor(cmd) => delegate(cmd, Editor, state),
Clock(cmd) => delegate(cmd, Clock, state),
Playhead(cmd) => delegate(cmd, Playhead, state)
}
}
}
impl<T> Command<T> for ArrangerCommand
where
T: FocusGrid + ArrangerControl + HasPhrases + PhraseControl + ClockApi + PlayheadApi
{
fn execute (self, state: &mut T) -> Perhaps<Self> {
use ArrangerCommand::*;
match self {
Focus(cmd) => { delegate(cmd, Focus, &mut state) },
Scene(cmd) => { delegate(cmd, Scene, &mut state) },
Track(cmd) => { delegate(cmd, Track, &mut state) },
Clip(cmd) => { delegate(cmd, Clip, &mut state) },
Phrases(cmd) => { delegate(cmd, Phrases, &mut state) },
Editor(cmd) => { delegate(cmd, Editor, &mut state) },
Clock(cmd) => { delegate(cmd, Clock, &mut state) },
Playhead(cmd) => { delegate(cmd, Playhead, &mut state) },
Zoom(zoom) => { todo!(); },
Select(selected) => { state.selected = selected; Ok(None) },
EditPhrase(phrase) => {
state.editor.phrase = phrase.clone();
state.focus(ArrangerFocus::PhraseEditor);
state.focus_enter();
Ok(None)
}
}
}
}
impl<T: ArrangerControl> Command<T> for ArrangerSceneCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
todo!();
Ok(None)
}
}
impl<T: ArrangerControl> Command<T> for ArrangerTrackCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
todo!();
Ok(None)
}
}
impl<T: ArrangerControl> Command<T> for ArrangerClipCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
todo!();
Ok(None)
}
}
impl<T: PhrasesControl> Command<T> for PhrasesCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhraseRenameCommand as Rename;
use PhraseLengthCommand as Length;
match self {
Self::Select(phrase) => {
state.phrase = phrase
},
Self::Edit(command) => {
return Ok(command.execute(&mut state)?.map(Self::Edit))
}
Self::Rename(command) => match command {
Rename::Begin => self.phrases_rename_begin(),
_ => return Ok(command.execute(state)?.map(Self::Rename)),
},
Self::Length(command) => match command {
Length::Begin => self.phrases_length_begin(),
_ => return Ok(command.execute(state)?.map(Self::Length)),
},
}
Ok(None)
}
}
impl<T: PhrasesControl> Command<T> for PhraseLengthCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhraseLengthFocus::*;
use PhraseLengthCommand::*;
if let Some(PhrasesMode::Length(phrase, ref mut length, ref mut focus)) = state.mode {
match self {
Self::Cancel => {
state.mode = 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 phrase = state.phrases[phrase].write().unwrap();
let old_length = phrase.length;
phrase.length = length;
state.mode = None;
return Ok(Some(Self::Set(old_length)))
},
_ => unreachable!()
}
Ok(None)
} else if self == Begin {
self.phrases_length_begin();
Ok(None)
} else {
unreachable!()
}
}
}
impl<T: PhrasesControl> Command<T> for PhraseRenameCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhraseRenameCommand::*;
if let Some(PhrasesMode::Rename(phrase, ref mut old_name)) = state.mode {
match self {
Set(s) => {
state.phrases[phrase].write().unwrap().name = s.into();
return Ok(Some(Self::Set(old_name.clone())))
},
Confirm => {
let old_name = old_name.clone();
state.mode = None;
return Ok(Some(Self::Set(old_name)))
},
Cancel => {
let mut phrase = state.phrases[phrase].write().unwrap();
phrase.name = old_name.clone();
},
_ => unreachable!()
};
Ok(None)
} else if self == Begin {
self.phrases_rename_begin();
Ok(None)
} else {
unreachable!()
}
}
}
impl<T: PhraseControl> Command<T> for PhraseCommand {
//fn translate (self, state: &PhraseTui<E>) -> Self {
//use PhraseCommand::*;
//match self {
//GoUp => match state.entered { true => NoteCursorInc, false => NoteScrollInc, },
//GoDown => match state.entered { true => NoteCursorDec, false => NoteScrollDec, },
//GoLeft => match state.entered { true => TimeCursorDec, false => TimeScrollDec, },
//GoRight => match state.entered { true => TimeCursorInc, false => TimeScrollInc, },
//_ => self
//}
//}
fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhraseCommand::*;
match self.translate(state) {
ToggleDirection => {
state.mode = !state.mode;
},
EnterEditMode => {
state.entered = true;
},
ExitEditMode => {
state.entered = false;
},
TimeZoomOut => {
let scale = state.time_axis().read().unwrap().scale;
state.time_axis().write().unwrap().scale = next_note_length(scale)
},
TimeZoomIn => {
let scale = state.time_axis().read().unwrap().scale;
state.time_axis().write().unwrap().scale = prev_note_length(scale)
},
TimeCursorDec => {
let scale = state.time_axis().read().unwrap().scale;
state.time_axis().write().unwrap().point_dec(scale);
},
TimeCursorInc => {
let scale = state.time_axis().read().unwrap().scale;
state.time_axis().write().unwrap().point_inc(scale);
},
TimeScrollDec => {
let scale = state.time_axis().read().unwrap().scale;
state.time_axis().write().unwrap().start_dec(scale);
},
TimeScrollInc => {
let scale = state.time_axis().read().unwrap().scale;
state.time_axis().write().unwrap().start_inc(scale);
},
NoteCursorDec => {
let mut axis = state.note_axis().write().unwrap();
axis.point_inc(1);
if let Some(point) = axis.point { if point > 73 { axis.point = Some(73); } }
},
NoteCursorInc => {
let mut axis = state.note_axis().write().unwrap();
axis.point_dec(1);
if let Some(point) = axis.point { if point < axis.start { axis.start = (point / 2) * 2; } }
},
NoteScrollDec => {
state.note_axis().write().unwrap().start_inc(1);
},
NoteScrollInc => {
state.note_axis().write().unwrap().start_dec(1);
},
NoteLengthDec => {
*state.note_len_mut() = prev_note_length(state.note_len())
},
NoteLengthInc => {
*state.note_len_mut() = next_note_length(state.note_len())
},
NotePageUp => {
let mut axis = state.note_axis().write().unwrap();
axis.start_dec(3);
axis.point_dec(3);
},
NotePageDown => {
let mut axis = state.note_axis().write().unwrap();
axis.start_inc(3);
axis.point_inc(3);
},
NoteAppend => {
if state.entered {
state.put();
state.time_cursor_advance();
}
},
NoteSet => {
if state.entered { state.put(); }
},
_ => unreachable!()
}
Ok(None)
}
}