tek/crates/tek_tui/src/tui_pool.rs

190 lines
5.6 KiB
Rust

use crate::*;
pub struct PhrasesTui {
/// Collection of phrases
pub phrases: Vec<Arc<RwLock<Phrase>>>,
/// Selected phrase
pub phrase: usize,
/// Scroll offset
pub scroll: usize,
/// Mode switch
pub mode: Option<PhrasesMode>,
/// Whether this widget is focused
pub focused: bool,
/// Whether this widget is entered
pub entered: bool,
}
/// Modes for phrase pool
pub enum PhrasesMode {
/// Renaming a pattern
Rename(usize, String),
/// Editing the length of a pattern
Length(usize, usize, PhraseLengthFocus),
}
impl PhrasesTui {
pub fn new (phrases: Vec<Arc<RwLock<Phrase>>>) -> Self {
Self {
scroll: 0,
phrase: 0,
mode: None,
focused: false,
entered: false,
phrases,
}
}
pub fn len (&self) -> usize {
self.phrases.len()
}
pub fn phrase (&self) -> &Arc<RwLock<Phrase>> {
&self.phrases[self.phrase]
}
pub fn index_before (&self, index: usize) -> usize {
index.overflowing_sub(1).0.min(self.len() - 1)
}
pub fn index_after (&self, index: usize) -> usize {
(index + 1) % self.len()
}
pub fn index_of (&self, phrase: &Phrase) -> Option<usize> {
for i in 0..self.phrases.len() {
if *self.phrases[i].read().unwrap() == *phrase { return Some(i) }
}
return None
}
fn new_phrase (name: Option<&str>, color: Option<ItemColorTriplet>) -> Arc<RwLock<Phrase>> {
Arc::new(RwLock::new(Phrase::new(
String::from(name.unwrap_or("(new)")), true, 4 * PPQ, None, color
)))
}
pub fn delete_selected (&mut self) {
if self.phrase > 0 {
self.phrases.remove(self.phrase);
self.phrase = self.phrase.min(self.phrases.len().saturating_sub(1));
}
}
pub fn append_new (&mut self, name: Option<&str>, color: Option<ItemColorTriplet>) {
self.phrases.push(Self::new_phrase(name, color));
self.phrase = self.phrases.len() - 1;
}
pub fn insert_new (&mut self, name: Option<&str>, color: Option<ItemColorTriplet>) {
self.phrases.insert(self.phrase + 1, Self::new_phrase(name, color));
self.phrase += 1;
}
pub fn insert_dup (&mut self) {
let mut phrase = self.phrases[self.phrase].read().unwrap().duplicate();
phrase.color = ItemColorTriplet::random_near(phrase.color, 0.25);
self.phrases.insert(self.phrase + 1, Arc::new(RwLock::new(phrase)));
self.phrase += 1;
}
pub fn move_up (&mut self) {
if self.phrase > 1 {
self.phrases.swap(self.phrase - 1, self.phrase);
self.phrase -= 1;
}
}
pub fn move_down (&mut self) {
if self.phrase < self.phrases.len().saturating_sub(1) {
self.phrases.swap(self.phrase + 1, self.phrase);
self.phrase += 1;
}
}
}
/// Displays and edits phrase length.
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 { _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 {
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,
}
}
}