mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 03:36:41 +01:00
144 lines
4.1 KiB
Rust
144 lines
4.1 KiB
Rust
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<PhraseLengthFocus>,
|
|
}
|
|
|
|
impl PhraseLength {
|
|
pub fn new (pulses: usize, focus: Option<PhraseLengthFocus>) -> 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!()
|
|
}
|
|
});
|