wip10 (6e)

This commit is contained in:
🪞👃🪞 2024-12-09 20:59:19 +01:00
parent f9d1456897
commit 06dab6d0d7
2 changed files with 180 additions and 148 deletions

View file

@ -16,17 +16,17 @@ use crate::*;
}
#[macro_export] macro_rules! col_up {
($(move)?|$add:ident|$expr:expr) => {
Stack::up($(move)?|$add|$expr)
([$($expr:expr),* $(,)?]) => {
Stack::up(move|add|{ $(add(&$expr)?;)* Ok(()) })
};
(![$($expr:expr),* $(,)?]) => {
Stack::up(|add|{ $(add(&$expr)?;)* Ok(()) })
};
($expr:expr) => {
Stack::up(expr)
};
($pat:pat in $collection:expr => $item:expr) => {
Stack::up(move |add|{
for $pat in $collection { add(&$item)?; }
Ok(())
})
};
($($expr:expr),* $(,)?) => {
Stack::up(move|add|{ $(add(&$expr)?;)* Ok(()) })
Stack::up(move |add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
};
}

View file

@ -3,42 +3,37 @@ use crate::*;
// Layout for standalone arranger app.
render!(|self: ArrangerTui|{
let arranger_focused = self.arranger_focused();
Tui::to_south(
let border = Lozenge(Style::default().bg(TuiTheme::border_bg()).fg(TuiTheme::border_fg(arranger_focused)));
col_up!([
TransportView::from(self),
Tui::to_south(
self.splits[0],
Tui::to_south(
lay!([
Layers::new(move |add|{
match self.mode {
ArrangerMode::Horizontal =>
add(&arranger_content_horizontal(self))?,
ArrangerMode::Vertical(factor) =>
add(&arranger_content_vertical(self, factor))?
};
add(&self.size)
})
.grow_y(1)
.border(Lozenge(Style::default()
.bg(TuiTheme::border_bg())
.fg(TuiTheme::border_fg(arranger_focused)))),
widget(&self.size),
widget(&format!("[{}] Arranger", if self.entered {
col!([
Tui::fixed_y(self.splits[0], lay!([
border.wrap(Tui::grow_y(1, Layers::new(move |add|{
match self.mode {
ArrangerMode::Horizontal =>
add(&arranger_content_horizontal(self))?,
ArrangerMode::Vertical(factor) =>
add(&arranger_content_vertical(self, factor))?
};
add(&self.size)
}))),
self.size,
Tui::push_x(1, Tui::fg(
TuiTheme::title_fg(arranger_focused),
format!("[{}] Arranger", if self.entered {
""
} else {
" "
}))
.fg(TuiTheme::title_fg(arranger_focused))
.push_x(1),
]),
Split::right(
self.splits[1],
PhraseListView::from(self),
PhraseView::from(self),
)
})
))
])),
Split::right(
self.splits[1],
PhraseListView::from(self),
PhraseView::from(self),
)
)
)
])
])
});
/// Display mode of arranger
@ -97,114 +92,19 @@ pub fn arranger_content_vertical (
view: &ArrangerTui,
factor: usize
) -> impl Render<Tui> + use<'_> {
let timebase = view.clock().timebase();
let current = &view.clock().playhead;
let tracks = view.tracks();
let scenes = view.scenes();
let cols = track_widths(tracks);
let rows = ArrangerScene::ppqs(scenes, factor);
let bg = view.color;
let clip_bg = TuiTheme::border_bg();
let sep_fg = TuiTheme::separator_fg(false);
let header_h = 3u16;//5u16;
let scenes_w = 3 + ArrangerScene::longest_name(scenes) as u16; // x of 1st track
//let rows: &[(usize, usize)] = rows_.as_ref();
//let cols: &[(usize, usize)] = cols_.as_ref();
// track titles
let header = row!((track, w) in tracks.iter().zip(cols.iter().map(|col|col.0)) => {
// name and width of track
let name = track.name().read().unwrap();
let max_w = w.saturating_sub(1).min(name.len()).max(2);
let name = format!("{}", &name[0..max_w]);
let name = Tui::bold(true, name);
// beats elapsed
let elapsed = if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() {
let length = phrase.read().unwrap().length;
let elapsed = track.player.pulses_since_start().unwrap();
let elapsed = timebase.format_beats_1_short(
(elapsed as usize % length) as f64
);
format!("▎+{elapsed:>}")
} else {
String::from("")
};
// beats until switchover
let until_next = track.player.next_phrase().as_ref().map(|(t, _)|{
let target = t.pulse.get();
let current = current.pulse.get();
if target > current {
let remaining = target - current;
format!("▎-{:>}", timebase.format_beats_0_short(remaining))
} else {
String::new()
}
}).unwrap_or(String::from(""));
let timer = Tui::to_south(until_next, elapsed);
// name of active MIDI input
let _input = format!("▎>{}", track.player.midi_ins().get(0)
.map(|port|port.short_name())
.transpose()?
.unwrap_or("(none)".into()));
// name of active MIDI output
let _output = format!("▎<{}", track.player.midi_outs().get(0)
.map(|port|port.short_name())
.transpose()?
.unwrap_or("(none)".into()));
Tui::push_x(scenes_w,
Tui::bg(track.color().rgb,
Tui::min_xy(w as u16, header_h,
Tui::to_south(name, timer))))
});
let content = Tui::fixed_y(
(view.size.h() as u16).saturating_sub(header_h),
col!((scene, pulses) in scenes.iter().zip(rows.iter().map(|row|row.0)) => {
let height = 1.max((pulses / PPQ) as u16);
let playing = scene.is_playing(tracks);
Tui::fixed_y(
height,
Tui::to_east(
Tui::to_east(
if playing { "" } else { " " },
Tui::bold(true, scene.name.read().unwrap().as_str())
),
row!((track, w) in cols.iter().map(|col|col.0).enumerate() => {
Tui::fixed_xy(w as u16, height, Layers::new(move |add|{
let mut bg = clip_bg;
match (tracks.get(track), scene.clips.get(track)) {
(Some(track), Some(Some(phrase))) => {
let name = &(phrase as &Arc<RwLock<Phrase>>).read().unwrap().name;
let name = format!("{}", name);
let max_w = name.len().min((w as usize).saturating_sub(2));
let color = phrase.read().unwrap().color;
bg = color.dark.rgb;
if let Some((_, Some(ref playing))) = track.player.play_phrase() {
if *playing.read().unwrap() == *phrase.read().unwrap() {
bg = color.light.rgb
}
};
add(&Tui::fixed_x(w as u16, Tui::push_x(1, &name.as_str()[0..max_w])))?;
},
_ => {}
};
//add(&Background(bg))
Ok(())
}))
})
)
)
}));
let color = TuiTheme::title_fg(view.arranger_focused());
lay!([
Tui::bg(bg.rgb, lay!(![
Tui::at_se(Tui::fill_xy(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!(![header, content]),
ArrangerCursor::from((view, factor)),
col!(![
ArrangerVerticalHeader::from(view),
ArrangerVerticalContent::from((view, factor)),
]),
ArrangerVerticalCursor::from((view, factor)),
])),
Tui::at_se(Tui::fill_xy(Tui::pull_x(1, Tui::fg(color, format!("{}x{}", view.size.w(), view.size.h()))))),
])
}
@ -259,7 +159,7 @@ render!(|self: ArrangerVerticalRowSeparator|custom_render(move|to: &mut TuiOutpu
})
}));
struct ArrangerCursor {
struct ArrangerVerticalCursor {
cols: Vec<(usize, usize)>,
rows: Vec<(usize, usize)>,
focused: bool,
@ -267,7 +167,7 @@ struct ArrangerCursor {
scenes_w: u16,
header_h: u16,
}
impl From<(&ArrangerTui, usize)> for ArrangerCursor {
impl From<(&ArrangerTui, usize)> for ArrangerVerticalCursor {
fn from ((state, factor): (&ArrangerTui, usize)) -> Self {
Self {
cols: track_widths(state.tracks()),
@ -279,7 +179,7 @@ impl From<(&ArrangerTui, usize)> for ArrangerCursor {
}
}
}
render!(|self: ArrangerCursor|custom_render(move|to: &mut TuiOutput|{
render!(|self: ArrangerVerticalCursor|custom_render(move|to: &mut TuiOutput|{
let area = to.area();
let focused = self.focused;
let selected = self.selected;
@ -334,10 +234,142 @@ render!(|self: ArrangerCursor|custom_render(move|to: &mut TuiOutput|{
})
}));
struct ArrangerVerticalHeader<'a> {
tracks: &'a Vec<ArrangerTrack>,
cols: Vec<(usize, usize)>,
focused: bool,
selected: ArrangerSelection,
scenes_w: u16,
header_h: u16,
timebase: &'a Arc<Timebase>,
current: &'a Arc<Moment>,
}
impl<'a> From<&'a ArrangerTui> for ArrangerVerticalHeader<'a> {
fn from (state: &'a ArrangerTui) -> Self {
Self {
tracks: &state.tracks,
cols: track_widths(state.tracks()),
focused: state.arranger_focused(),
selected: state.selected,
scenes_w: 3 + ArrangerScene::longest_name(state.scenes()) as u16,
header_h: 3,
timebase: state.clock().timebase(),
current: &state.clock().playhead,
}
}
}
render!(|self: ArrangerVerticalHeader<'a>|row!(
(track, w) in self.tracks.iter().zip(self.cols.iter().map(|col|col.0)) => {
// name and width of track
let name = track.name().read().unwrap();
let max_w = w.saturating_sub(1).min(name.len()).max(2);
let name = format!("{}", &name[0..max_w]);
let name = Tui::bold(true, name);
// beats elapsed
let elapsed = if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() {
let length = phrase.read().unwrap().length;
let elapsed = track.player.pulses_since_start().unwrap();
let elapsed = self.timebase.format_beats_1_short(
(elapsed as usize % length) as f64
);
format!("▎+{elapsed:>}")
} else {
String::from("")
};
// beats until switchover
let until_next = track.player.next_phrase().as_ref().map(|(t, _)|{
let target = t.pulse.get();
let current = self.current.pulse.get();
if target > current {
let remaining = target - current;
format!("▎-{:>}", self.timebase.format_beats_0_short(remaining))
} else {
String::new()
}
}).unwrap_or(String::from(""));
let timer = Tui::to_south(until_next, elapsed);
// name of active MIDI input
let _input = format!("▎>{}", track.player.midi_ins().get(0)
.map(|port|port.short_name())
.transpose()?
.unwrap_or("(none)".into()));
// name of active MIDI output
let _output = format!("▎<{}", track.player.midi_outs().get(0)
.map(|port|port.short_name())
.transpose()?
.unwrap_or("(none)".into()));
Tui::push_x(self.scenes_w,
Tui::bg(track.color().rgb,
Tui::min_xy(w as u16, self.header_h,
Tui::to_south(name, timer))))
}
));
struct ArrangerVerticalContent<'a> {
size: &'a Measure<Tui>,
scenes: &'a Vec<ArrangerScene>,
tracks: &'a Vec<ArrangerTrack>,
rows: Vec<(usize, usize)>,
cols: Vec<(usize, usize)>,
header_h: u16,
}
impl<'a> From<(&'a ArrangerTui, usize)> for ArrangerVerticalContent<'a> {
fn from ((state, factor): (&'a ArrangerTui, usize)) -> Self {
Self {
size: &state.size,
scenes: &state.scenes,
tracks: &state.tracks,
rows: ArrangerScene::ppqs(state.scenes(), factor),
cols: track_widths(state.tracks()),
header_h: 3,
}
}
}
render!(|self: ArrangerVerticalContent<'a>|Tui::fixed_y(
(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)) => {
let height = 1.max((pulses / PPQ) as u16);
let playing = scene.is_playing(self.tracks);
Tui::fixed_y(
height,
Tui::to_east(
Tui::to_east(
if playing { "" } else { " " },
Tui::bold(true, scene.name.read().unwrap().as_str())
),
row!((track, w) in self.cols.iter().map(|col|col.0).enumerate() => {
Tui::fixed_xy(w as u16, height, Layers::new(move |add|{
let mut bg = TuiTheme::border_bg();
match (self.tracks.get(track), scene.clips.get(track)) {
(Some(track), Some(Some(phrase))) => {
let name = &(phrase as &Arc<RwLock<Phrase>>).read().unwrap().name;
let name = format!("{}", name);
let max_w = name.len().min((w as usize).saturating_sub(2));
let color = phrase.read().unwrap().color;
bg = color.dark.rgb;
if let Some((_, Some(ref playing))) = track.player.play_phrase() {
if *playing.read().unwrap() == *phrase.read().unwrap() {
bg = color.light.rgb
}
};
add(&Tui::fixed_x(w as u16, Tui::push_x(1, &name.as_str()[0..max_w])))?;
},
_ => {}
};
//add(&Background(bg))
Ok(())
}))
})
)
)
})
));
pub fn arranger_content_horizontal (
view: &ArrangerTui,
) -> impl Render<Tui> + use<'_> {
todo!()
}
//let focused = view.arranger_focused();
//let _tracks = view.tracks();
//lay!(
@ -526,4 +558,4 @@ pub fn arranger_content_horizontal (
//}),
//)
//)
}
//}