mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 04:36:45 +01:00
wip: separate PhrasePlayer vs PhraseEditor
This commit is contained in:
parent
7668a6f339
commit
25e54eba4e
8 changed files with 491 additions and 636 deletions
|
|
@ -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() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue