use crate::*; use super::*; use PhraseLengthFocus::*; use PhraseLengthCommand::*; use KeyCode::{Up, Down, Left, Right, Enter, Esc}; /// Displays and edits phrase length. #[derive(Clone)] pub struct PhraseLength { /// Pulses per beat (quaver) pub ppq: usize, /// Beats per bar pub bpb: usize, /// Length of phrase in pulses pub pulses: usize, /// Selected subdivision pub focus: Option, } impl PhraseLength { pub fn new (pulses: usize, focus: Option) -> Self { Self { ppq: PPQ, bpb: 4, pulses, focus } } pub fn bars (&self) -> usize { self.pulses / (self.bpb * self.ppq) } pub fn beats (&self) -> usize { (self.pulses % (self.bpb * self.ppq)) / self.ppq } pub fn ticks (&self) -> usize { self.pulses % self.ppq } pub fn bars_string (&self) -> String { format!("{}", self.bars()) } pub fn beats_string (&self) -> String { format!("{}", self.beats()) } pub fn ticks_string (&self) -> String { format!("{:>02}", self.ticks()) } } /// Focused field of `PhraseLength` #[derive(Copy, Clone, Debug)] pub enum PhraseLengthFocus { /// Editing the number of bars Bar, /// Editing the number of beats Beat, /// Editing the number of ticks Tick, } impl PhraseLengthFocus { pub fn next (&mut self) { *self = match self { Self::Bar => Self::Beat, Self::Beat => Self::Tick, Self::Tick => Self::Bar, } } pub fn prev (&mut self) { *self = match self { Self::Bar => Self::Tick, Self::Beat => Self::Bar, Self::Tick => Self::Beat, } } } render!(Tui: (self: PhraseLength) => { let bars = ||self.bars_string(); let beats = ||self.beats_string(); let ticks = ||self.ticks_string(); match self.focus { None => row!(" ", bars(), ".", beats(), ".", ticks()), Some(PhraseLengthFocus::Bar) => row!("[", bars(), "]", beats(), ".", ticks()), Some(PhraseLengthFocus::Beat) => row!(" ", bars(), "[", beats(), "]", ticks()), Some(PhraseLengthFocus::Tick) => row!(" ", bars(), ".", beats(), "[", ticks()), } }); #[derive(Copy, Clone, Debug, PartialEq)] pub enum PhraseLengthCommand { Begin, Cancel, Set(usize), Next, Prev, Inc, Dec, } command!(|self:PhraseLengthCommand,state:PoolModel|{ match state.phrases_mode_mut().clone() { Some(PoolMode::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!() }; None }); input_to_command!(PhraseLengthCommand: |state: PoolModel, input: Event|{ if let Some(PoolMode::Length(_, length, _)) = state.phrases_mode() { match input { key_pat!(Up) => Self::Inc, key_pat!(Down) => Self::Dec, key_pat!(Right) => Self::Next, key_pat!(Left) => Self::Prev, key_pat!(Enter) => Self::Set(*length), key_pat!(Esc) => Self::Cancel, _ => return None } } else { unreachable!() } });