mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-01-13 03:26:41 +01:00
wip: p.44, e=135, removing E generic
This commit is contained in:
parent
a7998860b1
commit
260736f31d
20 changed files with 848 additions and 838 deletions
|
|
@ -1,7 +1,6 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct PhrasePoolView<E: Engine> {
|
||||
_engine: PhantomData<E>,
|
||||
pub struct PhrasesTui {
|
||||
/// Collection of phrases
|
||||
pub phrases: Vec<Arc<RwLock<Phrase>>>,
|
||||
/// Selected phrase
|
||||
|
|
@ -24,10 +23,9 @@ pub enum PhrasePoolMode {
|
|||
Length(usize, usize, PhraseLengthFocus),
|
||||
}
|
||||
|
||||
impl<E: Engine> PhrasePoolView<E> {
|
||||
impl PhrasesTui {
|
||||
pub fn new (phrases: Vec<Arc<RwLock<Phrase>>>) -> Self {
|
||||
Self {
|
||||
_engine: Default::default(),
|
||||
scroll: 0,
|
||||
phrase: 0,
|
||||
mode: None,
|
||||
|
|
@ -93,210 +91,8 @@ impl<E: Engine> PhrasePoolView<E> {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Display phrases always in order of appearance
|
||||
impl Content for PhrasePoolView<Tui> {
|
||||
type Engine = Tui;
|
||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||
let Self { focused, phrases, mode, .. } = self;
|
||||
let content = col!(
|
||||
(i, phrase) in phrases.iter().enumerate() => Layers::new(|add|{
|
||||
let Phrase { ref name, color, length, .. } = *phrase.read().unwrap();
|
||||
let mut length = PhraseLength::new(length, None);
|
||||
if let Some(PhrasePoolMode::Length(phrase, new_length, focus)) = mode {
|
||||
if *focused && i == *phrase {
|
||||
length.pulses = *new_length;
|
||||
length.focus = Some(*focus);
|
||||
}
|
||||
}
|
||||
let length = length.align_e().fill_x();
|
||||
let row1 = lay!(format!(" {i}").align_w().fill_x(), length).fill_x();
|
||||
let mut row2 = format!(" {name}");
|
||||
if let Some(PhrasePoolMode::Rename(phrase, _)) = mode {
|
||||
if *focused && i == *phrase { row2 = format!("{row2}▄"); }
|
||||
};
|
||||
let row2 = TuiStyle::bold(row2, true);
|
||||
add(&col!(row1, row2).fill_x().bg(color.base.rgb))?;
|
||||
Ok(if *focused && i == self.phrase { add(&CORNERS)?; })
|
||||
})
|
||||
);
|
||||
let border_color = if *focused {Color::Rgb(100, 110, 40)} else {Color::Rgb(70, 80, 50)};
|
||||
let border = Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color));
|
||||
let content = content.fill_xy().bg(Color::Rgb(28, 35, 25)).border(border);
|
||||
let title_color = if *focused {Color::Rgb(150, 160, 90)} else {Color::Rgb(120, 130, 100)};
|
||||
let upper_left = format!("[{}] Phrases", if self.entered {"■"} else {" "});
|
||||
let upper_right = format!("({})", phrases.len());
|
||||
lay!(
|
||||
content,
|
||||
TuiStyle::fg(upper_left.to_string(), title_color).push_x(1).align_nw().fill_xy(),
|
||||
TuiStyle::fg(upper_right.to_string(), title_color).pull_x(1).align_ne().fill_xy(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum PhrasePoolViewCommand {
|
||||
Select(usize),
|
||||
Edit(PhrasePoolCommand),
|
||||
Rename(PhraseRenameCommand),
|
||||
Length(PhraseLengthCommand),
|
||||
}
|
||||
|
||||
impl Handle<Tui> for PhrasePoolView<Tui> {
|
||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||
PhrasePoolViewCommand::execute_with_state(self, from)
|
||||
}
|
||||
}
|
||||
|
||||
impl InputToCommand<Tui, PhrasePoolView<Tui>> for PhrasePoolViewCommand {
|
||||
fn input_to_command (state: &PhrasePoolView<Tui>, input: &TuiInput) -> Option<Self> {
|
||||
use PhrasePoolViewCommand as Cmd;
|
||||
use PhrasePoolCommand as Edit;
|
||||
use PhraseRenameCommand as Rename;
|
||||
use PhraseLengthCommand as Length;
|
||||
match input.event() {
|
||||
key!(KeyCode::Up) => Some(Cmd::Select(0)),
|
||||
key!(KeyCode::Down) => Some(Cmd::Select(0)),
|
||||
key!(KeyCode::Char(',')) => Some(Cmd::Edit(Edit::Swap(0, 0))),
|
||||
key!(KeyCode::Char('.')) => Some(Cmd::Edit(Edit::Swap(0, 0))),
|
||||
key!(KeyCode::Delete) => Some(Cmd::Edit(Edit::Delete(0))),
|
||||
key!(KeyCode::Char('a')) => Some(Cmd::Edit(Edit::Add(0))),
|
||||
key!(KeyCode::Char('i')) => Some(Cmd::Edit(Edit::Add(0))),
|
||||
key!(KeyCode::Char('d')) => Some(Cmd::Edit(Edit::Duplicate(0))),
|
||||
key!(KeyCode::Char('c')) => Some(Cmd::Edit(Edit::RandomColor(0))),
|
||||
key!(KeyCode::Char('n')) => Some(Cmd::Rename(Rename::Begin)),
|
||||
key!(KeyCode::Char('t')) => Some(Cmd::Length(Length::Begin)),
|
||||
_ => match state.mode {
|
||||
Some(PhrasePoolMode::Rename(..)) => {
|
||||
Rename::input_to_command(state, input).map(Cmd::Rename)
|
||||
},
|
||||
Some(PhrasePoolMode::Length(..)) => {
|
||||
Length::input_to_command(state, input).map(Cmd::Length)
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Command<PhrasePoolView<E>> for PhrasePoolViewCommand {
|
||||
fn execute (self, view: &mut PhrasePoolView<E>) -> Perhaps<Self> {
|
||||
use PhraseRenameCommand as Rename;
|
||||
use PhraseLengthCommand as Length;
|
||||
match self {
|
||||
Self::Select(phrase) => {
|
||||
view.phrase = phrase
|
||||
},
|
||||
Self::Edit(command) => {
|
||||
return Ok(command.execute(&mut view)?.map(Self::Edit))
|
||||
}
|
||||
Self::Rename(command) => match command {
|
||||
Rename::Begin => {
|
||||
view.mode = Some(PhrasePoolMode::Rename(
|
||||
view.phrase,
|
||||
view.phrases[view.phrase].read().unwrap().name.clone()
|
||||
))
|
||||
},
|
||||
_ => {
|
||||
return Ok(command.execute(view)?.map(Self::Rename))
|
||||
}
|
||||
},
|
||||
Self::Length(command) => match command {
|
||||
Length::Begin => {
|
||||
view.mode = Some(PhrasePoolMode::Length(
|
||||
view.phrase,
|
||||
view.phrases[view.phrase].read().unwrap().length,
|
||||
PhraseLengthFocus::Bar
|
||||
))
|
||||
},
|
||||
_ => {
|
||||
return Ok(command.execute(view)?.map(Self::Length))
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum PhraseLengthCommand {
|
||||
Begin,
|
||||
Next,
|
||||
Prev,
|
||||
Inc,
|
||||
Dec,
|
||||
Set(usize),
|
||||
Cancel,
|
||||
}
|
||||
|
||||
impl InputToCommand<Tui, PhrasePoolView<Tui>> for PhraseLengthCommand {
|
||||
fn input_to_command (view: &PhrasePoolView<Tui>, from: &TuiInput) -> Option<Self> {
|
||||
if let Some(PhrasePoolMode::Length(_, length, _)) = view.mode {
|
||||
Some(match from.event() {
|
||||
key!(KeyCode::Up) => Self::Inc,
|
||||
key!(KeyCode::Down) => Self::Dec,
|
||||
key!(KeyCode::Right) => Self::Next,
|
||||
key!(KeyCode::Left) => Self::Prev,
|
||||
key!(KeyCode::Enter) => Self::Set(length),
|
||||
key!(KeyCode::Esc) => Self::Cancel,
|
||||
_ => return None
|
||||
})
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Command<PhrasePoolView<E>> for PhraseLengthCommand {
|
||||
fn execute (self, view: &mut PhrasePoolView<E>) -> Perhaps<Self> {
|
||||
use PhraseLengthFocus::*;
|
||||
use PhraseLengthCommand::*;
|
||||
if let Some(PhrasePoolMode::Length(phrase, ref mut length, ref mut focus)) = view.mode {
|
||||
match self {
|
||||
Self::Cancel => {
|
||||
view.mode = None;
|
||||
},
|
||||
Self::Prev => {
|
||||
focus.prev()
|
||||
},
|
||||
Self::Next => {
|
||||
focus.next()
|
||||
},
|
||||
Self::Inc => match focus {
|
||||
Bar => { *length += 4 * PPQ },
|
||||
Beat => { *length += PPQ },
|
||||
Tick => { *length += 1 },
|
||||
},
|
||||
Self::Dec => match focus {
|
||||
Bar => { *length = length.saturating_sub(4 * PPQ) },
|
||||
Beat => { *length = length.saturating_sub(PPQ) },
|
||||
Tick => { *length = length.saturating_sub(1) },
|
||||
},
|
||||
Self::Set(length) => {
|
||||
let mut phrase = view.phrases[phrase].write().unwrap();
|
||||
let old_length = phrase.length;
|
||||
phrase.length = length;
|
||||
view.mode = None;
|
||||
return Ok(Some(Self::Set(old_length)))
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
Ok(None)
|
||||
} else if self == Begin {
|
||||
view.mode = Some(PhrasePoolMode::Length(
|
||||
view.phrase,
|
||||
view.phrases[view.phrase].read().unwrap().length,
|
||||
PhraseLengthFocus::Bar
|
||||
));
|
||||
Ok(None)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Displays and edits phrase length.
|
||||
pub struct PhraseLength<E: Engine> {
|
||||
_engine: PhantomData<E>,
|
||||
pub struct PhraseLength {
|
||||
/// Pulses per beat (quaver)
|
||||
pub ppq: usize,
|
||||
/// Beats per bar
|
||||
|
|
@ -331,7 +127,7 @@ impl<E: Engine> PhraseLength<E> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Content for PhraseLength<Tui> {
|
||||
impl Content for PhraseLength {
|
||||
type Engine = Tui;
|
||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||
Layers::new(move|add|{
|
||||
|
|
@ -392,68 +188,3 @@ impl PhraseLengthFocus {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PhraseRenameCommand {
|
||||
Begin,
|
||||
Set(String),
|
||||
Confirm,
|
||||
Cancel,
|
||||
}
|
||||
|
||||
impl InputToCommand<Tui, PhrasePoolView<Tui>> for PhraseRenameCommand {
|
||||
fn input_to_command (view: &PhrasePoolView<Tui>, from: &TuiInput) -> Option<Self> {
|
||||
if let Some(PhrasePoolMode::Rename(_, ref old_name)) = view.mode {
|
||||
Some(match from.event() {
|
||||
key!(KeyCode::Char(c)) => {
|
||||
let mut new_name = old_name.clone();
|
||||
new_name.push(*c);
|
||||
Self::Set(new_name)
|
||||
},
|
||||
key!(KeyCode::Backspace) => {
|
||||
let mut new_name = old_name.clone();
|
||||
new_name.pop();
|
||||
Self::Set(new_name)
|
||||
},
|
||||
key!(KeyCode::Enter) => Self::Confirm,
|
||||
key!(KeyCode::Esc) => Self::Cancel,
|
||||
_ => return None
|
||||
})
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Command<PhrasePoolView<E>> for PhraseRenameCommand {
|
||||
fn execute (self, view: &mut PhrasePoolView<E>) -> Perhaps<Self> {
|
||||
use PhraseRenameCommand::*;
|
||||
if let Some(PhrasePoolMode::Rename(phrase, ref mut old_name)) = view.mode {
|
||||
match self {
|
||||
Set(s) => {
|
||||
view.phrases[phrase].write().unwrap().name = s.into();
|
||||
return Ok(Some(Self::Set(old_name.clone())))
|
||||
},
|
||||
Confirm => {
|
||||
let old_name = old_name.clone();
|
||||
view.mode = None;
|
||||
return Ok(Some(Self::Set(old_name)))
|
||||
},
|
||||
Cancel => {
|
||||
let mut phrase = view.phrases[phrase].write().unwrap();
|
||||
phrase.name = old_name.clone();
|
||||
},
|
||||
_ => unreachable!()
|
||||
};
|
||||
Ok(None)
|
||||
} else if self == Begin {
|
||||
view.mode = Some(PhrasePoolMode::Rename(
|
||||
view.phrase,
|
||||
view.phrases[view.phrase].read().unwrap().name.clone()
|
||||
));
|
||||
Ok(None)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue