mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-09 21:26:43 +01:00
wip: refactor pt.9, 403 errors
This commit is contained in:
parent
a784f7a6f2
commit
8aa1ba8d0f
29 changed files with 1008 additions and 902 deletions
176
crates/tek_tui/src/tui_pool_length.rs
Normal file
176
crates/tek_tui/src/tui_pool_length.rs
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
use crate::*;
|
||||
|
||||
/// Displays and edits phrase length.
|
||||
pub struct PhraseLength<E: Engine> {
|
||||
_engine: PhantomData<E>,
|
||||
/// 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<E: Engine> PhraseLength<E> {
|
||||
pub fn new (pulses: usize, focus: Option<PhraseLengthFocus>) -> Self {
|
||||
Self { _engine: Default::default(), 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())
|
||||
}
|
||||
}
|
||||
|
||||
impl Content for PhraseLength<Tui> {
|
||||
type Engine = Tui;
|
||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||
Layers::new(move|add|{
|
||||
match self.focus {
|
||||
None => add(&row!(
|
||||
" ", self.bars_string(),
|
||||
".", self.beats_string(),
|
||||
".", self.ticks_string(),
|
||||
" "
|
||||
)),
|
||||
Some(PhraseLengthFocus::Bar) => add(&row!(
|
||||
"[", self.bars_string(),
|
||||
"]", self.beats_string(),
|
||||
".", self.ticks_string(),
|
||||
" "
|
||||
)),
|
||||
Some(PhraseLengthFocus::Beat) => add(&row!(
|
||||
" ", self.bars_string(),
|
||||
"[", self.beats_string(),
|
||||
"]", self.ticks_string(),
|
||||
" "
|
||||
)),
|
||||
Some(PhraseLengthFocus::Tick) => add(&row!(
|
||||
" ", self.bars_string(),
|
||||
".", self.beats_string(),
|
||||
"[", self.ticks_string(),
|
||||
"]"
|
||||
)),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Focused field of `PhraseLength`
|
||||
#[derive(Copy, Clone)]
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum PhraseLengthCommand {
|
||||
Begin,
|
||||
Next,
|
||||
Prev,
|
||||
Inc,
|
||||
Dec,
|
||||
Set(usize),
|
||||
Confirm,
|
||||
Cancel,
|
||||
}
|
||||
|
||||
impl InputToCommand<Tui, PhrasePoolView<Tui>> for PhraseLengthCommand {
|
||||
fn input_to_command (_: &PhrasePoolView<Tui>, from: &TuiInput) -> Option<Self> {
|
||||
match from.event() {
|
||||
key!(KeyCode::Up) => Some(Self::Inc),
|
||||
key!(KeyCode::Down) => Some(Self::Dec),
|
||||
key!(KeyCode::Right) => Some(Self::Next),
|
||||
key!(KeyCode::Left) => Some(Self::Prev),
|
||||
key!(KeyCode::Enter) => Some(Self::Confirm),
|
||||
key!(KeyCode::Esc) => Some(Self::Cancel),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Command<PhrasePoolView<E>> for PhraseLengthCommand {
|
||||
fn translate (self, state: &PhrasePoolView<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 PhrasePoolView<E>) -> Perhaps<Self> {
|
||||
use PhraseLengthFocus::*;
|
||||
use PhraseLengthCommand::*;
|
||||
if let Some(PhrasePoolMode::Length(phrase, ref mut length, ref mut focus)) = state.mode {
|
||||
match self {
|
||||
Cancel => { state.mode = 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;
|
||||
state.mode = None;
|
||||
return Ok(Some(Self::Set(old_length)))
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
Ok(None)
|
||||
} else if self == Begin {
|
||||
todo!()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue