mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
break down arranger vertical mode into modules
This commit is contained in:
parent
fc053bc754
commit
48ec9af019
6 changed files with 308 additions and 274 deletions
|
|
@ -1,6 +1,14 @@
|
|||
use crate::*;
|
||||
|
||||
mod v_clips; pub(crate) use self::v_clips::*;
|
||||
mod v_cursor; pub(crate) use self::v_cursor::*;
|
||||
mod v_head; pub(crate) use self::v_head::*;
|
||||
mod v_io; pub(crate) use self::v_io::*;
|
||||
mod v_sep; pub(crate) use self::v_sep::*;
|
||||
|
||||
const HEADER_H: u16 = 5;
|
||||
const SCENES_W_OFFSET: u16 = 3;
|
||||
|
||||
impl ArrangerTui {
|
||||
pub fn render_mode_v (state: &ArrangerTui, factor: usize) -> impl Render<Tui> + use<'_> {
|
||||
lay!([
|
||||
|
|
@ -16,278 +24,4 @@ impl ArrangerTui {
|
|||
])
|
||||
}
|
||||
}
|
||||
pub struct ArrangerVColSep { fg: Color, cols: Vec<(usize, usize)>, scenes_w: u16, }
|
||||
from!(|state:&ArrangerTui|ArrangerVColSep = Self {
|
||||
fg: TuiTheme::separator_fg(false),
|
||||
cols: ArrangerTrack::widths(state.tracks()),
|
||||
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(state.scenes()) as u16,
|
||||
});
|
||||
render!(<Tui>|self: ArrangerVColSep|render(move|to: &mut TuiOutput|{
|
||||
let style = Some(Style::default().fg(self.fg));
|
||||
Ok(for x in self.cols.iter().map(|col|col.1) {
|
||||
let x = self.scenes_w + to.area().x() + x as u16;
|
||||
for y in to.area().y()..to.area().y2() {
|
||||
to.blit(&"▎", x, y, style);
|
||||
}
|
||||
})
|
||||
}));
|
||||
pub struct ArrangerVRowSep { fg: Color, rows: Vec<(usize, usize)>, }
|
||||
from!(|args:(&ArrangerTui, usize)|ArrangerVRowSep = Self {
|
||||
fg: TuiTheme::separator_fg(false),
|
||||
rows: ArrangerScene::ppqs(args.0.scenes(), args.1),
|
||||
});
|
||||
render!(<Tui>|self: ArrangerVRowSep|render(move|to: &mut TuiOutput|{
|
||||
Ok(for y in self.rows.iter().map(|row|row.1) {
|
||||
let y = to.area().y() + (y / PPQ) as u16 + 1;
|
||||
if y >= to.buffer.area.height { break }
|
||||
for x in to.area().x()..to.area().x2().saturating_sub(2) {
|
||||
if x < to.buffer.area.x && y < to.buffer.area.y {
|
||||
let cell = to.buffer.get_mut(x, y);
|
||||
cell.modifier = Modifier::UNDERLINED;
|
||||
cell.underline_color = self.fg;
|
||||
}
|
||||
}
|
||||
})
|
||||
}));
|
||||
pub struct ArrangerVHead<'a> {
|
||||
scenes_w: u16,
|
||||
timebase: &'a Arc<Timebase>,
|
||||
current: &'a Arc<Moment>,
|
||||
tracks: &'a [ArrangerTrack],
|
||||
}
|
||||
from!(<'a>|state: &'a ArrangerTui|ArrangerVHead<'a> = Self { // A
|
||||
tracks: state.tracks(),
|
||||
timebase: state.clock().timebase(),
|
||||
current: &state.clock().playhead,
|
||||
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(state.scenes()) as u16,
|
||||
});
|
||||
render!(<Tui>|self: ArrangerVHead<'a>|Tui::push_x(self.scenes_w, row!(
|
||||
(_, track, x1, x2) in ArrangerTrack::with_widths(self.tracks) => {
|
||||
let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H);
|
||||
let color = track.color();
|
||||
fn row <T: Render<Tui>> (color: ItemPalette, field: &T) -> impl Render<Tui> + use<'_, T> {
|
||||
row!([
|
||||
Tui::fg(color.light.rgb, "▎"),
|
||||
Tui::fg(color.lightest.rgb, field)
|
||||
])
|
||||
}
|
||||
Tui::bg(color.base.rgb, Tui::min_xy(w as u16, h, Fixed::wh(w as u16, 5, col!([
|
||||
row(color, &Self::format_name(track, w)),
|
||||
row(color, &Self::format_input(track)?),
|
||||
row(color, &Self::format_output(track)?),
|
||||
row(color, &Self::format_elapsed(track, &self.timebase)),
|
||||
row(color, &Self::format_until_next(track, &self.current)),
|
||||
]))))
|
||||
}
|
||||
)));
|
||||
|
||||
impl<'a> ArrangerVHead<'a> {
|
||||
/// name and width of track
|
||||
fn format_name (track: &ArrangerTrack, w: usize) -> impl Render<Tui> {
|
||||
let name = track.name().read().unwrap().clone();
|
||||
Tui::bold(true, Tui::fg(track.color.lightest.rgb, name))
|
||||
}
|
||||
/// input port
|
||||
fn format_input (track: &ArrangerTrack) -> Usually<impl Render<Tui>> {
|
||||
Ok(format!(">{}", track.player.midi_ins().get(0).map(|port|port.short_name())
|
||||
.transpose()?.unwrap_or("?".into())))
|
||||
}
|
||||
/// output port
|
||||
fn format_output (track: &ArrangerTrack) -> Usually<impl Render<Tui>> {
|
||||
Ok(format!("<{}", track.player.midi_outs().get(0).map(|port|port.short_name())
|
||||
.transpose()?.unwrap_or("?".into())))
|
||||
}
|
||||
/// beats elapsed
|
||||
fn format_elapsed (track: &ArrangerTrack, timebase: &Arc<Timebase>) -> impl Render<Tui> {
|
||||
let mut result = String::new();
|
||||
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
|
||||
);
|
||||
result = format!("+{elapsed:>}")
|
||||
}
|
||||
result
|
||||
}
|
||||
/// beats until switchover
|
||||
fn format_until_next (track: &ArrangerTrack, current: &Arc<Moment>)
|
||||
-> Option<impl Render<Tui>>
|
||||
{
|
||||
let timebase = ¤t.timebase;
|
||||
let mut result = String::new();
|
||||
if let Some((t, _)) = track.player.next_phrase().as_ref() {
|
||||
let target = t.pulse.get();
|
||||
let current = current.pulse.get();
|
||||
if target > current {
|
||||
let remaining = target - current;
|
||||
result = format!("-{:>}", timebase.format_beats_0_short(remaining))
|
||||
}
|
||||
}
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArrangerVIns<'a> {
|
||||
size: &'a Measure<Tui>,
|
||||
tracks: &'a Vec<ArrangerTrack>,
|
||||
}
|
||||
from!(<'a>|args: &'a ArrangerTui|ArrangerVIns<'a> = Self {
|
||||
size: &args.size,
|
||||
tracks: &args.tracks,
|
||||
});
|
||||
render!(<Tui>|self: ArrangerVIns<'a>|"");
|
||||
|
||||
pub struct ArrangerVOuts<'a> {
|
||||
size: &'a Measure<Tui>,
|
||||
tracks: &'a Vec<ArrangerTrack>,
|
||||
}
|
||||
from!(<'a>|args: &'a ArrangerTui|ArrangerVOuts<'a> = Self {
|
||||
size: &args.size,
|
||||
tracks: &args.tracks,
|
||||
});
|
||||
render!(<Tui>|self: ArrangerVOuts<'a>|"");
|
||||
|
||||
pub struct ArrangerVClocks<'a> {
|
||||
size: &'a Measure<Tui>,
|
||||
tracks: &'a Vec<ArrangerTrack>,
|
||||
}
|
||||
from!(<'a>|args: &'a ArrangerTui|ArrangerVClocks<'a> = Self {
|
||||
size: &args.size,
|
||||
tracks: &args.tracks,
|
||||
});
|
||||
render!(<Tui>|self: ArrangerVClocks<'a>|"");
|
||||
|
||||
pub struct ArrangerVClips<'a> {
|
||||
size: &'a Measure<Tui>,
|
||||
scenes: &'a Vec<ArrangerScene>,
|
||||
tracks: &'a Vec<ArrangerTrack>,
|
||||
rows: Vec<(usize, usize)>,
|
||||
}
|
||||
from!(<'a>|args:(&'a ArrangerTui, usize)|ArrangerVClips<'a> = Self {
|
||||
size: &args.0.size,
|
||||
scenes: &args.0.scenes,
|
||||
tracks: &args.0.tracks,
|
||||
rows: ArrangerScene::ppqs(args.0.scenes(), args.1),
|
||||
});
|
||||
render!(<Tui>|self: ArrangerVClips<'a>|Fill::wh(
|
||||
col!((scene, pulses) in self.scenes.iter().zip(self.rows.iter().map(|row|row.0)) => {
|
||||
Self::format_scene(&self.tracks, scene, pulses)
|
||||
})
|
||||
));
|
||||
impl<'a> ArrangerVClips<'a> {
|
||||
fn format_scene (
|
||||
tracks: &'a [ArrangerTrack], scene: &'a ArrangerScene, pulses: usize
|
||||
) -> impl Render<Tui> + use<'a> {
|
||||
let height = 1.max((pulses / PPQ) as u16);
|
||||
let playing = scene.is_playing(tracks);
|
||||
Fixed::h(height, row!([
|
||||
if playing { "▶ " } else { " " },
|
||||
Tui::bold(true, scene.name.read().unwrap().as_str()),
|
||||
row!((index, track, x1, x2) in ArrangerTrack::with_widths(tracks) => {
|
||||
Self::format_clip(scene, index, track, (x2 - x1) as u16, height)
|
||||
})])
|
||||
)
|
||||
}
|
||||
fn format_clip (
|
||||
scene: &'a ArrangerScene, index: usize, track: &'a ArrangerTrack, w: u16, h: u16
|
||||
) -> impl Render<Tui> + use<'a> {
|
||||
Fixed::wh(w, h, Layers::new(move |add|{
|
||||
let mut bg = TuiTheme::border_bg();
|
||||
if let Some(Some(phrase)) = scene.clips.get(index) {
|
||||
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::push_x(1, Tui::bg(bg,
|
||||
Tui::push_x(1, Fixed::w(w as u16, &name.as_str()[0..max_w])))
|
||||
))?;
|
||||
}
|
||||
//add(&Background(bg))
|
||||
Ok(())
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArrangerVCursor {
|
||||
cols: Vec<(usize, usize)>,
|
||||
rows: Vec<(usize, usize)>,
|
||||
color: ItemPalette,
|
||||
reticle: Reticle,
|
||||
selected: ArrangerSelection,
|
||||
scenes_w: u16,
|
||||
}
|
||||
from!(|args:(&ArrangerTui, usize)|ArrangerVCursor = Self {
|
||||
cols: ArrangerTrack::widths(args.0.tracks()),
|
||||
rows: ArrangerScene::ppqs(args.0.scenes(), args.1),
|
||||
selected: args.0.selected(),
|
||||
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(args.0.scenes()) as u16,
|
||||
color: args.0.color,
|
||||
reticle: Reticle(Style {
|
||||
fg: Some(args.0.color.lighter.rgb),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::DIM
|
||||
}),
|
||||
});
|
||||
render!(<Tui>|self: ArrangerVCursor|render(move|to: &mut TuiOutput|{
|
||||
let area = to.area();
|
||||
let focused = true;
|
||||
let selected = self.selected;
|
||||
let get_track_area = |t: usize| [
|
||||
self.scenes_w + area.x() + self.cols[t].1 as u16, area.y(),
|
||||
self.cols[t].0 as u16, area.h(),
|
||||
];
|
||||
let get_scene_area = |s: usize| [
|
||||
area.x(), HEADER_H + area.y() + (self.rows[s].1 / PPQ) as u16,
|
||||
area.w(), (self.rows[s].0 / PPQ) as u16
|
||||
];
|
||||
let get_clip_area = |t: usize, s: usize| [
|
||||
(self.scenes_w + area.x() + self.cols[t].1 as u16).saturating_sub(1),
|
||||
HEADER_H + area.y() + (self.rows[s].1/PPQ) as u16,
|
||||
self.cols[t].0 as u16 + 2,
|
||||
(self.rows[s].0 / PPQ) as u16
|
||||
];
|
||||
let mut track_area: Option<[u16;4]> = None;
|
||||
let mut scene_area: Option<[u16;4]> = None;
|
||||
let mut clip_area: Option<[u16;4]> = None;
|
||||
let area = match selected {
|
||||
ArrangerSelection::Mix => area,
|
||||
ArrangerSelection::Track(t) => {
|
||||
track_area = Some(get_track_area(t));
|
||||
area
|
||||
},
|
||||
ArrangerSelection::Scene(s) => {
|
||||
scene_area = Some(get_scene_area(s));
|
||||
area
|
||||
},
|
||||
ArrangerSelection::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));
|
||||
area
|
||||
},
|
||||
};
|
||||
let bg = self.color.lighter.rgb;//Color::Rgb(0, 255, 0);
|
||||
if let Some([x, y, width, height]) = track_area {
|
||||
to.fill_fg([x, y, 1, height], bg);
|
||||
to.fill_fg([x + width, y, 1, height], bg);
|
||||
}
|
||||
if let Some([_, y, _, height]) = scene_area {
|
||||
to.fill_ul([area.x(), y - 1, area.w(), 1], bg);
|
||||
to.fill_ul([area.x(), y + height - 1, area.w(), 1], bg);
|
||||
}
|
||||
Ok(if focused {
|
||||
to.render_in(if let Some(clip_area) = clip_area { clip_area }
|
||||
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 { area.clip_w(self.scenes_w).clip_h(HEADER_H) }, &self.reticle)?
|
||||
})
|
||||
}));
|
||||
|
|
|
|||
62
crates/tek/src/tui/arranger_mode_v/v_clips.rs
Normal file
62
crates/tek/src/tui/arranger_mode_v/v_clips.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
use crate::*;
|
||||
use super::*;
|
||||
|
||||
pub struct ArrangerVClips<'a> {
|
||||
size: &'a Measure<Tui>,
|
||||
scenes: &'a Vec<ArrangerScene>,
|
||||
tracks: &'a Vec<ArrangerTrack>,
|
||||
rows: Vec<(usize, usize)>,
|
||||
}
|
||||
|
||||
from!(<'a>|args:(&'a ArrangerTui, usize)|ArrangerVClips<'a> = Self {
|
||||
size: &args.0.size,
|
||||
scenes: &args.0.scenes,
|
||||
tracks: &args.0.tracks,
|
||||
rows: ArrangerScene::ppqs(args.0.scenes(), args.1),
|
||||
});
|
||||
|
||||
render!(<Tui>|self: ArrangerVClips<'a>|Fill::wh(
|
||||
col!((scene, pulses) in self.scenes.iter().zip(self.rows.iter().map(|row|row.0)) => {
|
||||
Self::format_scene(&self.tracks, scene, pulses)
|
||||
})
|
||||
));
|
||||
|
||||
impl<'a> ArrangerVClips<'a> {
|
||||
fn format_scene (
|
||||
tracks: &'a [ArrangerTrack], scene: &'a ArrangerScene, pulses: usize
|
||||
) -> impl Render<Tui> + use<'a> {
|
||||
let height = 1.max((pulses / PPQ) as u16);
|
||||
let playing = scene.is_playing(tracks);
|
||||
Fixed::h(height, row!([
|
||||
if playing { "▶ " } else { " " },
|
||||
Tui::bold(true, scene.name.read().unwrap().as_str()),
|
||||
row!((index, track, x1, x2) in ArrangerTrack::with_widths(tracks) => {
|
||||
Self::format_clip(scene, index, track, (x2 - x1) as u16, height)
|
||||
})])
|
||||
)
|
||||
}
|
||||
fn format_clip (
|
||||
scene: &'a ArrangerScene, index: usize, track: &'a ArrangerTrack, w: u16, h: u16
|
||||
) -> impl Render<Tui> + use<'a> {
|
||||
Fixed::wh(w, h, Layers::new(move |add|{
|
||||
let mut bg = TuiTheme::border_bg();
|
||||
if let Some(Some(phrase)) = scene.clips.get(index) {
|
||||
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::push_x(1, Tui::bg(bg,
|
||||
Tui::push_x(1, Fixed::w(w as u16, &name.as_str()[0..max_w])))
|
||||
))?;
|
||||
}
|
||||
//add(&Background(bg))
|
||||
Ok(())
|
||||
}))
|
||||
}
|
||||
}
|
||||
81
crates/tek/src/tui/arranger_mode_v/v_cursor.rs
Normal file
81
crates/tek/src/tui/arranger_mode_v/v_cursor.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
use crate::*;
|
||||
use super::*;
|
||||
|
||||
pub struct ArrangerVCursor {
|
||||
cols: Vec<(usize, usize)>,
|
||||
rows: Vec<(usize, usize)>,
|
||||
color: ItemPalette,
|
||||
reticle: Reticle,
|
||||
selected: ArrangerSelection,
|
||||
scenes_w: u16,
|
||||
}
|
||||
|
||||
from!(|args:(&ArrangerTui, usize)|ArrangerVCursor = Self {
|
||||
cols: ArrangerTrack::widths(args.0.tracks()),
|
||||
rows: ArrangerScene::ppqs(args.0.scenes(), args.1),
|
||||
selected: args.0.selected(),
|
||||
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(args.0.scenes()) as u16,
|
||||
color: args.0.color,
|
||||
reticle: Reticle(Style {
|
||||
fg: Some(args.0.color.lighter.rgb),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::DIM
|
||||
}),
|
||||
});
|
||||
|
||||
render!(<Tui>|self: ArrangerVCursor|render(move|to: &mut TuiOutput|{
|
||||
let area = to.area();
|
||||
let focused = true;
|
||||
let selected = self.selected;
|
||||
let get_track_area = |t: usize| [
|
||||
self.scenes_w + area.x() + self.cols[t].1 as u16, area.y(),
|
||||
self.cols[t].0 as u16, area.h(),
|
||||
];
|
||||
let get_scene_area = |s: usize| [
|
||||
area.x(), HEADER_H + area.y() + (self.rows[s].1 / PPQ) as u16,
|
||||
area.w(), (self.rows[s].0 / PPQ) as u16
|
||||
];
|
||||
let get_clip_area = |t: usize, s: usize| [
|
||||
(self.scenes_w + area.x() + self.cols[t].1 as u16).saturating_sub(1),
|
||||
HEADER_H + area.y() + (self.rows[s].1/PPQ) as u16,
|
||||
self.cols[t].0 as u16 + 2,
|
||||
(self.rows[s].0 / PPQ) as u16
|
||||
];
|
||||
let mut track_area: Option<[u16;4]> = None;
|
||||
let mut scene_area: Option<[u16;4]> = None;
|
||||
let mut clip_area: Option<[u16;4]> = None;
|
||||
let area = match selected {
|
||||
ArrangerSelection::Mix => area,
|
||||
ArrangerSelection::Track(t) => {
|
||||
track_area = Some(get_track_area(t));
|
||||
area
|
||||
},
|
||||
ArrangerSelection::Scene(s) => {
|
||||
scene_area = Some(get_scene_area(s));
|
||||
area
|
||||
},
|
||||
ArrangerSelection::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));
|
||||
area
|
||||
},
|
||||
};
|
||||
let bg = self.color.lighter.rgb;//Color::Rgb(0, 255, 0);
|
||||
if let Some([x, y, width, height]) = track_area {
|
||||
to.fill_fg([x, y, 1, height], bg);
|
||||
to.fill_fg([x + width, y, 1, height], bg);
|
||||
}
|
||||
if let Some([_, y, _, height]) = scene_area {
|
||||
to.fill_ul([area.x(), y - 1, area.w(), 1], bg);
|
||||
to.fill_ul([area.x(), y + height - 1, area.w(), 1], bg);
|
||||
}
|
||||
Ok(if focused {
|
||||
to.render_in(if let Some(clip_area) = clip_area { clip_area }
|
||||
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 { area.clip_w(self.scenes_w).clip_h(HEADER_H) }, &self.reticle)?
|
||||
})
|
||||
}));
|
||||
83
crates/tek/src/tui/arranger_mode_v/v_head.rs
Normal file
83
crates/tek/src/tui/arranger_mode_v/v_head.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
use crate::*;
|
||||
use super::*;
|
||||
|
||||
pub struct ArrangerVHead<'a> {
|
||||
scenes_w: u16,
|
||||
timebase: &'a Arc<Timebase>,
|
||||
current: &'a Arc<Moment>,
|
||||
tracks: &'a [ArrangerTrack],
|
||||
}
|
||||
|
||||
from!(<'a>|state: &'a ArrangerTui|ArrangerVHead<'a> = Self { // A
|
||||
tracks: state.tracks(),
|
||||
timebase: state.clock().timebase(),
|
||||
current: &state.clock().playhead,
|
||||
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(state.scenes()) as u16,
|
||||
});
|
||||
|
||||
render!(<Tui>|self: ArrangerVHead<'a>|Tui::push_x(self.scenes_w, row!(
|
||||
(_, track, x1, x2) in ArrangerTrack::with_widths(self.tracks) => {
|
||||
let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H);
|
||||
let color = track.color();
|
||||
fn row <T: Render<Tui>> (color: ItemPalette, field: &T) -> impl Render<Tui> + use<'_, T> {
|
||||
row!([
|
||||
Tui::fg(color.light.rgb, "▎"),
|
||||
Tui::fg(color.lightest.rgb, field)
|
||||
])
|
||||
}
|
||||
Tui::bg(color.base.rgb, Tui::min_xy(w as u16, h, Fixed::wh(w as u16, 5, col!([
|
||||
row(color, &Self::format_name(track, w)),
|
||||
row(color, &Self::format_input(track)?),
|
||||
row(color, &Self::format_output(track)?),
|
||||
row(color, &Self::format_elapsed(track, &self.timebase)),
|
||||
row(color, &Self::format_until_next(track, &self.current)),
|
||||
]))))
|
||||
}
|
||||
)));
|
||||
|
||||
impl<'a> ArrangerVHead<'a> {
|
||||
/// name and width of track
|
||||
fn format_name (track: &ArrangerTrack, w: usize) -> impl Render<Tui> {
|
||||
let name = track.name().read().unwrap().clone();
|
||||
Tui::bold(true, Tui::fg(track.color.lightest.rgb, name))
|
||||
}
|
||||
/// input port
|
||||
fn format_input (track: &ArrangerTrack) -> Usually<impl Render<Tui>> {
|
||||
Ok(format!(">{}", track.player.midi_ins().get(0).map(|port|port.short_name())
|
||||
.transpose()?.unwrap_or("?".into())))
|
||||
}
|
||||
/// output port
|
||||
fn format_output (track: &ArrangerTrack) -> Usually<impl Render<Tui>> {
|
||||
Ok(format!("<{}", track.player.midi_outs().get(0).map(|port|port.short_name())
|
||||
.transpose()?.unwrap_or("?".into())))
|
||||
}
|
||||
/// beats elapsed
|
||||
fn format_elapsed (track: &ArrangerTrack, timebase: &Arc<Timebase>) -> impl Render<Tui> {
|
||||
let mut result = String::new();
|
||||
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
|
||||
);
|
||||
result = format!("+{elapsed:>}")
|
||||
}
|
||||
result
|
||||
}
|
||||
/// beats until switchover
|
||||
fn format_until_next (track: &ArrangerTrack, current: &Arc<Moment>)
|
||||
-> Option<impl Render<Tui>>
|
||||
{
|
||||
let timebase = ¤t.timebase;
|
||||
let mut result = String::new();
|
||||
if let Some((t, _)) = track.player.next_phrase().as_ref() {
|
||||
let target = t.pulse.get();
|
||||
let current = current.pulse.get();
|
||||
if target > current {
|
||||
let remaining = target - current;
|
||||
result = format!("-{:>}", timebase.format_beats_0_short(remaining))
|
||||
}
|
||||
}
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
26
crates/tek/src/tui/arranger_mode_v/v_io.rs
Normal file
26
crates/tek/src/tui/arranger_mode_v/v_io.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
use crate::*;
|
||||
use super::*;
|
||||
|
||||
pub struct ArrangerVIns<'a> {
|
||||
size: &'a Measure<Tui>,
|
||||
tracks: &'a Vec<ArrangerTrack>,
|
||||
}
|
||||
|
||||
from!(<'a>|args: &'a ArrangerTui|ArrangerVIns<'a> = Self {
|
||||
size: &args.size,
|
||||
tracks: &args.tracks,
|
||||
});
|
||||
|
||||
render!(<Tui>|self: ArrangerVIns<'a>|"");
|
||||
|
||||
pub struct ArrangerVOuts<'a> {
|
||||
size: &'a Measure<Tui>,
|
||||
tracks: &'a Vec<ArrangerTrack>,
|
||||
}
|
||||
|
||||
from!(<'a>|args: &'a ArrangerTui|ArrangerVOuts<'a> = Self {
|
||||
size: &args.size,
|
||||
tracks: &args.tracks,
|
||||
});
|
||||
|
||||
render!(<Tui>|self: ArrangerVOuts<'a>|"");
|
||||
48
crates/tek/src/tui/arranger_mode_v/v_sep.rs
Normal file
48
crates/tek/src/tui/arranger_mode_v/v_sep.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
use crate::*;
|
||||
use super::*;
|
||||
|
||||
pub struct ArrangerVColSep {
|
||||
fg: Color,
|
||||
cols: Vec<(usize, usize)>,
|
||||
scenes_w: u16
|
||||
}
|
||||
|
||||
from!(|state:&ArrangerTui|ArrangerVColSep = Self {
|
||||
fg: TuiTheme::separator_fg(false),
|
||||
cols: ArrangerTrack::widths(state.tracks()),
|
||||
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(state.scenes()) as u16,
|
||||
});
|
||||
|
||||
render!(<Tui>|self: ArrangerVColSep|render(move|to: &mut TuiOutput|{
|
||||
let style = Some(Style::default().fg(self.fg));
|
||||
Ok(for x in self.cols.iter().map(|col|col.1) {
|
||||
let x = self.scenes_w + to.area().x() + x as u16;
|
||||
for y in to.area().y()..to.area().y2() {
|
||||
to.blit(&"▎", x, y, style);
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
||||
pub struct ArrangerVRowSep {
|
||||
fg: Color,
|
||||
rows: Vec<(usize, usize)>,
|
||||
}
|
||||
|
||||
from!(|args:(&ArrangerTui, usize)|ArrangerVRowSep = Self {
|
||||
fg: TuiTheme::separator_fg(false),
|
||||
rows: ArrangerScene::ppqs(args.0.scenes(), args.1),
|
||||
});
|
||||
|
||||
render!(<Tui>|self: ArrangerVRowSep|render(move|to: &mut TuiOutput|{
|
||||
Ok(for y in self.rows.iter().map(|row|row.1) {
|
||||
let y = to.area().y() + (y / PPQ) as u16 + 1;
|
||||
if y >= to.buffer.area.height { break }
|
||||
for x in to.area().x()..to.area().x2().saturating_sub(2) {
|
||||
if x < to.buffer.area.x && y < to.buffer.area.y {
|
||||
let cell = to.buffer.get_mut(x, y);
|
||||
cell.modifier = Modifier::UNDERLINED;
|
||||
cell.underline_color = self.fg;
|
||||
}
|
||||
}
|
||||
})
|
||||
}));
|
||||
Loading…
Add table
Add a link
Reference in a new issue