PhraseList -> Pool

This commit is contained in:
🪞👃🪞 2024-12-25 02:10:44 +01:00
parent ac0ee26b7c
commit 85cfb43e82
7 changed files with 52 additions and 54 deletions

View file

@ -41,7 +41,7 @@ mod phrase_editor; pub(crate) use phrase_editor::*;
mod piano_horizontal; pub(crate) use piano_horizontal::*;
mod phrase_length; pub(crate) use phrase_length::*;
mod phrase_rename; pub(crate) use phrase_rename::*;
mod phrase_list; pub(crate) use phrase_list::*;
mod pool; pub(crate) use pool::*;
mod port_select;
////////////////////////////////////////////////////////

View file

@ -3,7 +3,7 @@ use crate::*;
pub struct ArrangerTui {
jack: Arc<RwLock<JackClient>>,
pub clock: ClockModel,
pub phrases: PhraseListModel,
pub phrases: PoolModel,
pub tracks: Vec<ArrangerTrack>,
pub scenes: Vec<ArrangerScene>,
pub splits: [u16;2],
@ -97,7 +97,7 @@ render!(<Tui>|self: ArrangerTui|{
let transport = TransportView::from((self, Some(ItemPalette::from(TuiTheme::g(96))), true));
let with_transport = |x|col!([row!(![&play, &transport]), &x]);
let pool_size = if self.show_pool { self.splits[1] } else { 0 };
let with_pool = |x|Split::left(false, pool_size, PhraseListView(&self.phrases), x);
let with_pool = |x|Split::left(false, pool_size, PoolView(&self.phrases), x);
let status = ArrangerStatus::from(self);
let with_editbar = |x|Tui::split_n(false, 3, PhraseEditStatus(&self.editor), x);
let with_status = |x|Tui::split_n(false, 2, status, x);

View file

@ -8,7 +8,7 @@ use PhrasePoolCommand::*;
pub struct SequencerTui {
_jack: Arc<RwLock<JackClient>>,
pub(crate) clock: ClockModel,
pub(crate) phrases: PhraseListModel,
pub(crate) phrases: PoolModel,
pub(crate) player: PhrasePlayerModel,
pub(crate) editor: PhraseEditorModel,
pub(crate) size: Measure<Tui>,
@ -26,7 +26,7 @@ from_jack!(|jack|SequencerTui {
)));
Self {
_jack: jack.clone(),
phrases: PhraseListModel::from(&phrase),
phrases: PoolModel::from(&phrase),
editor: PhraseEditorModel::from(&phrase),
player: PhrasePlayerModel::from((&clock, &phrase)),
size: Measure::new(),
@ -42,7 +42,7 @@ render!(<Tui>|self: SequencerTui|{
let w = self.size.w();
let phrase_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
let pool_w = if self.show_pool { phrase_w } else { 0 };
let pool = Fill::h(Align::e(PhraseListView(&self.phrases)));
let pool = Fill::h(Align::e(PoolView(&self.phrases)));
let with_pool = move|x|Tui::split_w(false, pool_w, pool, x);
let status = SequencerStatus::from(self);
let with_status = |x|Tui::split_n(false, if self.status { 2 } else { 0 }, status, x);

View file

@ -47,10 +47,8 @@ pub enum ArrangerClipCommand {
}
input_to_command!(ArrangerCommand: <Tui>|state: ArrangerTui, input|match input.event() {
// TODO: u: undo
key_pat!(Char('u')) => { todo!("undo") },
// TODO: Shift-U: redo
key_pat!(Char('U')) => { todo!("redo") },
key_pat!(Char('u')) => Self::History(-1),
key_pat!(Char('U')) => Self::History(1),
// TODO: k: toggle on-screen keyboard
key_pat!(Ctrl-Char('k')) => { todo!("keyboard") },
// Transport: Play/pause

View file

@ -1,5 +1,5 @@
use crate::*;
use super::phrase_list::{PhraseListModel, PhraseListMode};
use super::pool::{PoolModel, PoolMode};
use PhraseLengthFocus::*;
use PhraseLengthCommand::*;
/// Displays and edits phrase length.
@ -88,9 +88,9 @@ pub enum PhraseLengthCommand {
Inc,
Dec,
}
command!(|self:PhraseLengthCommand,state:PhraseListModel|{
command!(|self:PhraseLengthCommand,state:PoolModel|{
match state.phrases_mode_mut().clone() {
Some(PhraseListMode::Length(phrase, ref mut length, ref mut focus)) => match self {
Some(PoolMode::Length(phrase, ref mut length, ref mut focus)) => match self {
Cancel => { *state.phrases_mode_mut() = None; },
Prev => { focus.prev() },
Next => { focus.next() },
@ -118,8 +118,8 @@ command!(|self:PhraseLengthCommand,state:PhraseListModel|{
};
None
});
input_to_command!(PhraseLengthCommand:<Tui>|state:PhraseListModel,from|{
if let Some(PhraseListMode::Length(_, length, _)) = state.phrases_mode() {
input_to_command!(PhraseLengthCommand:<Tui>|state:PoolModel,from|{
if let Some(PoolMode::Length(_, length, _)) = state.phrases_mode() {
match from.event() {
key_pat!(Up) => Self::Inc,
key_pat!(Down) => Self::Dec,

View file

@ -8,11 +8,11 @@ pub enum PhraseRenameCommand {
Set(String),
}
impl Command<PhraseListModel> for PhraseRenameCommand {
fn execute (self, state: &mut PhraseListModel) -> Perhaps<Self> {
impl Command<PoolModel> for PhraseRenameCommand {
fn execute (self, state: &mut PoolModel) -> Perhaps<Self> {
use PhraseRenameCommand::*;
match state.phrases_mode_mut().clone() {
Some(PhraseListMode::Rename(phrase, ref mut old_name)) => match self {
Some(PoolMode::Rename(phrase, ref mut old_name)) => match self {
Set(s) => {
state.phrases()[phrase].write().unwrap().name = s.into();
return Ok(Some(Self::Set(old_name.clone())))
@ -33,10 +33,10 @@ impl Command<PhraseListModel> for PhraseRenameCommand {
}
}
impl InputToCommand<Tui, PhraseListModel> for PhraseRenameCommand {
fn input_to_command (state: &PhraseListModel, from: &TuiInput) -> Option<Self> {
impl InputToCommand<Tui, PoolModel> for PhraseRenameCommand {
fn input_to_command (state: &PoolModel, from: &TuiInput) -> Option<Self> {
use KeyCode::{Char, Backspace, Enter, Esc};
if let Some(PhraseListMode::Rename(_, ref old_name)) = state.phrases_mode() {
if let Some(PoolMode::Rename(_, ref old_name)) = state.phrases_mode() {
Some(match from.event() {
key_pat!(Char(c)) => {
let mut new_name = old_name.clone();

View file

@ -7,14 +7,14 @@ use crate::{
};
#[derive(Debug)]
pub struct PhraseListModel {
pub struct PoolModel {
pub(crate) visible: bool,
/// Collection of phrases
pub(crate) phrases: Vec<Arc<RwLock<Phrase>>>,
/// Selected phrase
pub(crate) phrase: AtomicUsize,
/// Mode switch
pub(crate) mode: Option<PhraseListMode>,
pub(crate) mode: Option<PoolMode>,
/// Rendered size
size: Measure<Tui>,
/// Scroll offset
@ -23,7 +23,7 @@ pub struct PhraseListModel {
/// Modes for phrase pool
#[derive(Debug, Clone)]
pub enum PhraseListMode {
pub enum PoolMode {
/// Renaming a pattern
Rename(usize, String),
/// Editing the length of a pattern
@ -51,7 +51,7 @@ pub enum PhrasesCommand {
Export(Browse),
}
command!(|self:PhrasesCommand, state:PhraseListModel|{
command!(|self:PhrasesCommand, state: PoolModel|{
use PhrasesCommand::*;
match self {
Show(visible) => {
@ -62,7 +62,7 @@ command!(|self:PhrasesCommand, state:PhraseListModel|{
PhraseRenameCommand::Begin => {
let length = state.phrases()[state.phrase_index()].read().unwrap().length;
*state.phrases_mode_mut() = Some(
PhraseListMode::Length(state.phrase_index(), length, PhraseLengthFocus::Bar)
PoolMode::Length(state.phrase_index(), length, PhraseLengthFocus::Bar)
);
None
},
@ -72,7 +72,7 @@ command!(|self:PhrasesCommand, state:PhraseListModel|{
PhraseLengthCommand::Begin => {
let name = state.phrases()[state.phrase_index()].read().unwrap().name.clone();
*state.phrases_mode_mut() = Some(
PhraseListMode::Rename(state.phrase_index(), name)
PoolMode::Rename(state.phrase_index(), name)
);
None
},
@ -81,7 +81,7 @@ command!(|self:PhrasesCommand, state:PhraseListModel|{
Import(command) => match command {
FileBrowserCommand::Begin => {
*state.phrases_mode_mut() = Some(
PhraseListMode::Import(state.phrase_index(), FileBrowser::new(None)?)
PoolMode::Import(state.phrase_index(), FileBrowser::new(None)?)
);
None
},
@ -90,7 +90,7 @@ command!(|self:PhrasesCommand, state:PhraseListModel|{
Export(command) => match command {
FileBrowserCommand::Begin => {
*state.phrases_mode_mut() = Some(
PhraseListMode::Export(state.phrase_index(), FileBrowser::new(None)?)
PoolMode::Export(state.phrase_index(), FileBrowser::new(None)?)
);
None
},
@ -104,15 +104,15 @@ command!(|self:PhrasesCommand, state:PhraseListModel|{
}
});
input_to_command!(PhrasesCommand:<Tui>|state:PhraseListModel,input|match state.phrases_mode() {
Some(PhraseListMode::Rename(..)) => Self::Rename(Rename::input_to_command(state, input)?),
Some(PhraseListMode::Length(..)) => Self::Length(Length::input_to_command(state, input)?),
Some(PhraseListMode::Import(..)) => Self::Import(Browse::input_to_command(state, input)?),
Some(PhraseListMode::Export(..)) => Self::Export(Browse::input_to_command(state, input)?),
input_to_command!(PhrasesCommand:<Tui>|state: PoolModel,input|match state.phrases_mode() {
Some(PoolMode::Rename(..)) => Self::Rename(Rename::input_to_command(state, input)?),
Some(PoolMode::Length(..)) => Self::Length(Length::input_to_command(state, input)?),
Some(PoolMode::Import(..)) => Self::Import(Browse::input_to_command(state, input)?),
Some(PoolMode::Export(..)) => Self::Export(Browse::input_to_command(state, input)?),
_ => to_phrases_command(state, input)?
});
fn to_phrases_command (state: &PhraseListModel, input: &TuiInput) -> Option<PhrasesCommand> {
fn to_phrases_command (state: &PoolModel, input: &TuiInput) -> Option<PhrasesCommand> {
use KeyCode::{Up, Down, Delete, Char};
use PhrasesCommand as Cmd;
let index = state.phrase_index();
@ -161,7 +161,7 @@ fn to_phrases_command (state: &PhraseListModel, input: &TuiInput) -> Option<Phra
_ => return None
})
}
impl Default for PhraseListModel {
impl Default for PoolModel {
fn default () -> Self {
Self {
visible: true,
@ -173,32 +173,32 @@ impl Default for PhraseListModel {
}
}
}
from!(|phrase:&Arc<RwLock<Phrase>>|PhraseListModel = {
from!(|phrase:&Arc<RwLock<Phrase>>|PoolModel = {
let mut model = Self::default();
model.phrases.push(phrase.clone());
model.phrase.store(1, Relaxed);
model
});
has_phrases!(|self:PhraseListModel|self.phrases);
has_phrase!(|self:PhraseListModel|self.phrases[self.phrase_index()]);
impl PhraseListModel {
has_phrases!(|self: PoolModel|self.phrases);
has_phrase!(|self: PoolModel|self.phrases[self.phrase_index()]);
impl PoolModel {
pub(crate) fn phrase_index (&self) -> usize {
self.phrase.load(Relaxed)
}
pub(crate) fn set_phrase_index (&self, value: usize) {
self.phrase.store(value, Relaxed);
}
pub(crate) fn phrases_mode (&self) -> &Option<PhraseListMode> {
pub(crate) fn phrases_mode (&self) -> &Option<PoolMode> {
&self.mode
}
pub(crate) fn phrases_mode_mut (&mut self) -> &mut Option<PhraseListMode> {
pub(crate) fn phrases_mode_mut (&mut self) -> &mut Option<PoolMode> {
&mut self.mode
}
}
pub struct PhraseListView<'a>(pub(crate) &'a PhraseListModel);
pub struct PoolView<'a>(pub(crate) &'a PoolModel);
// TODO: Display phrases always in order of appearance
render!(<Tui>|self: PhraseListView<'a>|{
let PhraseListModel { phrases, mode, .. } = self.0;
render!(<Tui>|self: PoolView<'a>|{
let PoolModel { phrases, mode, .. } = self.0;
let bg = TuiTheme::g(32);
let title_color = TuiTheme::ti1();
let upper_left = "Pool:";
@ -208,13 +208,13 @@ render!(<Tui>|self: PhraseListView<'a>|{
add(&Fill::wh(Outer(Style::default().fg(color.base.rgb).bg(bg))))?;
//add(&Lozenge(Style::default().bg(border_bg).fg(border_color)))?;
add(&Tui::inset_xy(0, 1, Fill::wh(col!(move|add|match mode {
Some(PhraseListMode::Import(_, ref file_picker)) => add(file_picker),
Some(PhraseListMode::Export(_, ref file_picker)) => add(file_picker),
Some(PoolMode::Import(_, ref file_picker)) => add(file_picker),
Some(PoolMode::Export(_, ref file_picker)) => add(file_picker),
_ => Ok(for (i, phrase) in phrases.iter().enumerate() {
add(&lay!(|add|{
let Phrase { ref name, color, length, .. } = *phrase.read().unwrap();
let mut length = PhraseLength::new(length, None);
if let Some(PhraseListMode::Length(phrase, new_length, focus)) = mode {
if let Some(PoolMode::Length(phrase, new_length, focus)) = mode {
if i == *phrase {
length.pulses = *new_length;
length.focus = Some(*focus);
@ -227,7 +227,7 @@ render!(<Tui>|self: PhraseListView<'a>|{
})),
Tui::bold(true, {
let mut row2 = format!(" {name}");
if let Some(PhraseListMode::Rename(phrase, _)) = mode {
if let Some(PoolMode::Rename(phrase, _)) = mode {
if i == *phrase {
row2 = format!("{row2}");
}
@ -305,8 +305,8 @@ impl PhraseSelector {
Self { title: " Next|", time, name, color, }
}
}
command!(|self: FileBrowserCommand, state: PhraseListModel|{
use PhraseListMode::*;
command!(|self: FileBrowserCommand, state: PoolModel|{
use PoolMode::*;
use FileBrowserCommand::*;
let mode = &mut state.mode;
match mode {
@ -334,11 +334,11 @@ command!(|self: FileBrowserCommand, state: PhraseListModel|{
};
None
});
input_to_command!(FileBrowserCommand:<Tui>|state:PhraseListModel,from|{
input_to_command!(FileBrowserCommand:<Tui>|state: PoolModel,from|{
use FileBrowserCommand::*;
use KeyCode::{Up, Down, Left, Right, Enter, Esc, Backspace, Char};
if let Some(PhraseListMode::Import(_index, browser)) = &state.mode {
if let Some(PoolMode::Import(_index, browser)) = &state.mode {
match from.event() {
key_pat!(Up) => Select(browser.index.overflowing_sub(1).0
.min(browser.len().saturating_sub(1))),
@ -352,7 +352,7 @@ input_to_command!(FileBrowserCommand:<Tui>|state:PhraseListModel,from|{
key_pat!(Esc) => Cancel,
_ => return None
}
} else if let Some(PhraseListMode::Export(_index, browser)) = &state.mode {
} else if let Some(PoolMode::Export(_index, browser)) = &state.mode {
match from.event() {
key_pat!(Up) => Select(browser.index.overflowing_sub(1).0
.min(browser.len())),