mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
more arranger view refactors
This commit is contained in:
parent
99d8a0863e
commit
598319af35
2 changed files with 90 additions and 98 deletions
|
|
@ -14,8 +14,8 @@ pub use self::{
|
||||||
/// Prototypal case of implementor macro.
|
/// Prototypal case of implementor macro.
|
||||||
/// Saves 4loc per data pats.
|
/// Saves 4loc per data pats.
|
||||||
#[macro_export] macro_rules! from {
|
#[macro_export] macro_rules! from {
|
||||||
($(<$lt:lifetime>)?|$state:ident:$Source:ty|$Target:ty=$cb:expr) => {
|
($(<$($lt:lifetime),+>)?|$state:ident:$Source:ty|$Target:ty=$cb:expr) => {
|
||||||
impl $(<$lt>)? From<$Source> for $Target {
|
impl $(<$($lt),+>)? From<$Source> for $Target {
|
||||||
fn from ($state:$Source) -> Self { $cb }
|
fn from ($state:$Source) -> Self { $cb }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,60 +1,68 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
const HEADER_H: u16 = 3;
|
||||||
|
fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> {
|
||||||
|
let mut widths = vec![];
|
||||||
|
let mut total = 0;
|
||||||
|
for track in tracks.iter() {
|
||||||
|
let width = track.width;
|
||||||
|
widths.push((width, total));
|
||||||
|
total += width;
|
||||||
|
}
|
||||||
|
widths.push((0, total));
|
||||||
|
widths
|
||||||
|
}
|
||||||
|
fn tracks_with_widths (tracks: &[ArrangerTrack])
|
||||||
|
-> impl Iterator<Item = (usize, &ArrangerTrack, usize, usize)>
|
||||||
|
{
|
||||||
|
let mut x = 0;
|
||||||
|
tracks.iter().enumerate().map(move |(index, track)|{
|
||||||
|
let data = (index, track, x, x + track.width);
|
||||||
|
x += track.width;
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
pub struct ArrangerVHead<'a> {
|
pub struct ArrangerVHead<'a> {
|
||||||
focused: bool,
|
|
||||||
selected: ArrangerSelection,
|
|
||||||
scenes_w: u16,
|
scenes_w: u16,
|
||||||
header_h: u16,
|
|
||||||
timebase: &'a Arc<Timebase>,
|
timebase: &'a Arc<Timebase>,
|
||||||
current: &'a Arc<Moment>,
|
current: &'a Arc<Moment>,
|
||||||
tracks: Vec<(&'a ArrangerTrack, usize, usize)>,
|
tracks: &'a [ArrangerTrack],
|
||||||
}
|
}
|
||||||
from!(<'a>|state: &'a ArrangerTui|ArrangerVHead<'a> = Self { // A
|
from!(<'a>|state: &'a ArrangerTui|ArrangerVHead<'a> = Self { // A
|
||||||
focused: true,
|
tracks: state.tracks(),
|
||||||
selected: state.selected,
|
|
||||||
scenes_w: 3 + ArrangerScene::longest_name(state.scenes()) as u16,
|
|
||||||
header_h: 3,
|
|
||||||
timebase: state.clock().timebase(),
|
timebase: state.clock().timebase(),
|
||||||
current: &state.clock().playhead,
|
current: &state.clock().playhead,
|
||||||
tracks: track_widths(state.tracks())
|
scenes_w: 3 + ArrangerScene::longest_name(state.scenes()) as u16,
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, (a, b))|(&state.tracks()[i], *a, *b))
|
|
||||||
.collect(),
|
|
||||||
});
|
});
|
||||||
render!(<Tui>|self: ArrangerVHead<'a>|row!(
|
render!(<Tui>|self: ArrangerVHead<'a>|Tui::push_x(self.scenes_w, row!(
|
||||||
(track, w, _) in self.tracks.iter() => {
|
(_, track, x1, x2) in tracks_with_widths(self.tracks) => {
|
||||||
let name = Self::format_name(track, *w);
|
let (w, h) = (x2 - x1, HEADER_H);
|
||||||
let input = Self::format_input(track)?;
|
let color = track.color();
|
||||||
let elapsed = Self::format_elapsed(track, &self.timebase);
|
Tui::bg(color.base.rgb, Tui::min_xy(w as u16, h, Fixed::wh(w as u16, 5, col!([
|
||||||
let until_next = Self::format_until_next(track, &self.timebase, &self.current);
|
row!(![Tui::fg(color.light.rgb, "▎"), Self::format_name(track, w)]),
|
||||||
let output = Self::format_output(track)?;
|
row!(![Tui::fg(color.light.rgb, "▎"), Self::format_input(track)?]),
|
||||||
Tui::push_x(self.scenes_w,
|
row!(![Tui::fg(color.light.rgb, "▎"), Self::format_output(track)?]),
|
||||||
Tui::bg(track.color().base.rgb,
|
row!(![Tui::fg(color.light.rgb, "▎"), Self::format_elapsed(track, &self.timebase)]),
|
||||||
Tui::min_xy(*w as u16, self.header_h, row!([
|
row!(![Tui::fg(color.light.rgb, "▎"), Self::format_until_next(track, &self.current)]),
|
||||||
col!(!["▎", "▎", "▎", "▎", "▎", "▎",]),
|
]))))
|
||||||
col!(![name, input, output, elapsed, until_next, output])]))))
|
|
||||||
}
|
}
|
||||||
));
|
)));
|
||||||
impl<'a> ArrangerVHead<'a> {
|
impl<'a> ArrangerVHead<'a> {
|
||||||
/// name and width of track
|
/// name and width of track
|
||||||
fn format_name (track: &ArrangerTrack, w: usize) -> impl Render<Tui> {
|
fn format_name (track: &ArrangerTrack, w: usize) -> impl Render<Tui> {
|
||||||
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);
|
||||||
Tui::bold(true, Tui::fg(track.color.lightest.rgb, format!("▎{}", &name[0..max_w])));
|
let name = String::from(&name[0..max_w]);
|
||||||
|
Tui::bold(true, Tui::fg(track.color.lightest.rgb, name));
|
||||||
}
|
}
|
||||||
/// input port
|
/// input port
|
||||||
fn format_input (track: &ArrangerTrack) -> Usually<impl Render<Tui>> {
|
fn format_input (track: &ArrangerTrack) -> Usually<impl Render<Tui>> {
|
||||||
Ok(format!(">{}", track.player.midi_ins().get(0)
|
Ok(format!(">{}", track.player.midi_ins().get(0).map(|port|port.short_name())
|
||||||
.map(|port|port.short_name())
|
.transpose()?.unwrap_or("?".into())))
|
||||||
.transpose()?
|
|
||||||
.unwrap_or("(none)".into())))
|
|
||||||
}
|
}
|
||||||
/// output port
|
/// output port
|
||||||
fn format_output (track: &ArrangerTrack) -> Usually<impl Render<Tui>> {
|
fn format_output (track: &ArrangerTrack) -> Usually<impl Render<Tui>> {
|
||||||
Ok(format!("<{}", track.player.midi_outs().get(0)
|
Ok(format!("<{}", track.player.midi_outs().get(0).map(|port|port.short_name())
|
||||||
.map(|port|port.short_name())
|
.transpose()?.unwrap_or("?".into())))
|
||||||
.transpose()?
|
|
||||||
.unwrap_or("(none)".into())))
|
|
||||||
}
|
}
|
||||||
/// beats elapsed
|
/// beats elapsed
|
||||||
fn format_elapsed (track: &ArrangerTrack, timebase: &Arc<Timebase>) -> impl Render<Tui> {
|
fn format_elapsed (track: &ArrangerTrack, timebase: &Arc<Timebase>) -> impl Render<Tui> {
|
||||||
|
|
@ -70,9 +78,10 @@ impl<'a> ArrangerVHead<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// beats until switchover
|
/// beats until switchover
|
||||||
fn format_until_next (track: &ArrangerTrack, timebase: &Arc<Timebase>, current: &Arc<Moment>)
|
fn format_until_next (track: &ArrangerTrack, current: &Arc<Moment>)
|
||||||
-> Option<impl Render<Tui>>
|
-> Option<impl Render<Tui>>
|
||||||
{
|
{
|
||||||
|
let timebase = ¤t.timebase;
|
||||||
track.player.next_phrase().as_ref().map(|(t, _)|{
|
track.player.next_phrase().as_ref().map(|(t, _)|{
|
||||||
let target = t.pulse.get();
|
let target = t.pulse.get();
|
||||||
let current = current.pulse.get();
|
let current = current.pulse.get();
|
||||||
|
|
@ -128,22 +137,18 @@ render!(<Tui>|self: ArrangerVRowSep|render(move|to: &mut TuiOutput|{
|
||||||
pub struct ArrangerVCursor {
|
pub struct ArrangerVCursor {
|
||||||
cols: Vec<(usize, usize)>,
|
cols: Vec<(usize, usize)>,
|
||||||
rows: Vec<(usize, usize)>,
|
rows: Vec<(usize, usize)>,
|
||||||
focused: bool,
|
|
||||||
selected: ArrangerSelection,
|
selected: ArrangerSelection,
|
||||||
scenes_w: u16,
|
scenes_w: u16,
|
||||||
header_h: u16,
|
|
||||||
}
|
}
|
||||||
from!(|args:(&ArrangerTui, usize)|ArrangerVCursor = Self {
|
from!(|args:(&ArrangerTui, usize)|ArrangerVCursor = Self {
|
||||||
cols: track_widths(args.0.tracks()),
|
cols: track_widths(args.0.tracks()),
|
||||||
rows: ArrangerScene::ppqs(args.0.scenes(), args.1),
|
rows: ArrangerScene::ppqs(args.0.scenes(), args.1),
|
||||||
focused: true,
|
|
||||||
selected: args.0.selected(),
|
selected: args.0.selected(),
|
||||||
scenes_w: 3 + ArrangerScene::longest_name(args.0.scenes()) as u16,
|
scenes_w: 3 + ArrangerScene::longest_name(args.0.scenes()) as u16,
|
||||||
header_h: 3,
|
|
||||||
});
|
});
|
||||||
render!(<Tui>|self: ArrangerVCursor|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 = true;
|
||||||
let selected = self.selected;
|
let selected = self.selected;
|
||||||
let get_track_area = |t: usize| [
|
let get_track_area = |t: usize| [
|
||||||
self.scenes_w + area.x() + self.cols[t].1 as u16,
|
self.scenes_w + area.x() + self.cols[t].1 as u16,
|
||||||
|
|
@ -153,13 +158,13 @@ render!(<Tui>|self: ArrangerVCursor|render(move|to: &mut TuiOutput|{
|
||||||
];
|
];
|
||||||
let get_scene_area = |s: usize| [
|
let get_scene_area = |s: usize| [
|
||||||
area.x(),
|
area.x(),
|
||||||
self.header_h + area.y() + (self.rows[s].1 / PPQ) as u16,
|
HEADER_H + area.y() + (self.rows[s].1 / PPQ) as u16,
|
||||||
area.w(),
|
area.w(),
|
||||||
(self.rows[s].0 / PPQ) as u16
|
(self.rows[s].0 / PPQ) as u16
|
||||||
];
|
];
|
||||||
let get_clip_area = |t: usize, s: usize| [
|
let get_clip_area = |t: usize, s: usize| [
|
||||||
self.scenes_w + area.x() + self.cols[t].1 as u16,
|
self.scenes_w + area.x() + self.cols[t].1 as u16,
|
||||||
self.header_h + area.y() + (self.rows[s].1/PPQ) as u16,
|
HEADER_H + area.y() + (self.rows[s].1/PPQ) as u16,
|
||||||
self.cols[t].0 as u16,
|
self.cols[t].0 as u16,
|
||||||
(self.rows[s].0 / PPQ) as u16
|
(self.rows[s].0 / PPQ) as u16
|
||||||
];
|
];
|
||||||
|
|
@ -194,78 +199,65 @@ render!(<Tui>|self: ArrangerVCursor|render(move|to: &mut TuiOutput|{
|
||||||
}
|
}
|
||||||
Ok(if focused {
|
Ok(if focused {
|
||||||
to.render_in(if let Some(clip_area) = clip_area { clip_area }
|
to.render_in(if let Some(clip_area) = clip_area { clip_area }
|
||||||
else if let Some(track_area) = track_area { track_area.clip_h(self.header_h) }
|
else if let Some(track_area) = track_area { track_area.clip_h(HEADER_H) }
|
||||||
else if let Some(scene_area) = scene_area { scene_area.clip_w(self.scenes_w) }
|
else if let Some(scene_area) = scene_area { scene_area.clip_w(self.scenes_w) }
|
||||||
else { area.clip_w(self.scenes_w).clip_h(self.header_h) }, &CORNERS)?
|
else { area.clip_w(self.scenes_w).clip_h(HEADER_H) }, &CORNERS)?
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
pub struct ArrangerVBody<'a> {
|
pub struct ArrangerVBody<'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>,
|
||||||
rows: Vec<(usize, usize)>,
|
rows: Vec<(usize, usize)>,
|
||||||
cols: Vec<(usize, usize)>,
|
|
||||||
header_h: u16,
|
|
||||||
}
|
}
|
||||||
|
from!(<'a>|args:(&'a ArrangerTui, usize)|ArrangerVBody<'a> = Self {
|
||||||
impl<'a> From<(&'a ArrangerTui, usize)> for ArrangerVBody<'a> {
|
size: &args.0.size,
|
||||||
fn from ((state, factor): (&'a ArrangerTui, usize)) -> Self {
|
scenes: &args.0.scenes,
|
||||||
Self {
|
tracks: &args.0.tracks,
|
||||||
size: &state.size,
|
rows: ArrangerScene::ppqs(args.0.scenes(), args.1),
|
||||||
scenes: &state.scenes,
|
});
|
||||||
tracks: &state.tracks,
|
|
||||||
rows: ArrangerScene::ppqs(state.scenes(), factor),
|
|
||||||
cols: track_widths(state.tracks()),
|
|
||||||
header_h: 3,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render!(<Tui>|self: ArrangerVBody<'a>|Fixed::h(
|
render!(<Tui>|self: ArrangerVBody<'a>|Fixed::h(
|
||||||
(self.size.h() as u16).saturating_sub(self.header_h),
|
(self.size.h() as u16).saturating_sub(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);
|
||||||
let playing = scene.is_playing(self.tracks);
|
let playing = scene.is_playing(self.tracks);
|
||||||
Fixed::h(height, row!([
|
Fixed::h(height, row!([
|
||||||
if playing { "▶ " } else { " " },
|
Self::format_play(playing),
|
||||||
Tui::bold(true, scene.name.read().unwrap().as_str()),
|
Tui::bold(true, scene.name.read().unwrap().as_str()),
|
||||||
row!((track, w) in self.cols.iter().map(|col|col.0).enumerate() => {
|
row!((index, _, x1, x2) in tracks_with_widths(self.tracks) => {
|
||||||
Fixed::wh(w as u16, height, Layers::new(move |add|{
|
Self::format_clip(&self.tracks, &scene, index, (x2 - x1) as u16, height)
|
||||||
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(&Fixed::w(w as u16, Tui::push_x(1, &name.as_str()[0..max_w])))?;
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
//add(&Background(bg))
|
|
||||||
Ok(())
|
|
||||||
}))
|
|
||||||
})])
|
})])
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
));
|
));
|
||||||
|
impl<'a> ArrangerVBody<'a> {
|
||||||
fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> {
|
fn format_play (playing: bool) -> &'static str {
|
||||||
let mut widths = vec![];
|
if playing { "▶ " } else { " " }
|
||||||
let mut total = 0;
|
}
|
||||||
for track in tracks.iter() {
|
fn format_clip (tracks: &'a [ArrangerTrack], scene: &'a ArrangerScene, i: usize, w: u16, h: u16)
|
||||||
let width = track.width;
|
-> impl Render<Tui> + use<'a>
|
||||||
widths.push((width, total));
|
{
|
||||||
total += width;
|
Fixed::wh(w, h, Layers::new(move |add|{
|
||||||
|
let mut bg = TuiTheme::border_bg();
|
||||||
|
match (tracks.get(i), scene.clips.get(i)) {
|
||||||
|
(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(&Fixed::w(w as u16, Tui::push_x(1, &name.as_str()[0..max_w])))?;
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
//add(&Background(bg))
|
||||||
|
Ok(())
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
widths.push((0, total));
|
|
||||||
widths
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue