wip: separate PhrasePlayer vs PhraseEditor

This commit is contained in:
🪞👃🪞 2024-10-08 12:05:47 +03:00
parent 7668a6f339
commit 25e54eba4e
8 changed files with 491 additions and 636 deletions

View file

@ -1,5 +1,4 @@
use crate::*;
/// The standalone arranger consists of transport, clip grid, and sequencer.
impl Content for Arranger<Tui> {
type Engine = Tui;
@ -8,17 +7,11 @@ impl Content for Arranger<Tui> {
add(&Stack::down(move|add|{
add(&self.transport)?;
let arrangement = &self.arrangement as &dyn Widget<Engine = Tui>;
if let (Some(direction), Some(sequencer)) = (
self.show_sequencer,
self.arrangement.sequencer(),
) {
let sequencer = sequencer as &dyn Widget<Engine = Tui>;
add(&arrangement.split(
direction,
20,
self.phrases.clone()
.split(direction.ccw(), 20, sequencer)
.min_y(20)).fill_y())
if let Some(direction) = self.show_sequencer {
let editor = &self.editor as &dyn Widget<Engine = Tui>;
add(&arrangement.split(direction, 20, self.phrases.clone()
.split(direction.ccw(), 20, editor)
.min_y(20)).fill_y())
} else {
add(&self.arrangement)
}
@ -32,7 +25,6 @@ impl Content for Arranger<Tui> {
})
}
}
/// Handle top-level events in standalone arranger.
impl Handle<Tui> for Arranger<Tui> {
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
@ -46,13 +38,6 @@ impl Handle<Tui> for Arranger<Tui> {
let focus = self.focus;
let is_first_row = self.arrangement.is_first_row();
let is_last_row = self.arrangement.is_last_row();
let mut focused_handle = || {
if focus == 2 {
self.arrangement.sequencer_mut().handle(from)
} else {
self.focused_mut().handle(from)
}
};
match from.event() {
key!(KeyCode::Char(' ')) => {
if let Some(ref mut transport) = self.transport {
@ -73,22 +58,23 @@ impl Handle<Tui> for Arranger<Tui> {
} else if focus == 1 && is_last_row {
self.focus_next();
} else {
return focused_handle()
return
return self.focused_mut().handle(from)
}
},
key!(KeyCode::Up) => {
if focus == 1 && is_first_row {
self.focus_prev();
} else {
return focused_handle()
return self.focused_mut().handle(from)
}
},
_ => return focused_handle()
_ => return self.focused_mut().handle(from)
}
Ok(Some(true))
}
}
/// Focusable items in standalone arranger.
impl Focus<3, Tui> for Arranger<Tui> {
fn focus (&self) -> usize {
@ -98,27 +84,23 @@ impl Focus<3, Tui> for Arranger<Tui> {
&mut self.focus
}
fn focusable (&self) -> [&dyn Focusable<Tui>;3] {
focusables!(self.transport, self.arrangement, self.sequencer_proxy)
focusables!(self.transport, self.arrangement, self.editor)
}
fn focusable_mut (&mut self) -> [&mut dyn Focusable<Tui>;3] {
focusables_mut!(self.transport, self.arrangement, self.sequencer_proxy)
focusables_mut!(self.transport, self.arrangement, self.editor)
}
}
impl Arranger<Tui> {
pub fn rename_selected (&mut self) {
self.modal = Some(Box::new(ArrangerRenameModal::new(
self.arrangement.selected,
&match self.arrangement.selected {
ArrangerFocus::Mix => self.arrangement.name.clone(),
ArrangerFocus::Track(t) => self.arrangement.tracks[t].name.clone(),
ArrangerFocus::Scene(s) => self.arrangement.scenes[s].name.clone(),
ArrangerFocus::Clip(t, s) => self.arrangement.tracks[t].phrases[s].read().unwrap().name.clone(),
}
)));
let Arrangement { selected, ref name, ref tracks, ref scenes, .. } = self.arrangement;
self.modal = Some(Box::new(ArrangerRenameModal::new(selected, &match selected {
ArrangementFocus::Mix => name.clone(),
ArrangementFocus::Track(t) => tracks[t].name.clone(),
ArrangementFocus::Scene(s) => scenes[s].name.clone(),
ArrangementFocus::Clip(t, s) => tracks[t].phrases[s].read().unwrap().name.clone(),
})));
}
}
impl Focusable<Tui> for Arrangement<Tui> {
fn is_focused (&self) -> bool {
self.focused
@ -127,7 +109,6 @@ impl Focusable<Tui> for Arrangement<Tui> {
self.focused = focused
}
}
impl Handle<Tui> for Arrangement<Tui> {
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
match from.event() {
@ -138,7 +119,7 @@ impl Handle<Tui> for Arrangement<Tui> {
// cursor_up: move cursor up
key!(KeyCode::Up) => {
match self.mode {
ArrangerViewMode::Horizontal => self.track_prev(),
ArrangementViewMode::Horizontal => self.track_prev(),
_ => self.scene_prev(),
};
self.show_phrase();
@ -146,7 +127,7 @@ impl Handle<Tui> for Arrangement<Tui> {
// cursor_down
key!(KeyCode::Down) => {
match self.mode {
ArrangerViewMode::Horizontal => self.track_next(),
ArrangementViewMode::Horizontal => self.track_next(),
_ => self.scene_next(),
};
self.show_phrase();
@ -154,7 +135,7 @@ impl Handle<Tui> for Arrangement<Tui> {
// cursor left
key!(KeyCode::Left) => {
match self.mode {
ArrangerViewMode::Horizontal => self.scene_prev(),
ArrangementViewMode::Horizontal => self.scene_prev(),
_ => self.track_prev(),
};
self.show_phrase();
@ -162,7 +143,7 @@ impl Handle<Tui> for Arrangement<Tui> {
// cursor right
key!(KeyCode::Right) => {
match self.mode {
ArrangerViewMode::Horizontal => self.scene_next(),
ArrangementViewMode::Horizontal => self.scene_next(),
_ => self.track_next(),
};
self.show_phrase();
@ -211,14 +192,13 @@ impl Handle<Tui> for Arrangement<Tui> {
Ok(Some(true))
}
}
impl Content for Arrangement<Tui> {
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
Layers::new(move |add|{
match self.mode {
ArrangerViewMode::Horizontal => add(&HorizontalArranger(&self)),
ArrangerViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor))
ArrangementViewMode::Horizontal => add(&HorizontalArranger(&self)),
ArrangementViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor))
}?;
add(&Align::SE(self.selected.description(
&self.tracks,
@ -227,24 +207,21 @@ impl Content for Arrangement<Tui> {
})
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
impl<'a> Content for VerticalArranger<'a, Tui> {
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
let Self(state, factor) = self;
let (cols, rows) = if *factor == 0 {(
track_clip_name_lengths(state.tracks.as_slice()),
scene_ppqs(state.tracks.as_slice(), state.scenes.as_slice()),
ArrangementTrack::clip_name_lengths(state.tracks.as_slice()),
Scene::ppqs(state.tracks.as_slice(), state.scenes.as_slice()),
)} else {(
track_clip_name_lengths(state.tracks.as_slice()),
ArrangementTrack::clip_name_lengths(state.tracks.as_slice()),
(0..=state.scenes.len()).map(|i|(factor*PPQ, factor*PPQ*i)).collect::<Vec<_>>(),
)};
//let height = rows.last().map(|(w,y)|(y+w)/PPQ).unwrap_or(16);
let tracks: &[Sequencer<Tui>] = state.tracks.as_ref();
let tracks: &[ArrangementTrack<Tui>] = state.tracks.as_ref();
let scenes: &[Scene] = state.scenes.as_ref();
let offset = 4 + scene_name_max_len(scenes) as u16;
let offset = 4 + Scene::longest_name(scenes) as u16;
Layers::new(move |add|{
let rows: &[(usize, usize)] = rows.as_ref();
let cols: &[(usize, usize)] = cols.as_ref();
@ -267,7 +244,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
let name = name.read().unwrap();
let name = format!("{clip:02} {}", name);
add(&name.as_str().push_x(1).fixed_x(w))?;
if (track as &Sequencer<_>).playing_phrase == Some(*clip) {
if (track as &PhrasePlayer<_>).phrase == Some(*clip) {
color = COLOR_PLAYING
} else {
color = COLOR_BG1
@ -303,39 +280,6 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
.fg(Color::Rgb(70, 80, 50))))
}
}
pub fn track_clip_name_lengths <E: Engine> (tracks: &[Sequencer<E>]) -> Vec<(usize, usize)> {
let mut total = 0;
let mut lengths: Vec<(usize, usize)> = tracks.iter().map(|track|{
let len = 4 + track.phrases
.iter()
.fold(track.name.read().unwrap().len(), |len, phrase|{
len.max(phrase.read().unwrap().name.read().unwrap().len())
});
total = total + len;
(len, total - len)
}).collect();
lengths.push((0, total));
lengths
}
pub fn scene_ppqs <E: Engine> (tracks: &[Sequencer<E>], scenes: &[Scene]) -> Vec<(usize, usize)> {
let mut total = 0;
let mut scenes: Vec<(usize, usize)> = scenes.iter().map(|scene|{
let pulses = scene.pulses(tracks).max(PPQ);
total = total + pulses;
(pulses, total - pulses)
}).collect();
scenes.push((0, total));
scenes
}
pub fn scene_name_max_len (scenes: &[Scene]) -> usize {
scenes.iter()
.map(|s|s.name.read().unwrap().len())
.fold(0, usize::max)
}
impl<'a> Widget for VerticalArrangerGrid<'a> {
type Engine = Tui;
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
@ -364,7 +308,6 @@ impl<'a> Widget for VerticalArrangerGrid<'a> {
Ok(())
}
}
impl<'a> Widget for VerticalArrangerCursor<'a> {
type Engine = Tui;
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
@ -392,21 +335,21 @@ impl<'a> Widget for VerticalArrangerCursor<'a> {
let mut scene_area: Option<[u16;4]> = None;
let mut clip_area: Option<[u16;4]> = None;
let area = match selected {
ArrangerFocus::Mix => {
ArrangementFocus::Mix => {
if focused {
to.fill_bg(area, Color::Rgb(40, 50, 30));
}
area
},
ArrangerFocus::Track(t) => {
ArrangementFocus::Track(t) => {
track_area = Some(get_track_area(t));
area
},
ArrangerFocus::Scene(s) => {
ArrangementFocus::Scene(s) => {
scene_area = Some(get_scene_area(s));
area
},
ArrangerFocus::Clip(t, s) => {
ArrangementFocus::Clip(t, s) => {
track_area = Some(get_track_area(t));
scene_area = Some(get_scene_area(s));
clip_area = Some(get_clip_area(t, s));
@ -436,9 +379,6 @@ impl<'a> Widget for VerticalArrangerCursor<'a> {
Ok(())
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
impl<'a> Content for HorizontalArranger<'a, Tui> {
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
@ -653,15 +593,6 @@ impl<'a> Content for HorizontalArranger<'a, Tui> {
)
}
}
pub fn track_name_max_len <E: Engine> (tracks: &[Sequencer<E>]) -> usize {
tracks.iter()
.map(|s|s.name.read().unwrap().len())
.fold(0, usize::max)
}
///////////////////////////////////////////////////////////////////////////////////////////////////
impl Content for ArrangerRenameModal<Tui> {
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
@ -673,10 +604,10 @@ impl Content for ArrangerRenameModal<Tui> {
//to.fill_bg(bg_area, COLOR_BG1);
//Lozenge(Style::default().bold().white().dim()).draw(to.with_rect(bg_area));
//let label = match self.target {
//ArrangerFocus::Mix => "Rename project:",
//ArrangerFocus::Track(_) => "Rename track:",
//ArrangerFocus::Scene(_) => "Rename scene:",
//ArrangerFocus::Clip(_, _) => "Rename clip:",
//ArrangementFocus::Mix => "Rename project:",
//ArrangementFocus::Track(_) => "Rename track:",
//ArrangementFocus::Scene(_) => "Rename scene:",
//ArrangementFocus::Clip(_, _) => "Rename clip:",
//};
//let style = Some(Style::default().not_bold().white().not_dim());
//to.blit(&label, area.x() + 3, y, style);
@ -688,7 +619,6 @@ impl Content for ArrangerRenameModal<Tui> {
//Ok(())
}
}
impl Handle<Tui> for ArrangerRenameModal<Tui> {
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
match from.event() {