use crate::*; /// Draw arranger with 1 row per scene. pub fn draw_compact_1 <'a> ( state: &Arranger, 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::>(); 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, 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::>(); 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, 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, 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]); 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], &'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], &'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, 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)) } }