FocusCommand, Command::translate, ret old from TimeUnit::set

This commit is contained in:
🪞👃🪞 2024-11-08 22:53:08 +01:00
parent 2b78339e61
commit fe25da4f53
9 changed files with 249 additions and 261 deletions

View file

@ -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>;

View file

@ -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);

View file

@ -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});

View file

@ -38,8 +38,8 @@ 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(); },
@ -48,17 +48,17 @@ impl <E: Engine> Command<Arranger<E>> for ArrangerCommand {
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!(),

View file

@ -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 {

View file

@ -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 {
Rename(Rename::Begin) => { state.begin_rename() },
Length(Length::Begin) => { state.begin_length() },
Prev => { state.select_prev() }, Prev => { state.select_prev() },
Next => { state.select_next() }, Next => { state.select_next() },
Delete => { state.delete_selected() }, Delete => { state.delete_selected() },
Append => { state.append_new(None, None) }, Append => { state.append_new(None, None) },
Insert => { state.insert_new(None, None) }, Insert => { state.insert_new(None, None) },
Duplicate => { state.insert_dup() }, Duplicate => { state.insert_dup() },
Edit => { todo!(); }
RandomColor => { state.randomize_color() }, RandomColor => { state.randomize_color() },
MoveUp => { state.move_up() }, MoveUp => { state.move_up() },
MoveDown => { state.move_down() }, MoveDown => { state.move_down() },
Rename(Rename::Begin) => { state.begin_rename() }, _ => unreachable!(),
Rename(_) => { unreachable!() },
Length(Length::Begin) => { state.begin_length() },
Length(_) => { unreachable!() },
Import => todo!(),
Export => todo!(),
} }
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::*;
match focus {
Bar => { *length += 4 * PPQ }, Bar => { *length += 4 * PPQ },
Beat => { *length += PPQ }, Beat => { *length += PPQ },
Tick => { *length += 1 }, Tick => { *length += 1 },
}
}, },
Dec => { Dec => match focus {
use PhraseLengthFocus::*;
match focus {
Bar => { *length = length.saturating_sub(4 * PPQ) }, Bar => { *length = length.saturating_sub(4 * PPQ) },
Beat => { *length = length.saturating_sub(PPQ) }, Beat => { *length = length.saturating_sub(PPQ) },
Tick => { *length = length.saturating_sub(1) }, 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)
} }

View file

@ -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)

View file

@ -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*/
}, },
_ => {}
};
return self
} }
}, fn execute (self, state: &mut TransportToolbar<E>) -> Perhaps<Self> {
Self::FineIncrement => { use TransportCommand::*;
match state.focus { match self.translate(&state) {
TransportToolbarFocus::Bpm => { FocusNext =>
let bpm = state.clock.timebase().bpm.get(); { state.focus.next(); },
return Self::SetBpm(bpm + 0.001).run(state) FocusPrev =>
}, { state.focus.prev(); },
TransportToolbarFocus::Quant => { PlayToggle =>
return Self::Increment.run(state) { state.toggle_play()?; },
}, SeekUsec(usec) =>
TransportToolbarFocus::Sync => { { state.clock.current.update_from_usec(usec); },
return Self::Increment.run(state) SeekSample(sample) =>
}, { state.clock.current.update_from_sample(sample); },
TransportToolbarFocus::PlayPause => { SeekPulse(pulse) =>
/*todo seek*/ { state.clock.current.update_from_pulse(pulse); },
}, SetBpm(bpm) =>
TransportToolbarFocus::Clock => { { return Ok(Some(Self::SetBpm(state.clock.timebase().bpm.set(bpm)))) },
/*todo seek*/ SetQuant(quant) =>
}, { return Ok(Some(Self::SetQuant(state.clock.quant.set(quant)))) },
} SetSync(sync) =>
}, { return Ok(Some(Self::SetSync(state.clock.sync.set(sync)))) },
Self::Decrement => { _ => { unreachable!() }
match state.focus {
TransportToolbarFocus::Bpm => {
let bpm = state.clock.timebase().bpm.get();
return Self::SetBpm(bpm - 1.0).run(state)
},
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)
} }

View file

@ -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)