tek/crates/tek_sequencer/src/sequencer_cmd.rs
2024-11-08 20:05:05 +01:00

240 lines
8.1 KiB
Rust

use crate::*;
#[derive(Clone, PartialEq)]
pub enum SequencerCommand {
FocusNext,
FocusPrev,
FocusUp,
FocusDown,
FocusLeft,
FocusRight,
Transport(TransportCommand),
Phrases(PhrasePoolCommand),
Editor(PhraseEditorCommand),
}
#[derive(Clone, PartialEq)]
pub enum PhrasePoolCommand {
Prev,
Next,
MoveUp,
MoveDown,
Delete,
Append,
Insert,
Duplicate,
RandomColor,
Edit,
Import,
Export,
Rename(PhraseRenameCommand),
Length(PhraseLengthCommand),
}
#[derive(Clone, PartialEq)]
pub enum PhraseRenameCommand {
Begin,
Backspace,
Append(char),
Set(String),
Confirm,
Cancel,
}
#[derive(Clone, PartialEq)]
pub enum PhraseLengthCommand {
Begin,
Next,
Prev,
Inc,
Dec,
Set(usize),
Confirm,
Cancel,
}
#[derive(Clone, PartialEq)]
pub enum PhraseEditorCommand {
// TODO: 1-9 seek markers that by default start every 8th of the phrase
ToggleDirection,
EnterEditMode,
ExitEditMode,
NoteLengthDec,
NoteLengthInc,
TimeZoomIn,
TimeZoomOut,
NoteAppend,
NoteSet,
NotePageUp,
NotePageDown,
GoUp,
GoDown,
GoLeft,
GoRight,
}
impl<E: Engine> Command<Sequencer<E>> for SequencerCommand {
fn run (&self, state: &mut Sequencer<E>) -> Perhaps<Self> {
use SequencerCommand::*;
match self {
FocusNext => { state.focus_next(); },
FocusPrev => { state.focus_prev(); },
FocusUp => { state.focus_up(); },
FocusDown => { state.focus_down(); },
FocusLeft => { state.focus_left(); },
FocusRight => { state.focus_right(); },
Transport(command) => if let Some(ref transport) = state.transport {
return command.run(&mut*transport.write().unwrap()).map(|x|x.map(Transport))
},
Phrases(command) => {
return command.run(&mut*state.phrases.write().unwrap()).map(|x|x.map(Phrases))
},
Editor(command) => {
return command.run(&mut state.editor).map(|x|x.map(Editor))
},
}
Ok(None)
}
}
impl<E: Engine> Command<PhrasePool<E>> for PhrasePoolCommand {
fn run (&self, state: &mut PhrasePool<E>) -> Perhaps<Self> {
use PhrasePoolCommand::*;
use PhraseRenameCommand as Rename;
use PhraseLengthCommand as Length;
match self {
Prev => { state.select_prev() },
Next => { state.select_next() },
Delete => { state.delete_selected() },
Append => { state.append_new(None, None) },
Insert => { state.insert_new(None, None) },
Duplicate => { state.insert_dup() },
Edit => { todo!(); }
RandomColor => { state.randomize_color() },
MoveUp => { state.move_up() },
MoveDown => { state.move_down() },
Rename(Rename::Begin) => { state.begin_rename() },
Rename(_) => { unreachable!() },
Length(Length::Begin) => { state.begin_length() },
Length(_) => { unreachable!() },
Import => todo!(),
Export => todo!(),
}
Ok(None)
}
}
impl<E: Engine> Command<PhrasePool<E>> for PhraseRenameCommand {
fn run (&self, state: &mut PhrasePool<E>) -> Perhaps<Self> {
use PhraseRenameCommand::*;
if let Some(PhrasePoolMode::Rename(phrase, ref mut old_name)) = state.mode {
match self {
Begin => { unreachable!(); },
Backspace => {
let mut phrase = state.phrases[phrase].write().unwrap();
let old_name = phrase.name.clone();
phrase.name.pop();
return Ok(Some(Self::Set(old_name)))
},
Append(c) => {
let mut phrase = state.phrases[phrase].write().unwrap();
let old_name = phrase.name.clone();
phrase.name.push(*c);
return Ok(Some(Self::Set(old_name)))
},
Set(s) => {
let mut phrase = state.phrases[phrase].write().unwrap();
phrase.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();
}
};
Ok(None)
} else if *self == Begin {
todo!()
} else {
unreachable!()
}
}
}
impl<E: Engine> Command<PhrasePool<E>> for PhraseLengthCommand {
fn run (&self, state: &mut PhrasePool<E>) -> Perhaps<Self> {
use PhraseLengthCommand::*;
if let Some(PhrasePoolMode::Length(phrase, ref mut length, ref mut focus)) = state.mode {
match self {
Begin => { unreachable!(); },
Cancel => { state.mode = None; },
Confirm => { return Self::Set(*length).run(state) },
Prev => { focus.prev() },
Next => { focus.next() },
Inc => {
use PhraseLengthFocus::*;
match focus {
Bar => { *length += 4 * PPQ },
Beat => { *length += PPQ },
Tick => { *length += 1 },
}
},
Dec => {
use PhraseLengthFocus::*;
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;
state.mode = None;
return Ok(Some(Self::Set(old_length)))
},
}
Ok(None)
} else if *self == Begin {
todo!()
} else {
unreachable!()
}
}
}
impl<E: Engine> Command<PhraseEditor<E>> for PhraseEditorCommand {
fn run (&self, state: &mut PhraseEditor<E>) -> Perhaps<Self> {
use PhraseEditorCommand::*;
match self {
ToggleDirection => { state.mode = !state.mode; },
EnterEditMode => { state.entered = true; },
ExitEditMode => { state.entered = false; },
TimeZoomOut => { state.time_zoom_out() },
TimeZoomIn => { state.time_zoom_in() },
NoteLengthDec => { state.note_length_dec() },
NoteLengthInc => { state.note_length_inc() },
NotePageUp => { state.note_page_up() },
NotePageDown => { state.note_page_down() },
NoteAppend => if state.entered {
state.put();
state.time_cursor_advance();
},
NoteSet => if state.entered { state.put(); },
GoUp => match state.entered {
true => state.note_cursor_inc(),
false => state.note_scroll_inc(),
},
GoDown => match state.entered {
true => state.note_cursor_dec(),
false => state.note_scroll_dec(),
},
GoLeft => match state.entered {
true => state.time_cursor_dec(),
false => state.time_scroll_dec(),
},
GoRight => match state.entered {
true => state.time_cursor_inc(),
false => state.time_scroll_inc(),
},
}
Ok(None)
}
}