mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
start upgrading arranger
This commit is contained in:
parent
3d669d7d24
commit
99fb3f9732
7 changed files with 177 additions and 212 deletions
|
|
@ -29,18 +29,16 @@ impl ArrangerCli {
|
||||||
let track_color_1 = ItemColor::random();
|
let track_color_1 = ItemColor::random();
|
||||||
let track_color_2 = ItemColor::random();
|
let track_color_2 = ItemColor::random();
|
||||||
for i in 0..self.tracks {
|
for i in 0..self.tracks {
|
||||||
let _track = app.track_add(
|
let _track = app.track_add(None, Some(
|
||||||
None,
|
track_color_1.mix(track_color_2, i as f32 / self.tracks as f32).into()
|
||||||
Some(track_color_1.mix(track_color_2, i as f32 / self.tracks as f32))
|
))?;
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
let scene_color_1 = ItemColor::random();
|
let scene_color_1 = ItemColor::random();
|
||||||
let scene_color_2 = ItemColor::random();
|
let scene_color_2 = ItemColor::random();
|
||||||
for i in 0..self.scenes {
|
for i in 0..self.scenes {
|
||||||
let _scene = app.scene_add(
|
let _scene = app.scene_add(None, Some(
|
||||||
None,
|
scene_color_1.mix(scene_color_2, i as f32 / self.scenes as f32).into()
|
||||||
Some(scene_color_1.mix(scene_color_2, i as f32 / self.scenes as f32))
|
))?;
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
Ok(app)
|
Ok(app)
|
||||||
})?)?;
|
})?)?;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::*;
|
||||||
pub trait HasScenes<S: ArrangerSceneApi> {
|
pub trait HasScenes<S: ArrangerSceneApi> {
|
||||||
fn scenes (&self) -> &Vec<S>;
|
fn scenes (&self) -> &Vec<S>;
|
||||||
fn scenes_mut (&mut self) -> &mut Vec<S>;
|
fn scenes_mut (&mut self) -> &mut Vec<S>;
|
||||||
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemColor>) -> Usually<&mut S>;
|
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>) -> Usually<&mut S>;
|
||||||
fn scene_del (&mut self, index: usize) {
|
fn scene_del (&mut self, index: usize) {
|
||||||
self.scenes_mut().remove(index);
|
self.scenes_mut().remove(index);
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ pub enum ArrangerSceneCommand {
|
||||||
pub trait ArrangerSceneApi: Sized {
|
pub trait ArrangerSceneApi: Sized {
|
||||||
fn name (&self) -> &Arc<RwLock<String>>;
|
fn name (&self) -> &Arc<RwLock<String>>;
|
||||||
fn clips (&self) -> &Vec<Option<Arc<RwLock<Phrase>>>>;
|
fn clips (&self) -> &Vec<Option<Arc<RwLock<Phrase>>>>;
|
||||||
fn color (&self) -> ItemColor;
|
fn color (&self) -> ItemPalette;
|
||||||
|
|
||||||
fn ppqs (scenes: &[Self], factor: usize) -> Vec<(usize, usize)> {
|
fn ppqs (scenes: &[Self], factor: usize) -> Vec<(usize, usize)> {
|
||||||
let mut total = 0;
|
let mut total = 0;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ impl<T: ArrangerTrackApi> HasTracks<T> for Vec<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ArrangerTracksApi<T: ArrangerTrackApi>: HasTracks<T> {
|
pub trait ArrangerTracksApi<T: ArrangerTrackApi>: HasTracks<T> {
|
||||||
fn track_add (&mut self, name: Option<&str>, color: Option<ItemColor>)-> Usually<&mut T>;
|
fn track_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)-> Usually<&mut T>;
|
||||||
fn track_del (&mut self, index: usize);
|
fn track_del (&mut self, index: usize);
|
||||||
fn track_default_name (&self) -> String {
|
fn track_default_name (&self) -> String {
|
||||||
format!("Track {}", self.tracks().len() + 1)
|
format!("Track {}", self.tracks().len() + 1)
|
||||||
|
|
@ -41,7 +41,7 @@ pub trait ArrangerTrackApi: HasPlayer + Send + Sync + Sized {
|
||||||
/// Preferred width of track column
|
/// Preferred width of track column
|
||||||
fn width_mut (&mut self) -> &mut usize;
|
fn width_mut (&mut self) -> &mut usize;
|
||||||
/// Identifying color of track
|
/// Identifying color of track
|
||||||
fn color (&self) -> ItemColor;
|
fn color (&self) -> ItemPalette;
|
||||||
|
|
||||||
fn longest_name (tracks: &[Self]) -> usize {
|
fn longest_name (tracks: &[Self]) -> usize {
|
||||||
tracks.iter().map(|s|s.name().read().unwrap().len()).fold(0, usize::max)
|
tracks.iter().map(|s|s.name().read().unwrap().len()).fold(0, usize::max)
|
||||||
|
|
|
||||||
|
|
@ -97,3 +97,8 @@ impl<E: Engine, X: Render<E>, Y: Render<E>> Render<E> for Bsp<E, X, Y> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ pub const NOTE_DURATIONS: [(usize, &str);26] = [
|
||||||
(3456, "9/1"),
|
(3456, "9/1"),
|
||||||
(6144, "16/1"),
|
(6144, "16/1"),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Returns the next shorter length
|
/// Returns the next shorter length
|
||||||
pub fn prev_note_length (pulses: usize) -> usize {
|
pub fn prev_note_length (pulses: usize) -> usize {
|
||||||
for i in 1..=16 { let length = NOTE_DURATIONS[16-i].0; if length < pulses { return length } }
|
for i in 1..=16 { let length = NOTE_DURATIONS[16-i].0; if length < pulses { return length } }
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ pub struct ArrangerTui {
|
||||||
pub splits: [u16;2],
|
pub splits: [u16;2],
|
||||||
pub selected: ArrangerSelection,
|
pub selected: ArrangerSelection,
|
||||||
pub mode: ArrangerMode,
|
pub mode: ArrangerMode,
|
||||||
pub color: ItemColor,
|
pub color: ItemPalette,
|
||||||
pub entered: bool,
|
pub entered: bool,
|
||||||
pub size: Measure<Tui>,
|
pub size: Measure<Tui>,
|
||||||
pub cursor: (usize, usize),
|
pub cursor: (usize, usize),
|
||||||
|
|
@ -33,7 +33,7 @@ from_jack!(|jack| ArrangerTui Self {
|
||||||
tracks: vec![],
|
tracks: vec![],
|
||||||
color: TuiTheme::bg().into(),
|
color: TuiTheme::bg().into(),
|
||||||
history: vec![],
|
history: vec![],
|
||||||
mode: ArrangerMode::Vertical(2),
|
mode: ArrangerMode::V(2),
|
||||||
name: Arc::new(RwLock::new(String::new())),
|
name: Arc::new(RwLock::new(String::new())),
|
||||||
size: Measure::new(),
|
size: Measure::new(),
|
||||||
cursor: (0, 0),
|
cursor: (0, 0),
|
||||||
|
|
@ -51,23 +51,33 @@ has_phrases!(|self:ArrangerTui|self.phrases.phrases);
|
||||||
has_editor!(|self: ArrangerTui|self.editor);
|
has_editor!(|self: ArrangerTui|self.editor);
|
||||||
handle!(<Tui>|self: ArrangerTui, input|ArrangerCommand::execute_with_state(self, input));
|
handle!(<Tui>|self: ArrangerTui, input|ArrangerCommand::execute_with_state(self, input));
|
||||||
render!(<Tui>|self: ArrangerTui|{
|
render!(<Tui>|self: ArrangerTui|{
|
||||||
let arranger_focused = self.arranger_focused();
|
let arranger = ||lay!(|add|{
|
||||||
let transport_focused = match self.focus.inner() {
|
add(&Fill::wh(Lozenge(Style::default()
|
||||||
ArrangerFocus::Transport(_) => true, _ => false
|
|
||||||
};
|
|
||||||
let transport = TransportView::from((self, None, transport_focused));
|
|
||||||
let with_transport = move|x|col!([transport, x]);
|
|
||||||
let border = Lozenge(Style::default()
|
|
||||||
.bg(TuiTheme::border_bg())
|
.bg(TuiTheme::border_bg())
|
||||||
.fg(TuiTheme::border_fg(arranger_focused)));
|
.fg(TuiTheme::border_fg(true)))))?;
|
||||||
let arranger = move||border.wrap(Tui::grow_y(1, lay!(|add|{
|
add(&self.size)?;
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ArrangerMode::Horizontal => add(&arranger_content_horizontal(self))?,
|
ArrangerMode::H => todo!("horizontal arranger"),
|
||||||
ArrangerMode::Vertical(factor) => add(&arranger_content_vertical(self, factor))?
|
ArrangerMode::V(factor) => add(&lay!([
|
||||||
};
|
Align::se(Fill::wh(Tui::pull_x(1, Tui::fg(TuiTheme::title_fg(true),
|
||||||
add(&self.size)
|
format!("{}x{}", self.size.w(), self.size.h()))
|
||||||
})));
|
))),
|
||||||
|
Tui::bg(self.color.darkest.rgb, lay!(![
|
||||||
|
ArrangerVColumnSeparator::from(self),
|
||||||
|
ArrangerVRowSeparator::from((self, factor)),
|
||||||
|
col!(![
|
||||||
|
ArrangerVHeader::from(self),
|
||||||
|
ArrangerVContent::from((self, factor)),
|
||||||
|
]),
|
||||||
|
ArrangerVCursor::from((self, factor)),
|
||||||
|
])),
|
||||||
|
])),
|
||||||
|
}
|
||||||
|
});
|
||||||
let with_pool = |x|Split::right(false, self.splits[1], PhraseListView(&self.phrases), x);
|
let with_pool = |x|Split::right(false, self.splits[1], PhraseListView(&self.phrases), x);
|
||||||
|
let play = Fixed::wh(5, 2, PlayPause(self.clock.is_rolling()));
|
||||||
|
let transport = TransportView::from((self, None, true));
|
||||||
|
let with_transport = |x|col!([row!(![&play, &transport]), &x]);
|
||||||
with_transport(col!([Fixed::h(self.splits[0], arranger()), with_pool(&self.editor),]))
|
with_transport(col!([Fixed::h(self.splits[0], arranger()), with_pool(&self.editor),]))
|
||||||
});
|
});
|
||||||
audio!(|self: ArrangerTui, client, scope|{
|
audio!(|self: ArrangerTui, client, scope|{
|
||||||
|
|
@ -112,7 +122,7 @@ audio!(|self: ArrangerTui, client, scope|{
|
||||||
Undo,
|
Undo,
|
||||||
Redo,
|
Redo,
|
||||||
Clear,
|
Clear,
|
||||||
Color(ItemColor),
|
Color(ItemPalette),
|
||||||
Clock(ClockCommand),
|
Clock(ClockCommand),
|
||||||
Scene(ArrangerSceneCommand),
|
Scene(ArrangerSceneCommand),
|
||||||
Track(ArrangerTrackCommand),
|
Track(ArrangerTrackCommand),
|
||||||
|
|
@ -122,92 +132,9 @@ audio!(|self: ArrangerTui, client, scope|{
|
||||||
Phrases(PhrasesCommand),
|
Phrases(PhrasesCommand),
|
||||||
Editor(PhraseCommand),
|
Editor(PhraseCommand),
|
||||||
}
|
}
|
||||||
command!(|self:ArrangerCommand,state:ArrangerTui|{
|
input_to_command!(ArrangerCommand: <Tui>|state:ArrangerTui,input|
|
||||||
use ArrangerCommand::*;
|
|
||||||
match self {
|
|
||||||
Focus(cmd) => cmd.execute(state)?.map(Focus),
|
|
||||||
Scene(cmd) => cmd.execute(state)?.map(Scene),
|
|
||||||
Track(cmd) => cmd.execute(state)?.map(Track),
|
|
||||||
Clip(cmd) => cmd.execute(state)?.map(Clip),
|
|
||||||
Phrases(cmd) => cmd.execute(&mut state.phrases)?.map(Phrases),
|
|
||||||
Editor(cmd) => cmd.execute(&mut state.editor)?.map(Editor),
|
|
||||||
Clock(cmd) => cmd.execute(state)?.map(Clock),
|
|
||||||
Zoom(_) => { todo!(); },
|
|
||||||
Select(selected) => {
|
|
||||||
*state.selected_mut() = selected;
|
|
||||||
None
|
|
||||||
},
|
|
||||||
_ => { todo!() }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
command!(|self:ArrangerSceneCommand,_state:ArrangerTui|None);
|
|
||||||
command!(|self:ArrangerTrackCommand,_state:ArrangerTui|None);
|
|
||||||
command!(|self:ArrangerClipCommand, _state:ArrangerTui|None);
|
|
||||||
pub trait ArrangerControl: TransportControl<ArrangerFocus> {
|
|
||||||
fn selected (&self) -> ArrangerSelection;
|
|
||||||
fn selected_mut (&mut self) -> &mut ArrangerSelection;
|
|
||||||
fn activate (&mut self) -> Usually<()>;
|
|
||||||
fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>>;
|
|
||||||
fn toggle_loop (&mut self);
|
|
||||||
fn randomize_color (&mut self);
|
|
||||||
}
|
|
||||||
impl ArrangerControl for ArrangerTui {
|
|
||||||
fn selected (&self) -> ArrangerSelection {
|
|
||||||
self.selected
|
|
||||||
}
|
|
||||||
fn selected_mut (&mut self) -> &mut ArrangerSelection {
|
|
||||||
&mut self.selected
|
|
||||||
}
|
|
||||||
fn activate (&mut self) -> Usually<()> {
|
|
||||||
if let ArrangerSelection::Scene(s) = self.selected {
|
|
||||||
for (t, track) in self.tracks.iter_mut().enumerate() {
|
|
||||||
let phrase = self.scenes[s].clips[t].clone();
|
|
||||||
if track.player.play_phrase.is_some() || phrase.is_some() {
|
|
||||||
track.player.enqueue_next(phrase.as_ref());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.clock().is_stopped() {
|
|
||||||
self.clock().play_from(Some(0))?;
|
|
||||||
}
|
|
||||||
} else if let ArrangerSelection::Clip(t, s) = self.selected {
|
|
||||||
let phrase = self.scenes()[s].clips[t].clone();
|
|
||||||
self.tracks_mut()[t].player.enqueue_next(phrase.as_ref());
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>> {
|
|
||||||
self.selected_scene()?.clips.get(self.selected.track()?)?.clone()
|
|
||||||
}
|
|
||||||
fn toggle_loop (&mut self) {
|
|
||||||
if let Some(phrase) = self.selected_phrase() {
|
|
||||||
phrase.write().unwrap().toggle_loop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn randomize_color (&mut self) {
|
|
||||||
match self.selected {
|
|
||||||
ArrangerSelection::Mix => {
|
|
||||||
self.color = ItemColor::random_dark()
|
|
||||||
},
|
|
||||||
ArrangerSelection::Track(t) => {
|
|
||||||
self.tracks_mut()[t].color = ItemColor::random()
|
|
||||||
},
|
|
||||||
ArrangerSelection::Scene(s) => {
|
|
||||||
self.scenes_mut()[s].color = ItemColor::random()
|
|
||||||
},
|
|
||||||
ArrangerSelection::Clip(t, s) => {
|
|
||||||
if let Some(phrase) = &self.scenes_mut()[s].clips[t] {
|
|
||||||
phrase.write().unwrap().color = ItemPalette::random();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl InputToCommand<Tui, ArrangerTui> for ArrangerCommand {
|
|
||||||
fn input_to_command (state: &ArrangerTui, input: &TuiInput) -> Option<Self> {
|
|
||||||
to_arranger_command(state, input)
|
to_arranger_command(state, input)
|
||||||
.or_else(||to_focus_command(input).map(ArrangerCommand::Focus))
|
.or_else(||to_focus_command(input).map(ArrangerCommand::Focus))?);
|
||||||
}
|
|
||||||
}
|
|
||||||
fn to_arranger_command (state: &ArrangerTui, input: &TuiInput) -> Option<ArrangerCommand> {
|
fn to_arranger_command (state: &ArrangerTui, input: &TuiInput) -> Option<ArrangerCommand> {
|
||||||
use ArrangerCommand as Cmd;
|
use ArrangerCommand as Cmd;
|
||||||
use KeyCode::Char;
|
use KeyCode::Char;
|
||||||
|
|
@ -264,7 +191,7 @@ fn to_arranger_mix_command (input: &TuiInput) -> Option<ArrangerCommand> {
|
||||||
key_pat!(Char('<')) => Cmd::Zoom(0),
|
key_pat!(Char('<')) => Cmd::Zoom(0),
|
||||||
key_pat!(Char('>')) => Cmd::Zoom(0),
|
key_pat!(Char('>')) => Cmd::Zoom(0),
|
||||||
key_pat!(Delete) => Cmd::Clear,
|
key_pat!(Delete) => Cmd::Clear,
|
||||||
key_pat!(Char('c')) => Cmd::Color(ItemColor::random()),
|
key_pat!(Char('c')) => Cmd::Color(ItemPalette::random()),
|
||||||
_ => return None
|
_ => return None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -282,7 +209,7 @@ fn to_arranger_track_command (input: &TuiInput, t: usize) -> Option<ArrangerComm
|
||||||
key_pat!(Char('<')) => Cmd::Track(Track::Swap(t, t - 1)),
|
key_pat!(Char('<')) => Cmd::Track(Track::Swap(t, t - 1)),
|
||||||
key_pat!(Char('>')) => Cmd::Track(Track::Swap(t, t + 1)),
|
key_pat!(Char('>')) => Cmd::Track(Track::Swap(t, t + 1)),
|
||||||
key_pat!(Delete) => Cmd::Track(Track::Delete(t)),
|
key_pat!(Delete) => Cmd::Track(Track::Delete(t)),
|
||||||
//key_pat!(Char('c')) => Cmd::Track(Track::Color(t, ItemColor::random())),
|
//key_pat!(Char('c')) => Cmd::Track(Track::Color(t, ItemPalette::random())),
|
||||||
_ => return None
|
_ => return None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -301,7 +228,7 @@ fn to_arranger_scene_command (input: &TuiInput, s: usize) -> Option<ArrangerComm
|
||||||
key_pat!(Char('>')) => Cmd::Scene(Scene::Swap(s, s + 1)),
|
key_pat!(Char('>')) => Cmd::Scene(Scene::Swap(s, s + 1)),
|
||||||
key_pat!(Enter) => Cmd::Scene(Scene::Play(s)),
|
key_pat!(Enter) => Cmd::Scene(Scene::Play(s)),
|
||||||
key_pat!(Delete) => Cmd::Scene(Scene::Delete(s)),
|
key_pat!(Delete) => Cmd::Scene(Scene::Delete(s)),
|
||||||
//key_pat!(Char('c')) => Cmd::Track(Scene::Color(s, ItemColor::random())),
|
//key_pat!(Char('c')) => Cmd::Track(Scene::Color(s, ItemPalette::random())),
|
||||||
_ => return None
|
_ => return None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -320,12 +247,92 @@ fn to_arranger_clip_command (input: &TuiInput, t: usize, s: usize) -> Option<Arr
|
||||||
key_pat!(Char('<')) => Cmd::Clip(Clip::Set(t, s, None)),
|
key_pat!(Char('<')) => Cmd::Clip(Clip::Set(t, s, None)),
|
||||||
key_pat!(Char('>')) => Cmd::Clip(Clip::Set(t, s, None)),
|
key_pat!(Char('>')) => Cmd::Clip(Clip::Set(t, s, None)),
|
||||||
key_pat!(Delete) => Cmd::Clip(Clip::Set(t, s, None)),
|
key_pat!(Delete) => Cmd::Clip(Clip::Set(t, s, None)),
|
||||||
//key_pat!(Char('c')) => Cmd::Clip(Clip::Color(t, s, ItemColor::random())),
|
//key_pat!(Char('c')) => Cmd::Clip(Clip::Color(t, s, ItemPalette::random())),
|
||||||
//key_pat!(Char('g')) => Cmd::Clip(Clip(Clip::Get(t, s))),
|
//key_pat!(Char('g')) => Cmd::Clip(Clip(Clip::Get(t, s))),
|
||||||
//key_pat!(Char('s')) => Cmd::Clip(Clip(Clip::Set(t, s))),
|
//key_pat!(Char('s')) => Cmd::Clip(Clip(Clip::Set(t, s))),
|
||||||
_ => return None
|
_ => return None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
command!(|self:ArrangerCommand,state:ArrangerTui|{
|
||||||
|
use ArrangerCommand::*;
|
||||||
|
match self {
|
||||||
|
Focus(cmd) => cmd.execute(state)?.map(Focus),
|
||||||
|
Scene(cmd) => cmd.execute(state)?.map(Scene),
|
||||||
|
Track(cmd) => cmd.execute(state)?.map(Track),
|
||||||
|
Clip(cmd) => cmd.execute(state)?.map(Clip),
|
||||||
|
Phrases(cmd) => cmd.execute(&mut state.phrases)?.map(Phrases),
|
||||||
|
Editor(cmd) => cmd.execute(&mut state.editor)?.map(Editor),
|
||||||
|
Clock(cmd) => cmd.execute(state)?.map(Clock),
|
||||||
|
Zoom(_) => { todo!(); },
|
||||||
|
Select(selected) => {
|
||||||
|
*state.selected_mut() = selected;
|
||||||
|
None
|
||||||
|
},
|
||||||
|
_ => { todo!() }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
command!(|self:ArrangerSceneCommand,_state:ArrangerTui|None);
|
||||||
|
command!(|self:ArrangerTrackCommand,_state:ArrangerTui|None);
|
||||||
|
command!(|self:ArrangerClipCommand, _state:ArrangerTui|None);
|
||||||
|
pub trait ArrangerControl: TransportControl<ArrangerFocus> {
|
||||||
|
fn selected (&self) -> ArrangerSelection;
|
||||||
|
fn selected_mut (&mut self) -> &mut ArrangerSelection;
|
||||||
|
fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>>;
|
||||||
|
fn activate (&mut self) -> Usually<()>;
|
||||||
|
fn toggle_loop (&mut self);
|
||||||
|
fn randomize_color (&mut self);
|
||||||
|
}
|
||||||
|
impl ArrangerControl for ArrangerTui {
|
||||||
|
fn selected (&self) -> ArrangerSelection {
|
||||||
|
self.selected
|
||||||
|
}
|
||||||
|
fn selected_mut (&mut self) -> &mut ArrangerSelection {
|
||||||
|
&mut self.selected
|
||||||
|
}
|
||||||
|
fn activate (&mut self) -> Usually<()> {
|
||||||
|
if let ArrangerSelection::Scene(s) = self.selected {
|
||||||
|
for (t, track) in self.tracks.iter_mut().enumerate() {
|
||||||
|
let phrase = self.scenes[s].clips[t].clone();
|
||||||
|
if track.player.play_phrase.is_some() || phrase.is_some() {
|
||||||
|
track.player.enqueue_next(phrase.as_ref());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.clock().is_stopped() {
|
||||||
|
self.clock().play_from(Some(0))?;
|
||||||
|
}
|
||||||
|
} else if let ArrangerSelection::Clip(t, s) = self.selected {
|
||||||
|
let phrase = self.scenes()[s].clips[t].clone();
|
||||||
|
self.tracks_mut()[t].player.enqueue_next(phrase.as_ref());
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>> {
|
||||||
|
self.selected_scene()?.clips.get(self.selected.track()?)?.clone()
|
||||||
|
}
|
||||||
|
fn toggle_loop (&mut self) {
|
||||||
|
if let Some(phrase) = self.selected_phrase() {
|
||||||
|
phrase.write().unwrap().toggle_loop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn randomize_color (&mut self) {
|
||||||
|
match self.selected {
|
||||||
|
ArrangerSelection::Mix => {
|
||||||
|
self.color = ItemPalette::random()
|
||||||
|
},
|
||||||
|
ArrangerSelection::Track(t) => {
|
||||||
|
self.tracks_mut()[t].color = ItemPalette::random()
|
||||||
|
},
|
||||||
|
ArrangerSelection::Scene(s) => {
|
||||||
|
self.scenes_mut()[s].color = ItemPalette::random()
|
||||||
|
},
|
||||||
|
ArrangerSelection::Clip(t, s) => {
|
||||||
|
if let Some(phrase) = &self.scenes_mut()[s].clips[t] {
|
||||||
|
phrase.write().unwrap().color = ItemPalette::random();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
impl TransportControl<ArrangerFocus> for ArrangerTui {
|
impl TransportControl<ArrangerFocus> for ArrangerTui {
|
||||||
fn transport_focused (&self) -> Option<TransportFocus> {
|
fn transport_focused (&self) -> Option<TransportFocus> {
|
||||||
match self.focus.inner() {
|
match self.focus.inner() {
|
||||||
|
|
@ -368,32 +375,15 @@ impl From<&ArrangerTui> for Option<TransportFocus> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_focus!(ArrangerTui ArrangerFocus [
|
impl_focus!(ArrangerTui ArrangerFocus [
|
||||||
//&[
|
|
||||||
//Menu,
|
|
||||||
//Menu,
|
|
||||||
//Menu,
|
|
||||||
//Menu,
|
|
||||||
//Menu,
|
|
||||||
//],
|
|
||||||
&[
|
&[
|
||||||
Transport(TransportFocus::PlayPause),
|
Transport(TransportFocus::PlayPause),
|
||||||
Transport(TransportFocus::Bpm),
|
Transport(TransportFocus::Bpm),
|
||||||
Transport(TransportFocus::Sync),
|
Transport(TransportFocus::Sync),
|
||||||
Transport(TransportFocus::Quant),
|
Transport(TransportFocus::Quant),
|
||||||
Transport(TransportFocus::Clock),
|
Transport(TransportFocus::Clock),
|
||||||
], &[
|
|
||||||
Arranger,
|
|
||||||
Arranger,
|
|
||||||
Arranger,
|
|
||||||
Arranger,
|
|
||||||
Arranger,
|
|
||||||
], &[
|
|
||||||
Phrases,
|
|
||||||
Phrases,
|
|
||||||
PhraseEditor,
|
|
||||||
PhraseEditor,
|
|
||||||
PhraseEditor,
|
|
||||||
],
|
],
|
||||||
|
&[Arranger;5],
|
||||||
|
&[Phrases, Phrases, PhraseEditor, PhraseEditor, PhraseEditor],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/// Status bar for arranger app
|
/// Status bar for arranger app
|
||||||
|
|
@ -412,10 +402,10 @@ pub enum ArrangerStatus {
|
||||||
/// Display mode of arranger
|
/// Display mode of arranger
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum ArrangerMode {
|
pub enum ArrangerMode {
|
||||||
/// Tracks are rows
|
|
||||||
Horizontal,
|
|
||||||
/// Tracks are columns
|
/// Tracks are columns
|
||||||
Vertical(usize),
|
V(usize),
|
||||||
|
/// Tracks are rows
|
||||||
|
H,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Arranger display mode can be cycled
|
/// Arranger display mode can be cycled
|
||||||
|
|
@ -423,24 +413,15 @@ impl ArrangerMode {
|
||||||
/// Cycle arranger display mode
|
/// Cycle arranger display mode
|
||||||
pub fn to_next (&mut self) {
|
pub fn to_next (&mut self) {
|
||||||
*self = match self {
|
*self = match self {
|
||||||
Self::Horizontal => Self::Vertical(1),
|
Self::H => Self::V(1),
|
||||||
Self::Vertical(1) => Self::Vertical(2),
|
Self::V(1) => Self::V(2),
|
||||||
Self::Vertical(2) => Self::Vertical(2),
|
Self::V(2) => Self::V(2),
|
||||||
Self::Vertical(0) => Self::Horizontal,
|
Self::V(0) => Self::H,
|
||||||
Self::Vertical(_) => Self::Vertical(0),
|
Self::V(_) => Self::V(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ArrangerViewState {
|
|
||||||
fn arranger_focused (&self) -> bool;
|
|
||||||
}
|
|
||||||
impl ArrangerViewState for ArrangerTui {
|
|
||||||
fn arranger_focused (&self) -> bool {
|
|
||||||
self.focused() == ArrangerFocus::Arranger
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> {
|
fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> {
|
||||||
let mut widths = vec![];
|
let mut widths = vec![];
|
||||||
let mut total = 0;
|
let mut total = 0;
|
||||||
|
|
@ -457,32 +438,12 @@ fn any_size <E: Engine> (_: E::Size) -> Perhaps<E::Size>{
|
||||||
Ok(Some([0.into(),0.into()].into()))
|
Ok(Some([0.into(),0.into()].into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arranger_content_vertical (
|
struct ArrangerVColumnSeparator {
|
||||||
view: &ArrangerTui,
|
|
||||||
factor: usize
|
|
||||||
) -> impl Render<Tui> + use<'_> {
|
|
||||||
lay!([
|
|
||||||
Align::se(Fill::wh(Tui::pull_x(1, Tui::fg(TuiTheme::title_fg(view.arranger_focused()),
|
|
||||||
format!("{}x{}", view.size.w(), view.size.h()))
|
|
||||||
))),
|
|
||||||
Tui::bg(view.color.rgb, lay!(![
|
|
||||||
ArrangerVerticalColumnSeparator::from(view),
|
|
||||||
ArrangerVerticalRowSeparator::from((view, factor)),
|
|
||||||
col!(![
|
|
||||||
ArrangerVerticalHeader::from(view),
|
|
||||||
ArrangerVerticalContent::from((view, factor)),
|
|
||||||
]),
|
|
||||||
ArrangerVerticalCursor::from((view, factor)),
|
|
||||||
])),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ArrangerVerticalColumnSeparator {
|
|
||||||
cols: Vec<(usize, usize)>,
|
cols: Vec<(usize, usize)>,
|
||||||
scenes_w: u16,
|
scenes_w: u16,
|
||||||
sep_fg: Color,
|
sep_fg: Color,
|
||||||
}
|
}
|
||||||
impl From<&ArrangerTui> for ArrangerVerticalColumnSeparator {
|
impl From<&ArrangerTui> for ArrangerVColumnSeparator {
|
||||||
fn from (state: &ArrangerTui) -> Self {
|
fn from (state: &ArrangerTui) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cols: track_widths(state.tracks()),
|
cols: track_widths(state.tracks()),
|
||||||
|
|
@ -491,7 +452,7 @@ impl From<&ArrangerTui> for ArrangerVerticalColumnSeparator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render!(<Tui>|self: ArrangerVerticalColumnSeparator|render(move|to: &mut TuiOutput|{
|
render!(<Tui>|self: ArrangerVColumnSeparator|render(move|to: &mut TuiOutput|{
|
||||||
let style = Some(Style::default().fg(self.sep_fg));
|
let style = Some(Style::default().fg(self.sep_fg));
|
||||||
Ok(for x in self.cols.iter().map(|col|col.1) {
|
Ok(for x in self.cols.iter().map(|col|col.1) {
|
||||||
let x = self.scenes_w + to.area().x() + x as u16;
|
let x = self.scenes_w + to.area().x() + x as u16;
|
||||||
|
|
@ -501,11 +462,11 @@ render!(<Tui>|self: ArrangerVerticalColumnSeparator|render(move|to: &mut TuiOutp
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
|
||||||
struct ArrangerVerticalRowSeparator {
|
struct ArrangerVRowSeparator {
|
||||||
rows: Vec<(usize, usize)>,
|
rows: Vec<(usize, usize)>,
|
||||||
sep_fg: Color,
|
sep_fg: Color,
|
||||||
}
|
}
|
||||||
impl From<(&ArrangerTui, usize)> for ArrangerVerticalRowSeparator {
|
impl From<(&ArrangerTui, usize)> for ArrangerVRowSeparator {
|
||||||
fn from ((state, factor): (&ArrangerTui, usize)) -> Self {
|
fn from ((state, factor): (&ArrangerTui, usize)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
rows: ArrangerScene::ppqs(state.scenes(), factor),
|
rows: ArrangerScene::ppqs(state.scenes(), factor),
|
||||||
|
|
@ -514,7 +475,7 @@ impl From<(&ArrangerTui, usize)> for ArrangerVerticalRowSeparator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(<Tui>|self: ArrangerVerticalRowSeparator|render(move|to: &mut TuiOutput|{
|
render!(<Tui>|self: ArrangerVRowSeparator|render(move|to: &mut TuiOutput|{
|
||||||
Ok(for y in self.rows.iter().map(|row|row.1) {
|
Ok(for y in self.rows.iter().map(|row|row.1) {
|
||||||
let y = to.area().y() + (y / PPQ) as u16 + 1;
|
let y = to.area().y() + (y / PPQ) as u16 + 1;
|
||||||
if y >= to.buffer.area.height { break }
|
if y >= to.buffer.area.height { break }
|
||||||
|
|
@ -528,7 +489,7 @@ render!(<Tui>|self: ArrangerVerticalRowSeparator|render(move|to: &mut TuiOutput|
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
|
||||||
struct ArrangerVerticalCursor {
|
struct ArrangerVCursor {
|
||||||
cols: Vec<(usize, usize)>,
|
cols: Vec<(usize, usize)>,
|
||||||
rows: Vec<(usize, usize)>,
|
rows: Vec<(usize, usize)>,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
|
|
@ -536,19 +497,19 @@ struct ArrangerVerticalCursor {
|
||||||
scenes_w: u16,
|
scenes_w: u16,
|
||||||
header_h: u16,
|
header_h: u16,
|
||||||
}
|
}
|
||||||
impl From<(&ArrangerTui, usize)> for ArrangerVerticalCursor {
|
impl From<(&ArrangerTui, usize)> for ArrangerVCursor {
|
||||||
fn from ((state, factor): (&ArrangerTui, usize)) -> Self {
|
fn from ((state, factor): (&ArrangerTui, usize)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cols: track_widths(state.tracks()),
|
cols: track_widths(state.tracks()),
|
||||||
rows: ArrangerScene::ppqs(state.scenes(), factor),
|
rows: ArrangerScene::ppqs(state.scenes(), factor),
|
||||||
focused: state.arranger_focused(),
|
focused: true,
|
||||||
selected: state.selected,
|
selected: state.selected,
|
||||||
scenes_w: 3 + ArrangerScene::longest_name(state.scenes()) as u16,
|
scenes_w: 3 + ArrangerScene::longest_name(state.scenes()) as u16,
|
||||||
header_h: 3,
|
header_h: 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render!(<Tui>|self: ArrangerVerticalCursor|render(move|to: &mut TuiOutput|{
|
render!(<Tui>|self: ArrangerVCursor|render(move|to: &mut TuiOutput|{
|
||||||
let area = to.area();
|
let area = to.area();
|
||||||
let focused = self.focused;
|
let focused = self.focused;
|
||||||
let selected = self.selected;
|
let selected = self.selected;
|
||||||
|
|
@ -603,7 +564,7 @@ render!(<Tui>|self: ArrangerVerticalCursor|render(move|to: &mut TuiOutput|{
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
|
||||||
struct ArrangerVerticalHeader<'a> {
|
struct ArrangerVHeader<'a> {
|
||||||
tracks: &'a Vec<ArrangerTrack>,
|
tracks: &'a Vec<ArrangerTrack>,
|
||||||
cols: Vec<(usize, usize)>,
|
cols: Vec<(usize, usize)>,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
|
|
@ -613,12 +574,12 @@ struct ArrangerVerticalHeader<'a> {
|
||||||
timebase: &'a Arc<Timebase>,
|
timebase: &'a Arc<Timebase>,
|
||||||
current: &'a Arc<Moment>,
|
current: &'a Arc<Moment>,
|
||||||
}
|
}
|
||||||
impl<'a> From<&'a ArrangerTui> for ArrangerVerticalHeader<'a> {
|
impl<'a> From<&'a ArrangerTui> for ArrangerVHeader<'a> {
|
||||||
fn from (state: &'a ArrangerTui) -> Self {
|
fn from (state: &'a ArrangerTui) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tracks: &state.tracks,
|
tracks: &state.tracks,
|
||||||
cols: track_widths(state.tracks()),
|
cols: track_widths(state.tracks()),
|
||||||
focused: state.arranger_focused(),
|
focused: true,
|
||||||
selected: state.selected,
|
selected: state.selected,
|
||||||
scenes_w: 3 + ArrangerScene::longest_name(state.scenes()) as u16,
|
scenes_w: 3 + ArrangerScene::longest_name(state.scenes()) as u16,
|
||||||
header_h: 3,
|
header_h: 3,
|
||||||
|
|
@ -627,13 +588,13 @@ impl<'a> From<&'a ArrangerTui> for ArrangerVerticalHeader<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render!(<Tui>|self: ArrangerVerticalHeader<'a>|row!(
|
render!(<Tui>|self: ArrangerVHeader<'a>|row!(
|
||||||
(track, w) in self.tracks.iter().zip(self.cols.iter().map(|col|col.0)) => {
|
(track, w) in self.tracks.iter().zip(self.cols.iter().map(|col|col.0)) => {
|
||||||
// name and width of track
|
// name and width of track
|
||||||
let name = track.name().read().unwrap();
|
let name = track.name().read().unwrap();
|
||||||
let max_w = w.saturating_sub(1).min(name.len()).max(2);
|
let max_w = w.saturating_sub(1).min(name.len()).max(2);
|
||||||
let name = format!("▎{}", &name[0..max_w]);
|
let name = format!("▎{}", &name[0..max_w]);
|
||||||
let name = Tui::bold(true, name);
|
let name = Tui::bold(true, Tui::fg(track.color.lightest.rgb, name));
|
||||||
// beats elapsed
|
// beats elapsed
|
||||||
let elapsed = if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() {
|
let elapsed = if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() {
|
||||||
let length = phrase.read().unwrap().length;
|
let length = phrase.read().unwrap().length;
|
||||||
|
|
@ -668,13 +629,13 @@ render!(<Tui>|self: ArrangerVerticalHeader<'a>|row!(
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or("(none)".into()));
|
.unwrap_or("(none)".into()));
|
||||||
Tui::push_x(self.scenes_w,
|
Tui::push_x(self.scenes_w,
|
||||||
Tui::bg(track.color().rgb,
|
Tui::bg(track.color().base.rgb,
|
||||||
Tui::min_xy(w as u16, self.header_h,
|
Tui::min_xy(w as u16, self.header_h,
|
||||||
col!([name, timer]))))
|
col!([name, timer]))))
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
struct ArrangerVerticalContent<'a> {
|
struct ArrangerVContent<'a> {
|
||||||
size: &'a Measure<Tui>,
|
size: &'a Measure<Tui>,
|
||||||
scenes: &'a Vec<ArrangerScene>,
|
scenes: &'a Vec<ArrangerScene>,
|
||||||
tracks: &'a Vec<ArrangerTrack>,
|
tracks: &'a Vec<ArrangerTrack>,
|
||||||
|
|
@ -682,7 +643,7 @@ struct ArrangerVerticalContent<'a> {
|
||||||
cols: Vec<(usize, usize)>,
|
cols: Vec<(usize, usize)>,
|
||||||
header_h: u16,
|
header_h: u16,
|
||||||
}
|
}
|
||||||
impl<'a> From<(&'a ArrangerTui, usize)> for ArrangerVerticalContent<'a> {
|
impl<'a> From<(&'a ArrangerTui, usize)> for ArrangerVContent<'a> {
|
||||||
fn from ((state, factor): (&'a ArrangerTui, usize)) -> Self {
|
fn from ((state, factor): (&'a ArrangerTui, usize)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
size: &state.size,
|
size: &state.size,
|
||||||
|
|
@ -694,7 +655,7 @@ impl<'a> From<(&'a ArrangerTui, usize)> for ArrangerVerticalContent<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render!(<Tui>|self: ArrangerVerticalContent<'a>|Fixed::h(
|
render!(<Tui>|self: ArrangerVContent<'a>|Fixed::h(
|
||||||
(self.size.h() as u16).saturating_sub(self.header_h),
|
(self.size.h() as u16).saturating_sub(self.header_h),
|
||||||
col!((scene, pulses) in self.scenes.iter().zip(self.rows.iter().map(|row|row.0)) => {
|
col!((scene, pulses) in self.scenes.iter().zip(self.rows.iter().map(|row|row.0)) => {
|
||||||
let height = 1.max((pulses / PPQ) as u16);
|
let height = 1.max((pulses / PPQ) as u16);
|
||||||
|
|
@ -736,14 +697,14 @@ impl HasScenes<ArrangerScene> for ArrangerTui {
|
||||||
fn scenes_mut (&mut self) -> &mut Vec<ArrangerScene> {
|
fn scenes_mut (&mut self) -> &mut Vec<ArrangerScene> {
|
||||||
&mut self.scenes
|
&mut self.scenes
|
||||||
}
|
}
|
||||||
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemColor>)
|
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
||||||
-> Usually<&mut ArrangerScene>
|
-> Usually<&mut ArrangerScene>
|
||||||
{
|
{
|
||||||
let name = name.map_or_else(||self.scene_default_name(), |x|x.to_string());
|
let name = name.map_or_else(||self.scene_default_name(), |x|x.to_string());
|
||||||
let scene = ArrangerScene {
|
let scene = ArrangerScene {
|
||||||
name: Arc::new(name.into()),
|
name: Arc::new(name.into()),
|
||||||
clips: vec![None;self.tracks().len()],
|
clips: vec![None;self.tracks().len()],
|
||||||
color: color.unwrap_or_else(||ItemColor::random()),
|
color: color.unwrap_or_else(||ItemPalette::random()),
|
||||||
};
|
};
|
||||||
self.scenes_mut().push(scene);
|
self.scenes_mut().push(scene);
|
||||||
let index = self.scenes().len() - 1;
|
let index = self.scenes().len() - 1;
|
||||||
|
|
@ -763,7 +724,7 @@ impl HasScenes<ArrangerScene> for ArrangerTui {
|
||||||
/// Clips in scene, one per track
|
/// Clips in scene, one per track
|
||||||
pub(crate) clips: Vec<Option<Arc<RwLock<Phrase>>>>,
|
pub(crate) clips: Vec<Option<Arc<RwLock<Phrase>>>>,
|
||||||
/// Identifying color of scene
|
/// Identifying color of scene
|
||||||
pub(crate) color: ItemColor,
|
pub(crate) color: ItemPalette,
|
||||||
}
|
}
|
||||||
impl ArrangerSceneApi for ArrangerScene {
|
impl ArrangerSceneApi for ArrangerScene {
|
||||||
fn name (&self) -> &Arc<RwLock<String>> {
|
fn name (&self) -> &Arc<RwLock<String>> {
|
||||||
|
|
@ -772,7 +733,7 @@ impl ArrangerSceneApi for ArrangerScene {
|
||||||
fn clips (&self) -> &Vec<Option<Arc<RwLock<Phrase>>>> {
|
fn clips (&self) -> &Vec<Option<Arc<RwLock<Phrase>>>> {
|
||||||
&self.clips
|
&self.clips
|
||||||
}
|
}
|
||||||
fn color (&self) -> ItemColor {
|
fn color (&self) -> ItemPalette {
|
||||||
self.color
|
self.color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -785,14 +746,14 @@ impl HasTracks<ArrangerTrack> for ArrangerTui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ArrangerTracksApi<ArrangerTrack> for ArrangerTui {
|
impl ArrangerTracksApi<ArrangerTrack> for ArrangerTui {
|
||||||
fn track_add (&mut self, name: Option<&str>, color: Option<ItemColor>)
|
fn track_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
||||||
-> Usually<&mut ArrangerTrack>
|
-> Usually<&mut ArrangerTrack>
|
||||||
{
|
{
|
||||||
let name = name.map_or_else(||self.track_default_name(), |x|x.to_string());
|
let name = name.map_or_else(||self.track_default_name(), |x|x.to_string());
|
||||||
let track = ArrangerTrack {
|
let track = ArrangerTrack {
|
||||||
width: name.len() + 2,
|
width: name.len() + 2,
|
||||||
name: Arc::new(name.into()),
|
name: Arc::new(name.into()),
|
||||||
color: color.unwrap_or_else(||ItemColor::random()),
|
color: color.unwrap_or_else(||ItemPalette::random()),
|
||||||
player: PhrasePlayerModel::from(&self.clock),
|
player: PhrasePlayerModel::from(&self.clock),
|
||||||
};
|
};
|
||||||
self.tracks_mut().push(track);
|
self.tracks_mut().push(track);
|
||||||
|
|
@ -813,7 +774,7 @@ impl ArrangerTracksApi<ArrangerTrack> for ArrangerTui {
|
||||||
/// Preferred width of track column
|
/// Preferred width of track column
|
||||||
pub(crate) width: usize,
|
pub(crate) width: usize,
|
||||||
/// Identifying color of track
|
/// Identifying color of track
|
||||||
pub(crate) color: ItemColor,
|
pub(crate) color: ItemPalette,
|
||||||
/// MIDI player state
|
/// MIDI player state
|
||||||
pub(crate) player: PhrasePlayerModel,
|
pub(crate) player: PhrasePlayerModel,
|
||||||
}
|
}
|
||||||
|
|
@ -833,7 +794,7 @@ impl ArrangerTrackApi for ArrangerTrack {
|
||||||
&mut self.width
|
&mut self.width
|
||||||
}
|
}
|
||||||
/// Identifying color of track
|
/// Identifying color of track
|
||||||
fn color (&self) -> ItemColor {
|
fn color (&self) -> ItemPalette {
|
||||||
self.color
|
self.color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -930,7 +891,7 @@ pub fn arranger_content_horizontal (
|
||||||
) -> impl Render<Tui> + use<'_> {
|
) -> impl Render<Tui> + use<'_> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
//let focused = view.arranger_focused();
|
//let focused = true;
|
||||||
//let _tracks = view.tracks();
|
//let _tracks = view.tracks();
|
||||||
//lay!(
|
//lay!(
|
||||||
//focused.then_some(Background(TuiTheme::border_bg())),
|
//focused.then_some(Background(TuiTheme::border_bg())),
|
||||||
|
|
@ -1137,13 +1098,13 @@ pub fn arranger_content_horizontal (
|
||||||
//AddTrack => { state.state.track_add(None, None)?; },
|
//AddTrack => { state.state.track_add(None, None)?; },
|
||||||
//ToggleLoop => { state.state.toggle_loop() },
|
//ToggleLoop => { state.state.toggle_loop() },
|
||||||
//pub fn zoom_in (&mut self) {
|
//pub fn zoom_in (&mut self) {
|
||||||
//if let ArrangerEditorMode::Vertical(factor) = self.mode {
|
//if let ArrangerEditorMode::V(factor) = self.mode {
|
||||||
//self.mode = ArrangerEditorMode::Vertical(factor + 1)
|
//self.mode = ArrangerEditorMode::V(factor + 1)
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
//pub fn zoom_out (&mut self) {
|
//pub fn zoom_out (&mut self) {
|
||||||
//if let ArrangerEditorMode::Vertical(factor) = self.mode {
|
//if let ArrangerEditorMode::V(factor) = self.mode {
|
||||||
//self.mode = ArrangerEditorMode::Vertical(factor.saturating_sub(1))
|
//self.mode = ArrangerEditorMode::V(factor.saturating_sub(1))
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
//pub fn move_back (&mut self) {
|
//pub fn move_back (&mut self) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue