tek/crates/tek/src/tui/ctrl_transport.rs
2024-12-09 17:31:31 +01:00

134 lines
5 KiB
Rust

use crate::*;
impl Handle<Tui> for TransportTui {
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
TransportCommand::execute_with_state(self, from)
}
}
#[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(cmd) => cmd.execute(state)?.map(Clock),
//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)
})
}
}
pub trait TransportControl: HasClock + FocusGrid + HasEnter {
fn transport_focused (&self) -> Option<TransportFocus>;
}
impl TransportControl for TransportTui {
fn transport_focused (&self) -> Option<TransportFocus> {
Some(self.focus.inner())
}
}
impl TransportControl for SequencerTui {
fn transport_focused (&self) -> Option<TransportFocus> {
match self.focus.inner() {
SequencerFocus::Transport(focus) => Some(focus),
_ => None
}
}
}
impl TransportControl for ArrangerTui {
fn transport_focused (&self) -> Option<TransportFocus> {
match self.focus.inner() {
ArrangerFocus::Transport(focus) => Some(focus),
_ => None
}
}
}
impl<T: TransportControl> InputToCommand<Tui, T> for TransportCommand {
fn input_to_command (state: &T, input: &TuiInput) -> Option<Self> {
to_transport_command(state, input)
.or_else(||to_focus_command(input).map(TransportCommand::Focus))
}
}
pub fn to_transport_command <T> (state: &T, input: &TuiInput) -> Option<TransportCommand>
where
T: TransportControl
{
use ClockCommand::{SetBpm, SetQuant, SetSync};
use TransportCommand::{Focus, Clock};
use KeyCode::{Enter, Left, Right, Char};
Some(match input.event() {
key!(Left) => Focus(FocusCommand::Prev),
key!(Right) => Focus(FocusCommand::Next),
key!(Char(' ')) => Clock(if state.clock().is_stopped() {
ClockCommand::Play(None)
} else {
ClockCommand::Pause(None)
}),
key!(Shift-Char(' ')) => Clock(if state.clock().is_stopped() {
ClockCommand::Play(Some(0))
} else {
ClockCommand::Pause(Some(0))
}),
_ => match state.transport_focused().unwrap() {
TransportFocus::Bpm => match input.event() {
key!(Char(',')) => Clock(SetBpm(state.clock().bpm().get() - 1.0)),
key!(Char('.')) => Clock(SetBpm(state.clock().bpm().get() + 1.0)),
key!(Char('<')) => Clock(SetBpm(state.clock().bpm().get() - 0.001)),
key!(Char('>')) => Clock(SetBpm(state.clock().bpm().get() + 0.001)),
_ => return None,
},
TransportFocus::Quant => match input.event() {
key!(Char(',')) => Clock(SetQuant(state.clock().quant.prev())),
key!(Char('.')) => Clock(SetQuant(state.clock().quant.next())),
key!(Char('<')) => Clock(SetQuant(state.clock().quant.prev())),
key!(Char('>')) => Clock(SetQuant(state.clock().quant.next())),
_ => return None,
},
TransportFocus::Sync => match input.event() {
key!(Char(',')) => Clock(SetSync(state.clock().sync.prev())),
key!(Char('.')) => Clock(SetSync(state.clock().sync.next())),
key!(Char('<')) => Clock(SetSync(state.clock().sync.prev())),
key!(Char('>')) => Clock(SetSync(state.clock().sync.next())),
_ => return None,
},
TransportFocus::Clock => match input.event() {
key!(Char(',')) => todo!("transport seek bar"),
key!(Char('.')) => todo!("transport seek bar"),
key!(Char('<')) => todo!("transport seek beat"),
key!(Char('>')) => todo!("transport seek beat"),
_ => return None,
},
TransportFocus::PlayPause => match input.event() {
key!(Enter) => Clock(
if state.clock().is_stopped() {
ClockCommand::Play(None)
} else {
ClockCommand::Pause(None)
}
),
key!(Shift-Enter) => Clock(
if state.clock().is_stopped() {
ClockCommand::Play(Some(0))
} else {
ClockCommand::Pause(Some(0))
}
),
_ => return None,
},
}
})
}