mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 12:46:42 +01:00
FocusCommand, Command::translate, ret old from TimeUnit::set
This commit is contained in:
parent
2b78339e61
commit
fe25da4f53
9 changed files with 249 additions and 261 deletions
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub trait Command<S>: Sized {
|
pub trait Command<S>: Sized {
|
||||||
fn run (&self, state: &mut S) -> Perhaps<Self>;
|
fn translate (self, _: &S) -> Self { self }
|
||||||
|
fn execute (self, state: &mut S) -> Perhaps<Self>;
|
||||||
}
|
}
|
||||||
pub trait MatchInput<E: Engine, S>: Sized {
|
pub trait MatchInput<E: Engine, S>: Sized {
|
||||||
fn match_input (state: &S, input: &E::Input) -> Option<Self>;
|
fn match_input (state: &S, input: &E::Input) -> Option<Self>;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,15 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum FocusCommand {
|
||||||
|
Next,
|
||||||
|
Prev,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait FocusGrid<T: Copy + PartialEq> {
|
pub trait FocusGrid<T: Copy + PartialEq> {
|
||||||
fn layout (&self) -> &[&[T]];
|
fn layout (&self) -> &[&[T]];
|
||||||
fn cursor (&self) -> (usize, usize);
|
fn cursor (&self) -> (usize, usize);
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,10 @@ pub const PPQ: usize = 96;
|
||||||
/// This should mean that, even at 192kHz sampling rate, over 1 year of audio
|
/// This should mean that, even at 192kHz sampling rate, over 1 year of audio
|
||||||
/// can be clocked in microseconds with f64 without losing precision.
|
/// can be clocked in microseconds with f64 without losing precision.
|
||||||
pub trait TimeUnit {
|
pub trait TimeUnit {
|
||||||
fn get (&self) -> f64;// { self.0.load(Ordering::Relaxed) }
|
/// Returns current value
|
||||||
fn set (&self, value: f64);// { self.0.store(value, Ordering::Relaxed) }
|
fn get (&self) -> f64;
|
||||||
|
/// Sets new value, returns old
|
||||||
|
fn set (&self, value: f64) -> f64;
|
||||||
}
|
}
|
||||||
/// Implement arithmetic for a unit of time
|
/// Implement arithmetic for a unit of time
|
||||||
macro_rules! impl_op {
|
macro_rules! impl_op {
|
||||||
|
|
@ -40,7 +42,11 @@ macro_rules! impl_time_unit {
|
||||||
($T:ident) => {
|
($T:ident) => {
|
||||||
impl TimeUnit for $T {
|
impl TimeUnit for $T {
|
||||||
fn get (&self) -> f64 { self.0.load(Ordering::Relaxed) }
|
fn get (&self) -> f64 { self.0.load(Ordering::Relaxed) }
|
||||||
fn set (&self, value: f64) { self.0.store(value, Ordering::Relaxed) }
|
fn set (&self, value: f64) -> f64 {
|
||||||
|
let old = self.get();
|
||||||
|
self.0.store(value, Ordering::Relaxed);
|
||||||
|
old
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl_op!($T, Add, add, |a, b|{a + b});
|
impl_op!($T, Add, add, |a, b|{a + b});
|
||||||
impl_op!($T, Sub, sub, |a, b|{a - b});
|
impl_op!($T, Sub, sub, |a, b|{a - b});
|
||||||
|
|
|
||||||
|
|
@ -38,27 +38,27 @@ pub enum ArrangementCommand {
|
||||||
GoLeft,
|
GoLeft,
|
||||||
GoRight,
|
GoRight,
|
||||||
}
|
}
|
||||||
impl <E: Engine> Command<Arranger<E>> for ArrangerCommand {
|
impl<E: Engine> Command<Arranger<E>> for ArrangerCommand {
|
||||||
fn run (&self, state: &mut Arranger<E>) -> Perhaps<Self> {
|
fn execute (self, state: &mut Arranger<E>) -> Perhaps<Self> {
|
||||||
use ArrangerCommand::*;
|
use ArrangerCommand::*;
|
||||||
match self {
|
match self {
|
||||||
FocusNext => { state.focus_next(); },
|
FocusNext => { state.focus_next(); },
|
||||||
FocusPrev => { state.focus_prev(); },
|
FocusPrev => { state.focus_prev(); },
|
||||||
FocusUp => { state.focus_up(); },
|
FocusUp => { state.focus_up(); },
|
||||||
FocusDown => { state.focus_down(); },
|
FocusDown => { state.focus_down(); },
|
||||||
FocusLeft => { state.focus_left(); },
|
FocusLeft => { state.focus_left(); },
|
||||||
FocusRight => { state.focus_right(); },
|
FocusRight => { state.focus_right(); },
|
||||||
Transport(command) => if let Some(ref transport) = state.transport {
|
Transport(cmd) => if let Some(ref transport) = state.transport {
|
||||||
return command.run(&mut*transport.write().unwrap()).map(|x|x.map(Transport))
|
return cmd.execute(&mut*transport.write().unwrap()).map(|x|x.map(Transport))
|
||||||
},
|
},
|
||||||
Phrases(command) => {
|
Phrases(cmd) => {
|
||||||
return command.run(&mut*state.phrases.write().unwrap()).map(|x|x.map(Phrases))
|
return cmd.execute(&mut*state.phrases.write().unwrap()).map(|x|x.map(Phrases))
|
||||||
},
|
},
|
||||||
Editor(command) => {
|
Editor(cmd) => {
|
||||||
return command.run(&mut state.editor).map(|x|x.map(Editor))
|
return cmd.execute(&mut state.editor).map(|x|x.map(Editor))
|
||||||
},
|
},
|
||||||
Arrangement(command) => {
|
Arrangement(cmd) => {
|
||||||
return command.run(&mut state.arrangement).map(|x|x.map(Arrangement))
|
return cmd.execute(&mut state.arrangement).map(|x|x.map(Arrangement))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
state.show_phrase();
|
state.show_phrase();
|
||||||
|
|
@ -66,8 +66,8 @@ impl <E: Engine> Command<Arranger<E>> for ArrangerCommand {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl <E: Engine> Command<Arrangement<E>> for ArrangementCommand {
|
impl<E: Engine> Command<Arrangement<E>> for ArrangementCommand {
|
||||||
fn run (&self, state: &mut Arrangement<E>) -> Perhaps<Self> {
|
fn execute (self, state: &mut Arrangement<E>) -> Perhaps<Self> {
|
||||||
use ArrangementCommand::*;
|
use ArrangementCommand::*;
|
||||||
match self {
|
match self {
|
||||||
New => todo!(),
|
New => todo!(),
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ impl Content for Arrangement<Tui> {
|
||||||
ArrangementViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor)),
|
ArrangementViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor)),
|
||||||
}?;
|
}?;
|
||||||
let color = if self.focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)};
|
let color = if self.focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)};
|
||||||
add(&TuiStyle::fg("Session", color).push_x(2))?;
|
add(&TuiStyle::fg("Project", color).push_x(2))?;
|
||||||
add(&self.size)
|
add(&self.size)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -511,21 +511,23 @@ impl<'a> Content for HorizontalArranger<'a, Tui> {
|
||||||
/// Handle top-level events in standalone arranger.
|
/// Handle top-level events in standalone arranger.
|
||||||
impl Handle<Tui> for Arranger<Tui> {
|
impl Handle<Tui> for Arranger<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
if let Some(command) = ArrangerCommand::match_input(self, from) {
|
Ok(if let Some(command) = ArrangerCommand::match_input(self, from) {
|
||||||
let _undo = command.run(self)?;
|
let _undo = command.execute(self)?;
|
||||||
return Ok(Some(true))
|
Some(true)
|
||||||
}
|
} else {
|
||||||
Ok(None)
|
None
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Handle events for arrangement.
|
/// Handle events for arrangement.
|
||||||
impl Handle<Tui> for Arrangement<Tui> {
|
impl Handle<Tui> for Arrangement<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
if let Some(command) = ArrangementCommand::match_input(self, from) {
|
Ok(if let Some(command) = ArrangementCommand::match_input(self, from) {
|
||||||
let _undo = command.run(self)?;
|
let _undo = command.execute(self)?;
|
||||||
return Ok(Some(true))
|
Some(true)
|
||||||
}
|
} else {
|
||||||
Ok(None)
|
None
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MatchInput<Tui, Arranger<Tui>> for ArrangerCommand {
|
impl MatchInput<Tui, Arranger<Tui>> for ArrangerCommand {
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,7 @@ use crate::*;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum SequencerCommand {
|
pub enum SequencerCommand {
|
||||||
FocusNext,
|
Focus(FocusCommand),
|
||||||
FocusPrev,
|
|
||||||
FocusUp,
|
|
||||||
FocusDown,
|
|
||||||
FocusLeft,
|
|
||||||
FocusRight,
|
|
||||||
Transport(TransportCommand),
|
Transport(TransportCommand),
|
||||||
Phrases(PhrasePoolCommand),
|
Phrases(PhrasePoolCommand),
|
||||||
Editor(PhraseEditorCommand),
|
Editor(PhraseEditorCommand),
|
||||||
|
|
@ -55,89 +50,103 @@ pub enum PhraseEditorCommand {
|
||||||
ToggleDirection,
|
ToggleDirection,
|
||||||
EnterEditMode,
|
EnterEditMode,
|
||||||
ExitEditMode,
|
ExitEditMode,
|
||||||
|
NoteAppend,
|
||||||
|
NoteCursorDec,
|
||||||
|
NoteCursorInc,
|
||||||
NoteLengthDec,
|
NoteLengthDec,
|
||||||
NoteLengthInc,
|
NoteLengthInc,
|
||||||
|
NotePageDown,
|
||||||
|
NotePageUp,
|
||||||
|
NoteScrollDec,
|
||||||
|
NoteScrollInc,
|
||||||
|
NoteSet,
|
||||||
|
TimeCursorDec,
|
||||||
|
TimeCursorInc,
|
||||||
|
TimeScrollDec,
|
||||||
|
TimeScrollInc,
|
||||||
TimeZoomIn,
|
TimeZoomIn,
|
||||||
TimeZoomOut,
|
TimeZoomOut,
|
||||||
NoteAppend,
|
|
||||||
NoteSet,
|
|
||||||
NotePageUp,
|
|
||||||
NotePageDown,
|
|
||||||
GoUp,
|
GoUp,
|
||||||
GoDown,
|
GoDown,
|
||||||
GoLeft,
|
GoLeft,
|
||||||
GoRight,
|
GoRight,
|
||||||
}
|
}
|
||||||
impl<E: Engine> Command<Sequencer<E>> for SequencerCommand {
|
impl<E: Engine> Command<Sequencer<E>> for SequencerCommand {
|
||||||
fn run (&self, state: &mut Sequencer<E>) -> Perhaps<Self> {
|
fn execute (self, state: &mut Sequencer<E>) -> Perhaps<Self> {
|
||||||
|
use FocusCommand::*;
|
||||||
use SequencerCommand::*;
|
use SequencerCommand::*;
|
||||||
match self {
|
match self {
|
||||||
FocusNext => { state.focus_next(); },
|
Focus(command) => match command {
|
||||||
FocusPrev => { state.focus_prev(); },
|
Next => { state.focus_next(); },
|
||||||
FocusUp => { state.focus_up(); },
|
Prev => { state.focus_prev(); },
|
||||||
FocusDown => { state.focus_down(); },
|
Up => { state.focus_up(); },
|
||||||
FocusLeft => { state.focus_left(); },
|
Down => { state.focus_down(); },
|
||||||
FocusRight => { state.focus_right(); },
|
Left => { state.focus_left(); },
|
||||||
|
Right => { state.focus_right(); },
|
||||||
|
},
|
||||||
Transport(command) => if let Some(ref transport) = state.transport {
|
Transport(command) => if let Some(ref transport) = state.transport {
|
||||||
return command.run(&mut*transport.write().unwrap()).map(|x|x.map(Transport))
|
return command.execute(&mut*transport.write().unwrap()).map(|x|x.map(Transport))
|
||||||
},
|
},
|
||||||
Phrases(command) => {
|
Phrases(command) => {
|
||||||
return command.run(&mut*state.phrases.write().unwrap()).map(|x|x.map(Phrases))
|
return command.execute(&mut*state.phrases.write().unwrap()).map(|x|x.map(Phrases))
|
||||||
},
|
},
|
||||||
Editor(command) => {
|
Editor(command) => {
|
||||||
return command.run(&mut state.editor).map(|x|x.map(Editor))
|
return command.execute(&mut state.editor).map(|x|x.map(Editor))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<E: Engine> Command<PhrasePool<E>> for PhrasePoolCommand {
|
impl<E: Engine> Command<PhrasePool<E>> for PhrasePoolCommand {
|
||||||
fn run (&self, state: &mut PhrasePool<E>) -> Perhaps<Self> {
|
fn execute (self, state: &mut PhrasePool<E>) -> Perhaps<Self> {
|
||||||
use PhrasePoolCommand::*;
|
use PhrasePoolCommand::*;
|
||||||
use PhraseRenameCommand as Rename;
|
use PhraseRenameCommand as Rename;
|
||||||
use PhraseLengthCommand as Length;
|
use PhraseLengthCommand as Length;
|
||||||
match self {
|
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(Rename::Begin) => { state.begin_rename() },
|
||||||
Rename(_) => { unreachable!() },
|
|
||||||
Length(Length::Begin) => { state.begin_length() },
|
Length(Length::Begin) => { state.begin_length() },
|
||||||
Length(_) => { unreachable!() },
|
Prev => { state.select_prev() },
|
||||||
Import => todo!(),
|
Next => { state.select_next() },
|
||||||
Export => todo!(),
|
Delete => { state.delete_selected() },
|
||||||
|
Append => { state.append_new(None, None) },
|
||||||
|
Insert => { state.insert_new(None, None) },
|
||||||
|
Duplicate => { state.insert_dup() },
|
||||||
|
RandomColor => { state.randomize_color() },
|
||||||
|
MoveUp => { state.move_up() },
|
||||||
|
MoveDown => { state.move_down() },
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<E: Engine> Command<PhrasePool<E>> for PhraseRenameCommand {
|
impl<E: Engine> Command<PhrasePool<E>> for PhraseRenameCommand {
|
||||||
fn run (&self, state: &mut PhrasePool<E>) -> Perhaps<Self> {
|
fn translate (self, state: &PhrasePool<E>) -> Self {
|
||||||
|
use PhraseRenameCommand::*;
|
||||||
|
if let Some(PhrasePoolMode::Rename(_, ref old_name)) = state.mode {
|
||||||
|
match self {
|
||||||
|
Backspace => {
|
||||||
|
let mut new_name = old_name.clone();
|
||||||
|
new_name.pop();
|
||||||
|
return Self::Set(new_name)
|
||||||
|
},
|
||||||
|
Append(c) => {
|
||||||
|
let mut new_name = old_name.clone();
|
||||||
|
new_name.push(c);
|
||||||
|
return Self::Set(new_name)
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
} else if self != Begin {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn execute (self, state: &mut PhrasePool<E>) -> Perhaps<Self> {
|
||||||
use PhraseRenameCommand::*;
|
use PhraseRenameCommand::*;
|
||||||
if let Some(PhrasePoolMode::Rename(phrase, ref mut old_name)) = state.mode {
|
if let Some(PhrasePoolMode::Rename(phrase, ref mut old_name)) = state.mode {
|
||||||
match self {
|
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) => {
|
Set(s) => {
|
||||||
let mut phrase = state.phrases[phrase].write().unwrap();
|
state.phrases[phrase].write().unwrap().name = s.into();
|
||||||
phrase.name = s.into();
|
|
||||||
return Ok(Some(Self::Set(old_name.clone())))
|
return Ok(Some(Self::Set(old_name.clone())))
|
||||||
},
|
},
|
||||||
Confirm => {
|
Confirm => {
|
||||||
|
|
@ -148,10 +157,11 @@ impl<E: Engine> Command<PhrasePool<E>> for PhraseRenameCommand {
|
||||||
Cancel => {
|
Cancel => {
|
||||||
let mut phrase = state.phrases[phrase].write().unwrap();
|
let mut phrase = state.phrases[phrase].write().unwrap();
|
||||||
phrase.name = old_name.clone();
|
phrase.name = old_name.clone();
|
||||||
}
|
},
|
||||||
|
_ => unreachable!()
|
||||||
};
|
};
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else if *self == Begin {
|
} else if self == Begin {
|
||||||
todo!()
|
todo!()
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
@ -159,41 +169,48 @@ impl<E: Engine> Command<PhrasePool<E>> for PhraseRenameCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<E: Engine> Command<PhrasePool<E>> for PhraseLengthCommand {
|
impl<E: Engine> Command<PhrasePool<E>> for PhraseLengthCommand {
|
||||||
fn run (&self, state: &mut PhrasePool<E>) -> Perhaps<Self> {
|
fn translate (self, state: &PhrasePool<E>) -> Self {
|
||||||
|
use PhraseLengthCommand::*;
|
||||||
|
if let Some(PhrasePoolMode::Length(_, length, _)) = state.mode {
|
||||||
|
match self {
|
||||||
|
Confirm => { return Self::Set(length) },
|
||||||
|
_ => self
|
||||||
|
}
|
||||||
|
} else if self == Begin {
|
||||||
|
todo!()
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn execute (self, state: &mut PhrasePool<E>) -> Perhaps<Self> {
|
||||||
|
use PhraseLengthFocus::*;
|
||||||
use PhraseLengthCommand::*;
|
use PhraseLengthCommand::*;
|
||||||
if let Some(PhrasePoolMode::Length(phrase, ref mut length, ref mut focus)) = state.mode {
|
if let Some(PhrasePoolMode::Length(phrase, ref mut length, ref mut focus)) = state.mode {
|
||||||
match self {
|
match self {
|
||||||
Begin => { unreachable!(); },
|
|
||||||
Cancel => { state.mode = None; },
|
Cancel => { state.mode = None; },
|
||||||
Confirm => { return Self::Set(*length).run(state) },
|
|
||||||
Prev => { focus.prev() },
|
Prev => { focus.prev() },
|
||||||
Next => { focus.next() },
|
Next => { focus.next() },
|
||||||
Inc => {
|
Inc => match focus {
|
||||||
use PhraseLengthFocus::*;
|
Bar => { *length += 4 * PPQ },
|
||||||
match focus {
|
Beat => { *length += PPQ },
|
||||||
Bar => { *length += 4 * PPQ },
|
Tick => { *length += 1 },
|
||||||
Beat => { *length += PPQ },
|
|
||||||
Tick => { *length += 1 },
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Dec => {
|
Dec => match focus {
|
||||||
use PhraseLengthFocus::*;
|
Bar => { *length = length.saturating_sub(4 * PPQ) },
|
||||||
match focus {
|
Beat => { *length = length.saturating_sub(PPQ) },
|
||||||
Bar => { *length = length.saturating_sub(4 * PPQ) },
|
Tick => { *length = length.saturating_sub(1) },
|
||||||
Beat => { *length = length.saturating_sub(PPQ) },
|
|
||||||
Tick => { *length = length.saturating_sub(1) },
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Set(length) => {
|
Set(length) => {
|
||||||
let mut phrase = state.phrases[phrase].write().unwrap();
|
let mut phrase = state.phrases[phrase].write().unwrap();
|
||||||
let old_length = phrase.length;
|
let old_length = phrase.length;
|
||||||
phrase.length = *length;
|
phrase.length = length;
|
||||||
state.mode = None;
|
state.mode = None;
|
||||||
return Ok(Some(Self::Set(old_length)))
|
return Ok(Some(Self::Set(old_length)))
|
||||||
},
|
},
|
||||||
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else if *self == Begin {
|
} else if self == Begin {
|
||||||
todo!()
|
todo!()
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
@ -201,9 +218,19 @@ impl<E: Engine> Command<PhrasePool<E>> for PhraseLengthCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<E: Engine> Command<PhraseEditor<E>> for PhraseEditorCommand {
|
impl<E: Engine> Command<PhraseEditor<E>> for PhraseEditorCommand {
|
||||||
fn run (&self, state: &mut PhraseEditor<E>) -> Perhaps<Self> {
|
fn translate (self, state: &PhraseEditor<E>) -> Self {
|
||||||
use PhraseEditorCommand::*;
|
use PhraseEditorCommand::*;
|
||||||
match self {
|
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 PhraseEditor<E>) -> Perhaps<Self> {
|
||||||
|
use PhraseEditorCommand::*;
|
||||||
|
match self.translate(state) {
|
||||||
ToggleDirection => { state.mode = !state.mode; },
|
ToggleDirection => { state.mode = !state.mode; },
|
||||||
EnterEditMode => { state.entered = true; },
|
EnterEditMode => { state.entered = true; },
|
||||||
ExitEditMode => { state.entered = false; },
|
ExitEditMode => { state.entered = false; },
|
||||||
|
|
@ -218,22 +245,7 @@ impl<E: Engine> Command<PhraseEditor<E>> for PhraseEditorCommand {
|
||||||
state.time_cursor_advance();
|
state.time_cursor_advance();
|
||||||
},
|
},
|
||||||
NoteSet => if state.entered { state.put(); },
|
NoteSet => if state.entered { state.put(); },
|
||||||
GoUp => match state.entered {
|
_ => unreachable!()
|
||||||
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)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ impl Content for Sequencer<Tui> {
|
||||||
impl Handle<Tui> for Sequencer<Tui> {
|
impl Handle<Tui> for Sequencer<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
if let Some(command) = SequencerCommand::match_input(self, from) {
|
if let Some(command) = SequencerCommand::match_input(self, from) {
|
||||||
let _undo = command.run(self)?;
|
let _undo = command.execute(self)?;
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
@ -21,27 +21,30 @@ impl Handle<Tui> for Sequencer<Tui> {
|
||||||
}
|
}
|
||||||
impl MatchInput<Tui, Sequencer<Tui>> for SequencerCommand {
|
impl MatchInput<Tui, Sequencer<Tui>> for SequencerCommand {
|
||||||
fn match_input (state: &Sequencer<Tui>, input: &TuiInput) -> Option<Self> {
|
fn match_input (state: &Sequencer<Tui>, input: &TuiInput) -> Option<Self> {
|
||||||
|
use SequencerCommand::*;
|
||||||
|
use FocusCommand::*;
|
||||||
match input.event() {
|
match input.event() {
|
||||||
key!(KeyCode::Tab) => Some(Self::FocusNext),
|
key!(KeyCode::Tab) => Some(Focus(Next)),
|
||||||
key!(Shift-KeyCode::Tab) => Some(Self::FocusPrev),
|
key!(Shift-KeyCode::Tab) => Some(Focus(Prev)),
|
||||||
key!(KeyCode::BackTab) => Some(Self::FocusPrev),
|
key!(KeyCode::BackTab) => Some(Focus(Prev)),
|
||||||
key!(Shift-KeyCode::BackTab) => Some(Self::FocusPrev),
|
key!(Shift-KeyCode::BackTab) => Some(Focus(Prev)),
|
||||||
key!(KeyCode::Up) => Some(Self::FocusUp),
|
key!(KeyCode::Up) => Some(Focus(Up)),
|
||||||
key!(KeyCode::Down) => Some(Self::FocusDown),
|
key!(KeyCode::Down) => Some(Focus(Down)),
|
||||||
key!(KeyCode::Left) => Some(Self::FocusLeft),
|
key!(KeyCode::Left) => Some(Focus(Left)),
|
||||||
key!(KeyCode::Right) => Some(Self::FocusRight),
|
key!(KeyCode::Right) => Some(Focus(Right)),
|
||||||
key!(KeyCode::Char(' ')) => Some(Self::Transport(TransportCommand::PlayToggle)),
|
key!(KeyCode::Char(' ')) => Some(Transport(TransportCommand::PlayToggle)),
|
||||||
_ => match state.focused() {
|
_ => match state.focused() {
|
||||||
SequencerFocus::Transport => state.transport.as_ref()
|
SequencerFocus::Transport => if let Some(t) = state.transport.as_ref() {
|
||||||
.map(|t|TransportCommand::match_input(&*t.read().unwrap(), input)
|
TransportCommand::match_input(&*t.read().unwrap(), input).map(Transport)
|
||||||
.map(Self::Transport))
|
} else {
|
||||||
.flatten(),
|
None
|
||||||
|
},
|
||||||
SequencerFocus::PhrasePool =>
|
SequencerFocus::PhrasePool =>
|
||||||
PhrasePoolCommand::match_input(&*state.phrases.read().unwrap(), input)
|
PhrasePoolCommand::match_input(&*state.phrases.read().unwrap(), input)
|
||||||
.map(Self::Phrases),
|
.map(Phrases),
|
||||||
SequencerFocus::PhraseEditor =>
|
SequencerFocus::PhraseEditor =>
|
||||||
PhraseEditorCommand::match_input(&state.editor, input)
|
PhraseEditorCommand::match_input(&state.editor, input)
|
||||||
.map(Self::Editor),
|
.map(Editor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +87,7 @@ impl Content for PhrasePool<Tui> {
|
||||||
impl Handle<Tui> for PhrasePool<Tui> {
|
impl Handle<Tui> for PhrasePool<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
if let Some(command) = PhrasePoolCommand::match_input(self, from) {
|
if let Some(command) = PhrasePoolCommand::match_input(self, from) {
|
||||||
let _undo = command.run(self)?;
|
let _undo = command.execute(self)?;
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
@ -282,7 +285,7 @@ impl Content for PhraseEditor<Tui> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let lower_right = format!(
|
let lower_right = format!(
|
||||||
"{}x{}",
|
"{{{}x{}}}",
|
||||||
self.width.load(Ordering::Relaxed),
|
self.width.load(Ordering::Relaxed),
|
||||||
self.height.load(Ordering::Relaxed),
|
self.height.load(Ordering::Relaxed),
|
||||||
);
|
);
|
||||||
|
|
@ -436,7 +439,7 @@ pub(crate) fn keys_vert () -> Buffer {
|
||||||
impl Handle<Tui> for PhraseEditor<Tui> {
|
impl Handle<Tui> for PhraseEditor<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
if let Some(command) = PhraseEditorCommand::match_input(self, from) {
|
if let Some(command) = PhraseEditorCommand::match_input(self, from) {
|
||||||
let _undo = command.run(self)?;
|
let _undo = command.execute(self)?;
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum TransportCommand {
|
pub enum TransportCommand {
|
||||||
FocusNext,
|
FocusNext,
|
||||||
FocusPrev,
|
FocusPrev,
|
||||||
|
|
@ -19,130 +19,84 @@ pub enum TransportCommand {
|
||||||
SetSync(f64),
|
SetSync(f64),
|
||||||
}
|
}
|
||||||
impl<E: Engine> Command<TransportToolbar<E>> for TransportCommand {
|
impl<E: Engine> Command<TransportToolbar<E>> for TransportCommand {
|
||||||
fn run (&self, state: &mut TransportToolbar<E>) -> Perhaps<Self> {
|
fn translate (self, state: &TransportToolbar<E>) -> Self {
|
||||||
|
use TransportCommand::*;
|
||||||
|
use TransportToolbarFocus::*;
|
||||||
match self {
|
match self {
|
||||||
Self::FocusNext => {
|
Increment => match state.focus {
|
||||||
state.focus.next();
|
Bpm =>
|
||||||
|
{return SetBpm(state.clock.timebase().bpm.get() + 1.0) },
|
||||||
|
Quant =>
|
||||||
|
{return SetQuant(next_note_length(state.clock.quant.get()as usize)as f64)},
|
||||||
|
Sync =>
|
||||||
|
{return SetSync(next_note_length(state.clock.sync.get()as usize)as f64+1.)},
|
||||||
|
PlayPause =>
|
||||||
|
{/*todo seek*/},
|
||||||
|
Clock =>
|
||||||
|
{/*todo seek*/},
|
||||||
},
|
},
|
||||||
Self::FocusPrev => {
|
FineIncrement => match state.focus {
|
||||||
state.focus.prev();
|
Bpm =>
|
||||||
|
{return SetBpm(state.clock.timebase().bpm.get() + 0.001)},
|
||||||
|
Quant =>
|
||||||
|
{return Increment},
|
||||||
|
Sync =>
|
||||||
|
{return Increment},
|
||||||
|
PlayPause =>
|
||||||
|
{/*todo seek*/},
|
||||||
|
Clock =>
|
||||||
|
{/*todo seek*/},
|
||||||
},
|
},
|
||||||
Self::PlayToggle => {
|
Decrement => match state.focus {
|
||||||
state.toggle_play()?;
|
Bpm =>
|
||||||
|
{return SetBpm(state.clock.timebase().bpm.get() - 1.0)},
|
||||||
|
Quant =>
|
||||||
|
{return SetQuant(prev_note_length(state.clock.quant.get()as usize)as f64)},
|
||||||
|
Sync =>
|
||||||
|
{return SetSync(prev_note_length(state.clock.sync.get()as usize)as f64)},
|
||||||
|
PlayPause =>
|
||||||
|
{/*todo seek*/},
|
||||||
|
Clock =>
|
||||||
|
{/*todo seek*/},
|
||||||
},
|
},
|
||||||
Self::Increment => {
|
FineDecrement => match state.focus {
|
||||||
match state.focus {
|
Bpm =>
|
||||||
TransportToolbarFocus::Bpm => {
|
{return SetBpm(state.clock.timebase().bpm.get() - 0.001)},
|
||||||
let bpm = state.clock.timebase().bpm.get();
|
Quant =>
|
||||||
return Self::SetBpm(bpm + 1.0).run(state)
|
{return Decrement},
|
||||||
},
|
Sync =>
|
||||||
TransportToolbarFocus::Quant => {
|
{return Decrement},
|
||||||
let quant = state.clock.quant.get() as usize;
|
PlayPause =>
|
||||||
let quant = next_note_length(quant) as f64;
|
{/*todo seek*/},
|
||||||
return Self::SetQuant(quant).run(state)
|
Clock =>
|
||||||
},
|
{/*todo seek*/},
|
||||||
TransportToolbarFocus::Sync => {
|
|
||||||
let sync = state.clock.sync.get() as usize;
|
|
||||||
let sync = next_note_length(sync) as f64;
|
|
||||||
return Self::SetSync(sync + 1.0).run(state)
|
|
||||||
},
|
|
||||||
TransportToolbarFocus::PlayPause => {
|
|
||||||
/*todo seek*/
|
|
||||||
},
|
|
||||||
TransportToolbarFocus::Clock => {
|
|
||||||
/*todo seek*/
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Self::FineIncrement => {
|
_ => {}
|
||||||
match state.focus {
|
};
|
||||||
TransportToolbarFocus::Bpm => {
|
return self
|
||||||
let bpm = state.clock.timebase().bpm.get();
|
}
|
||||||
return Self::SetBpm(bpm + 0.001).run(state)
|
fn execute (self, state: &mut TransportToolbar<E>) -> Perhaps<Self> {
|
||||||
},
|
use TransportCommand::*;
|
||||||
TransportToolbarFocus::Quant => {
|
match self.translate(&state) {
|
||||||
return Self::Increment.run(state)
|
FocusNext =>
|
||||||
},
|
{ state.focus.next(); },
|
||||||
TransportToolbarFocus::Sync => {
|
FocusPrev =>
|
||||||
return Self::Increment.run(state)
|
{ state.focus.prev(); },
|
||||||
},
|
PlayToggle =>
|
||||||
TransportToolbarFocus::PlayPause => {
|
{ state.toggle_play()?; },
|
||||||
/*todo seek*/
|
SeekUsec(usec) =>
|
||||||
},
|
{ state.clock.current.update_from_usec(usec); },
|
||||||
TransportToolbarFocus::Clock => {
|
SeekSample(sample) =>
|
||||||
/*todo seek*/
|
{ state.clock.current.update_from_sample(sample); },
|
||||||
},
|
SeekPulse(pulse) =>
|
||||||
}
|
{ state.clock.current.update_from_pulse(pulse); },
|
||||||
},
|
SetBpm(bpm) =>
|
||||||
Self::Decrement => {
|
{ return Ok(Some(Self::SetBpm(state.clock.timebase().bpm.set(bpm)))) },
|
||||||
match state.focus {
|
SetQuant(quant) =>
|
||||||
TransportToolbarFocus::Bpm => {
|
{ return Ok(Some(Self::SetQuant(state.clock.quant.set(quant)))) },
|
||||||
let bpm = state.clock.timebase().bpm.get();
|
SetSync(sync) =>
|
||||||
return Self::SetBpm(bpm - 1.0).run(state)
|
{ return Ok(Some(Self::SetSync(state.clock.sync.set(sync)))) },
|
||||||
},
|
_ => { unreachable!() }
|
||||||
TransportToolbarFocus::Quant => {
|
|
||||||
let quant = state.clock.quant.get() as usize;
|
|
||||||
let quant = prev_note_length(quant) as f64;
|
|
||||||
return Self::SetQuant(quant).run(state)
|
|
||||||
},
|
|
||||||
TransportToolbarFocus::Sync => {
|
|
||||||
let sync = state.clock.sync.get() as usize;
|
|
||||||
let sync = prev_note_length(sync) as f64;
|
|
||||||
return Self::SetSync(sync).run(state)
|
|
||||||
},
|
|
||||||
TransportToolbarFocus::PlayPause => {
|
|
||||||
/*todo seek*/
|
|
||||||
},
|
|
||||||
TransportToolbarFocus::Clock => {
|
|
||||||
/*todo seek*/
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Self::FineDecrement => {
|
|
||||||
match state.focus {
|
|
||||||
TransportToolbarFocus::Bpm => {
|
|
||||||
let bpm = state.clock.timebase().bpm.get();
|
|
||||||
return Self::SetBpm(bpm - 0.001).run(state)
|
|
||||||
},
|
|
||||||
TransportToolbarFocus::Quant => {
|
|
||||||
return Self::Decrement.run(state)
|
|
||||||
},
|
|
||||||
TransportToolbarFocus::Sync => {
|
|
||||||
return Self::Decrement.run(state)
|
|
||||||
},
|
|
||||||
TransportToolbarFocus::PlayPause => {
|
|
||||||
/*todo seek*/
|
|
||||||
},
|
|
||||||
TransportToolbarFocus::Clock => {
|
|
||||||
/*todo seek*/
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Self::SeekUsec(usec) => {
|
|
||||||
state.clock.current.update_from_usec(*usec);
|
|
||||||
},
|
|
||||||
Self::SeekSample(sample) => {
|
|
||||||
state.clock.current.update_from_sample(*sample);
|
|
||||||
},
|
|
||||||
Self::SeekPulse(pulse) => {
|
|
||||||
state.clock.current.update_from_pulse(*pulse);
|
|
||||||
},
|
|
||||||
Self::SetBpm(bpm) => {
|
|
||||||
let old_bpm = state.clock.timebase().bpm.get();
|
|
||||||
state.clock.timebase().bpm.set(*bpm);
|
|
||||||
return Ok(Some(Self::SetBpm(old_bpm)))
|
|
||||||
},
|
|
||||||
Self::SetQuant(quant) => {
|
|
||||||
let old_quant = state.clock.quant.get();
|
|
||||||
state.clock.quant.set(*quant);
|
|
||||||
return Ok(Some(Self::SetQuant(old_quant)))
|
|
||||||
},
|
|
||||||
Self::SetSync(sync) => {
|
|
||||||
let old_sync = state.clock.sync.get();
|
|
||||||
state.clock.sync.set(*sync);
|
|
||||||
return Ok(Some(Self::SetSync(old_sync)))
|
|
||||||
},
|
|
||||||
_ => { unimplemented!() }
|
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ impl TransportToolbarFocus {
|
||||||
impl Handle<Tui> for TransportToolbar<Tui> {
|
impl Handle<Tui> for TransportToolbar<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
if let Some(command) = TransportCommand::match_input(self, from) {
|
if let Some(command) = TransportCommand::match_input(self, from) {
|
||||||
let _undo = command.run(self)?;
|
let _undo = command.execute(self)?;
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue