tek/crates/tek_tui/src/tui_command.rs

356 lines
12 KiB
Rust

use crate::*;
#[derive(Clone, Debug, PartialEq)]
pub enum TransportCommand {
Focus(FocusCommand),
Clock(ClockCommand),
}
impl<T: TransportControl> Command<T> for TransportCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use TransportCommand::{Focus, Clock};
use FocusCommand::{Next, Prev};
use ClockCommand::{SetBpm, SetQuant, SetSync};
Ok(match self {
Focus(cmd) => cmd.execute(state)?.map(Focus),
Clock(SetBpm(bpm)) => Some(Clock(SetBpm(state.bpm().set(bpm)))),
Clock(SetQuant(quant)) => Some(Clock(SetQuant(state.quant().set(quant)))),
Clock(SetSync(sync)) => Some(Clock(SetSync(state.sync().set(sync)))),
_ => return Ok(None)
})
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum SequencerCommand {
Focus(FocusCommand),
Undo,
Redo,
Clear,
Clock(ClockCommand),
Phrases(PhrasesCommand),
Editor(PhraseCommand),
}
impl Command<SequencerTui> for SequencerCommand {
fn execute (self, state: &mut SequencerTui) -> Perhaps<Self> {
use SequencerCommand::*;
Ok(match self {
Focus(cmd) => cmd.execute(state)?.map(Focus),
Phrases(cmd) => cmd.execute(state)?.map(Phrases),
Editor(cmd) => cmd.execute(state)?.map(Editor),
Clock(cmd) => cmd.execute(state)?.map(Clock),
Undo => { todo!() },
Redo => { todo!() },
Clear => { todo!() },
})
}
}
#[derive(Clone, Debug)]
pub enum ArrangerCommand {
Focus(FocusCommand),
Undo,
Redo,
Clear,
Color(ItemColor),
Clock(ClockCommand),
Scene(ArrangerSceneCommand),
Track(ArrangerTrackCommand),
Clip(ArrangerClipCommand),
Select(ArrangerSelection),
Zoom(usize),
Phrases(PhrasesCommand),
Editor(PhraseCommand),
EditPhrase(Option<Arc<RwLock<Phrase>>>),
}
impl Command<ArrangerTui> for ArrangerCommand {
fn execute (self, state: &mut ArrangerTui) -> Perhaps<Self> {
use ArrangerCommand::*;
Ok(match self {
Focus(cmd) => cmd.execute(state)?.map(Focus),
Scene(cmd) => cmd.execute(state)?.map(Scene),
Track(cmd) => cmd.execute(state)?.map(Track),
Clip(cmd) => cmd.execute(state)?.map(Clip),
Phrases(cmd) => cmd.execute(state)?.map(Phrases),
Editor(cmd) => cmd.execute(state)?.map(Editor),
Clock(cmd) => cmd.execute(state)?.map(Clock),
Zoom(zoom) => { todo!(); },
Select(selected) => {
*state.selected_mut() = selected;
None
},
EditPhrase(phrase) => {
state.edit_phrase(&phrase);
None
},
_ => { todo!() }
})
}
}
impl Command<ArrangerTui> for ArrangerSceneCommand {
fn execute (self, state: &mut ArrangerTui) -> Perhaps<Self> {
todo!();
Ok(None)
}
}
impl Command<ArrangerTui> for ArrangerTrackCommand {
fn execute (self, state: &mut ArrangerTui) -> Perhaps<Self> {
todo!();
Ok(None)
}
}
impl Command<ArrangerTui> for ArrangerClipCommand {
fn execute (self, state: &mut ArrangerTui) -> Perhaps<Self> {
todo!();
Ok(None)
}
}
#[derive(Clone, PartialEq, Debug)]
pub enum PhrasesCommand {
Select(usize),
Phrase(PhrasePoolCommand),
Rename(PhraseRenameCommand),
Length(PhraseLengthCommand),
Import(FileBrowserCommand),
Export(FileBrowserCommand),
}
impl<T: PhrasesControl> Command<T> for PhrasesCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhrasesCommand::*;
Ok(match self {
Phrase(command) => command.execute(state)?.map(Phrase),
Rename(command) => match command {
PhraseRenameCommand::Begin => {
let length = state.phrases()[state.phrase_index()].read().unwrap().length;
*state.phrases_mode_mut() = Some(
PhrasesMode::Length(state.phrase_index(), length, PhraseLengthFocus::Bar)
);
None
},
_ => command.execute(state)?.map(Rename)
},
Length(command) => match command {
PhraseLengthCommand::Begin => {
let name = state.phrases()[state.phrase_index()].read().unwrap().name.clone();
*state.phrases_mode_mut() = Some(
PhrasesMode::Rename(state.phrase_index(), name)
);
None
},
_ => command.execute(state)?.map(Length)
},
Import(command) => match command {
FileBrowserCommand::Begin => {
*state.phrases_mode_mut() = Some(
PhrasesMode::Import(state.phrase_index(), FileBrowser::new(None)?)
);
None
},
_ => command.execute(state)?.map(Import)
},
Export(command) => match command {
FileBrowserCommand::Begin => {
*state.phrases_mode_mut() = Some(
PhrasesMode::Export(state.phrase_index(), FileBrowser::new(None)?)
);
None
},
_ => command.execute(state)?.map(Export)
},
Select(phrase) => {
state.set_phrase_index(phrase);
None
},
})
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum PhraseLengthCommand {
Begin,
Cancel,
Set(usize),
Next,
Prev,
Inc,
Dec,
}
impl<T: PhrasesControl> Command<T> for PhraseLengthCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhraseLengthFocus::*;
use PhraseLengthCommand::*;
match state.phrases_mode_mut().clone() {
Some(PhrasesMode::Length(phrase, ref mut length, ref mut focus)) => match self {
Cancel => { *state.phrases_mode_mut() = None; },
Prev => { focus.prev() },
Next => { focus.next() },
Inc => match focus {
Bar => { *length += 4 * PPQ },
Beat => { *length += PPQ },
Tick => { *length += 1 },
},
Dec => match focus {
Bar => { *length = length.saturating_sub(4 * PPQ) },
Beat => { *length = length.saturating_sub(PPQ) },
Tick => { *length = length.saturating_sub(1) },
},
Set(length) => {
let mut phrase = state.phrases()[phrase].write().unwrap();
let old_length = phrase.length;
phrase.length = length;
std::mem::drop(phrase);
*state.phrases_mode_mut() = None;
return Ok(Some(Self::Set(old_length)))
},
_ => unreachable!()
},
_ => unreachable!()
};
Ok(None)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum PhraseRenameCommand {
Begin,
Cancel,
Confirm,
Set(String),
}
impl<T: PhrasesControl> Command<T> for PhraseRenameCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhraseRenameCommand::*;
match state.phrases_mode_mut().clone() {
Some(PhrasesMode::Rename(phrase, ref mut old_name)) => 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.phrases_mode_mut() = None;
return Ok(Some(Self::Set(old_name)))
},
Cancel => {
state.phrases()[phrase].write().unwrap().name = old_name.clone();
},
_ => unreachable!()
},
_ => unreachable!()
};
Ok(None)
}
}
/// Commands supported by [FileBrowser]
#[derive(Debug, Clone, PartialEq)]
pub enum FileBrowserCommand {
Begin,
Cancel,
Confirm,
Select(usize),
Chdir(PathBuf),
Filter(String),
}
impl<T: PhrasesControl> Command<T> for FileBrowserCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use FileBrowserCommand::*;
let mode = state.phrases_mode_mut();
match mode {
Some(PhrasesMode::Import(index, ref mut browser)) => match self {
Cancel => {
*mode = None;
},
Chdir(cwd) => {
*mode = Some(PhrasesMode::Import(*index, FileBrowser::new(Some(cwd))?));
},
Select(index) => {
browser.index = index;
},
Confirm => {
todo!("import midi to phrase");
},
_ => todo!(),
_ => unreachable!()
},
Some(PhrasesMode::Export(index, ref mut browser)) => match self {
Cancel => {
*mode = None;
},
Chdir(cwd) => {
*mode = Some(PhrasesMode::Export(*index, FileBrowser::new(Some(cwd))?));
},
Select(index) => {
browser.index = index;
},
_ => unreachable!()
},
_ => unreachable!(),
};
Ok(None)
}
}
#[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(Option<usize>),
NoteLengthSet(usize),
NoteScrollSet(usize),
TimeCursorSet(Option<usize>),
TimeScrollSet(usize),
TimeZoomSet(usize),
}
impl<T: PhraseEditorControl + HasEnter> Command<T> for PhraseCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhraseCommand::*;
Ok(match self {
ToggleDirection => { todo!() },
EnterEditMode => { state.focus_enter(); None },
ExitEditMode => { state.focus_exit(); None },
NoteAppend => {
if state.phrase_editor_entered() {
state.put_note();
state.time_cursor_advance();
}
None
},
NoteSet => { if state.phrase_editor_entered() { state.put_note(); } None },
TimeCursorSet(time) => { state.time_axis().write().unwrap().point_set(time); None },
TimeScrollSet(time) => { state.time_axis().write().unwrap().start_set(time); None },
TimeZoomSet(zoom) => { state.time_axis().write().unwrap().scale_set(zoom); None },
NoteScrollSet(note) => { state.note_axis().write().unwrap().start_set(note); None },
NoteLengthSet(time) => { *state.note_len_mut() = time; None },
NoteCursorSet(note) => {
let mut axis = state.note_axis().write().unwrap();
axis.point_set(note);
if let Some(point) = axis.point {
if point > 73 {
axis.point = Some(73);
}
if point < axis.start {
axis.start = (point / 2) * 2;
}
}
None
},
_ => unreachable!()
})
}
}