tek/crates/tek_sequencer/src/arranger_view_v.rs

262 lines
9.1 KiB
Rust

use crate::*;
/// Draw arranger with 1 row per scene.
pub fn draw_compact_1 <'a> (
state: &Arranger<Tui>, to: &mut Tui
) -> Perhaps<[u16;4]> {
let track_cols = track_clip_name_lengths(state.tracks.as_slice());
let scene_rows = (0..=state.scenes.len()).map(|i|(96, 96*i)).collect::<Vec<_>>();
draw(state, to, track_cols.as_slice(), scene_rows.as_slice())
}
/// Draw arranger with 2 rows per scene.
pub fn draw_compact_2 <'a> (
state: &Arranger<Tui>, to: &mut Tui
) -> Perhaps<[u16;4]> {
let track_cols = track_clip_name_lengths(state.tracks.as_slice());
let scene_rows = (0..=state.scenes.len()).map(|i|(192, 192*i)).collect::<Vec<_>>();
draw(state, to, track_cols.as_slice(), scene_rows.as_slice())
}
/// Draw arranger with number of rows per scene proportional to duration of scene.
pub fn draw_expanded <'a> (
state: &Arranger<Tui>, to: &mut Tui
) -> Perhaps<[u16;4]> {
let track_cols = track_clip_name_lengths(state.tracks.as_slice());
let scene_rows = scene_ppqs(state.tracks.as_slice(), state.scenes.as_slice());
draw(state, to, track_cols.as_slice(), scene_rows.as_slice())
}
pub fn draw <'a, 'b> (
state: &Arranger<Tui>,
to: &mut Tui,
cols: &'b [(usize, usize)],
rows: &'b [(usize, usize)],
) -> Perhaps<[u16;4]> {
let area = to.area();
let area = [area.x(), area.y(), area.w(), 2 + (rows[rows.len() - 1].1 / 96) as u16];
let offset = 3 + scene_name_max_len(state.scenes.as_ref()) as u16;
let tracks = state.tracks.as_ref();
let scenes = state.scenes.as_ref();
Layers::new(|add|{
//.add_ref(&FillBg(Color::Rgb(30, 33, 36)))//COLOR_BG1))//bg_lo(state.focused, state.entered)))
add(&ColumnSeparators(offset, cols))?;
add(&RowSeparators(rows))?;
add(&CursorFocus(state.selected, offset, cols, rows))?;
add(&Split::right(|add|{
add(&TracksHeader(offset, cols, tracks))?;
add(&SceneRows(offset, cols, rows, tracks, scenes))
}))
}).render(to.with_rect(area))
}
struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]);
impl<'a> Widget for ColumnSeparators<'a> {
type Engine = Tui;
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area();
let Self(offset, cols) = self;
let style = Some(Style::default().fg(Nord::SEPARATOR));
for (_, x) in cols.iter() {
let x = offset + area.x() + *x as u16 - 1;
for y in area.y()..area.y2() {
to.blit(&"", x, y, style)?;
}
}
Ok(Some(area))
}
}
struct RowSeparators<'a>(&'a [(usize, usize)]);
impl<'a> Widget for RowSeparators<'a> {
type Engine = Tui;
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area();
let Self(rows) = self;
for (_, y) in rows.iter() {
let y = area.y() + (*y / 96) as u16 + 1;
if y >= to.buffer().area.height {
break
}
for x in area.x()..area.x2().saturating_sub(2) {
let cell = to.buffer().get_mut(x, y);
cell.modifier = Modifier::UNDERLINED;
cell.underline_color = Nord::SEPARATOR;
}
}
Ok(Some(area))
}
}
struct CursorFocus<'a>(
ArrangerFocus, u16, &'a [(usize, usize)], &'a [(usize, usize)]
);
impl<'a> Widget for CursorFocus<'a> {
type Engine = Tui;
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area();
let Self(selected, offset, cols, rows) = *self;
let get_track_area = |t: usize| [
offset + area.x() + cols[t].1 as u16 - 1,
area.y(),
cols[t].0 as u16,
area.h()
];
let get_scene_area = |s: usize| [
area.x(),
2 + area.y() + (rows[s].1 / 96) as u16,
area.w(),
(rows[s].0 / 96) as u16
];
let get_clip_area = |t: usize, s: usize| [
offset + area.x() + cols[t].1 as u16 - 1,
2 + area.y() + (rows[s].1 / 96) as u16,
cols[t].0 as u16,
(rows[s].0 / 96) 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 {
ArrangerFocus::Mix => {
to.fill_bg(area, COLOR_BG0);
area
},
ArrangerFocus::Track(t) => {
track_area = Some(get_track_area(t));
area
},
ArrangerFocus::Scene(s) => {
scene_area = Some(get_scene_area(s));
area
},
ArrangerFocus::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
},
};
if let Some([x, y, width, height]) = track_area {
to.fill_fg([x, y, 1, height], COLOR_BG5);
to.fill_fg([x + width, y, 1, height], COLOR_BG5);
}
if let Some([_, y, _, height]) = scene_area {
to.fill_ul([area.x(), y - 1, area.w(), 1], COLOR_BG5);
to.fill_ul([area.x(), y + height - 1, area.w(), 1], COLOR_BG5);
}
if let Some(clip_area) = clip_area {
to.fill_bg(clip_area, COLOR_BG0);
} else if let Some(track_area) = track_area {
to.fill_bg(track_area, COLOR_BG0);
} else if let Some(scene_area) = scene_area {
to.fill_bg(scene_area, COLOR_BG0);
}
Ok(Some(area))
}
}
struct TracksHeader<'a>(u16, &'a[(usize, usize)], &'a [Sequencer<Tui>]);
impl<'a> Widget for TracksHeader<'a> {
type Engine = Tui;
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area();
let Self(offset, track_cols, tracks) = *self;
let [x, y, width, _] = area;
for (track, (w, x)) in tracks.iter().zip(track_cols) {
let x = *x as u16;
if x > width {
break
}
let name = track.name.read().unwrap();
to.fill_bg([offset + x, y, *w as u16, 2], COLOR_BG1);
to.blit(&*name, offset + x + 1, y, Some(Style::default().white()))?;
}
Ok(Some([x, y, width, 2]))
}
}
struct SceneRows<'a>(u16, &'a[(usize, usize)], &'a[(usize, usize)], &'a[Sequencer<Tui>], &'a[Scene]);
impl<'a> Widget for SceneRows<'a> {
type Engine = Tui;
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area();
let Self(offset, track_cols, scene_rows, tracks, scenes) = *self;
let black = Some(Style::default().fg(Nord::SEPARATOR));
let [_, mut y, _, _height] = area;
for (_, x) in track_cols.iter() {
let x = *x as u16;
if x > 0 {
for y in area.y()-2..y-2 {
to.blit(&"", x - 1, y, black)?;
}
}
}
for (scene, (pulses, _)) in scenes.iter().zip(scene_rows) {
//if y > height {
//break
//}
let h = 1.max((pulses / 96) as u16);
SceneRow(tracks, scene, track_cols, offset)
.render(to.with_area(area.x(), y, area.w(), h))?;
y = y + h
}
Ok(Some(area))
}
}
struct SceneRow<'a>(&'a[Sequencer<Tui>], &'a Scene, &'a[(usize, usize)], u16);
impl<'a> Widget for SceneRow<'a> {
type Engine = Tui;
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area();
let Self(tracks, scene, track_cols, offset) = self;
let [x, y, width, _] = area;
let playing = scene.is_playing(tracks);
to.blit(&if playing { "" } else { " " }, x, y, None)?;
to.blit(&*scene.name.read().unwrap(), x + 1, y, Some(Style::default().white()))?;
to.fill_bg([x, y, offset.saturating_sub(1), area.h()], COLOR_BG1);
for (track, (w, x)) in track_cols.iter().enumerate() {
let x = *x as u16 + offset;
if x > width {
break
}
if let (Some(track), Some(Some(clip))) = (
tracks.get(track), scene.clips.get(track)
) {
SceneClip(track, *clip).render(to.with_area(x, y, *w as u16, area.h()))?;
}
}
Ok(Some(area))
}
}
struct SceneClip<'a>(&'a Sequencer<Tui>, usize);
impl<'a> Widget for SceneClip<'a> {
type Engine = Tui;
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area();
let Self(track, clip) = self;
let style = Some(Style::default().white());
if let Some(phrase) = track.phrases.get(*clip) {
let phrase = phrase.read().unwrap();
let name = phrase.name.read().unwrap();
to.blit(&format!("{clip:02} {name}"), area.x() + 1, area.y(), style)?;
to.fill_bg(area, if track.sequence == Some(*clip) {
Nord::PLAYING
} else {
COLOR_BG1
});
} else {
to.fill_bg(area, COLOR_BG0)
}
Ok(Some(area))
}
}