mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
PhraseList -> Pool
This commit is contained in:
parent
ac0ee26b7c
commit
85cfb43e82
7 changed files with 52 additions and 54 deletions
|
|
@ -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;
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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())),
|
||||
Loading…
Add table
Add a link
Reference in a new issue