diff --git a/crates/tek_core/src/lib.rs b/crates/tek_core/src/lib.rs index dd8d47b9..a7d68ed8 100644 --- a/crates/tek_core/src/lib.rs +++ b/crates/tek_core/src/lib.rs @@ -43,8 +43,9 @@ use crossterm::terminal::{ submod! { exit - render handle + render + render_split time_base time_note time_tick diff --git a/crates/tek_core/src/render.rs b/crates/tek_core/src/render.rs index 12e32a2c..fe88de0e 100644 --- a/crates/tek_core/src/render.rs +++ b/crates/tek_core/src/render.rs @@ -241,101 +241,6 @@ impl<'a> Render for IfElse<'a> { } } -#[derive(Copy, Clone)] -pub enum Direction { Down, Right } - -impl Direction { - pub fn split <'a, const N: usize> (&self, items: [&'a (dyn Render + Sync);N]) -> Split<'a, N> { - Split(*self, items) - } - pub fn split_focus <'a> (&self, index: usize, items: Renderables<'a>, style: Style) -> SplitFocus<'a> { - SplitFocus(*self, index, items, style) - } - pub fn is_down (&self) -> bool { - match self { Self::Down => true, _ => false } - } - pub fn is_right (&self) -> bool { - match self { Self::Right => true, _ => false } - } -} - -pub struct Split<'a, const N: usize>( - pub Direction, pub [&'a (dyn Render + Sync);N] -); - -impl<'a, const N: usize> Split<'a, N> { - pub fn down (items: [&'a (dyn Render + Sync);N]) -> Self { - Self(Direction::Down, items) - } - pub fn right (items: [&'a (dyn Render + Sync);N]) -> Self { - Self(Direction::Right, items) - } - pub fn render_areas (&self, buf: &mut Buffer, area: Rect) -> Usually<(Rect, Vec)> { - let Rect { mut x, mut y, mut width, mut height } = area; - let mut areas = vec![]; - for item in self.1 { - if width == 0 || height == 0 { - break - } - let result = item.render(buf, Rect { x, y, width, height })?; - match self.0 { - Direction::Down => { - y = y + result.height; - height = height.saturating_sub(result.height); - }, - Direction::Right => { - x = x + result.width; - width = width.saturating_sub(result.width); - }, - }; - areas.push(area); - } - Ok((area, areas)) - } -} - -impl<'a, const N: usize> Render for Split<'a, N> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { - Ok(self.render_areas(buf, area)?.0) - } -} - -type Renderables<'a> = &'a [&'a (dyn Render + Send + Sync)]; - -pub struct SplitFocus<'a>(pub Direction, pub usize, pub Renderables<'a>, pub Style); - -impl<'a> SplitFocus<'a> { - pub fn render_areas (&self, buf: &mut Buffer, area: Rect) -> Usually<(Rect, Vec)> { - let Rect { mut x, mut y, mut width, mut height } = area; - let mut areas = vec![]; - for item in self.2.iter() { - if width == 0 || height == 0 { - break - } - let result = item.render(buf, Rect { x, y, width, height })?; - areas.push(result); - match self.0 { - Direction::Down => { - y = y + result.height; - height = height.saturating_sub(result.height); - }, - Direction::Right => { - x = x + result.width; - width = width.saturating_sub(result.width); - }, - } - Lozenge(self.3).draw(buf, result)?; - } - Ok((area, areas)) - } -} - -impl<'a> Render for SplitFocus<'a> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { - Ok(self.render_areas(buf, area)?.0) - } -} - pub trait Theme { const BG0: Color; const BG1: Color; diff --git a/crates/tek_core/src/render_split.rs b/crates/tek_core/src/render_split.rs new file mode 100644 index 00000000..db7f63f7 --- /dev/null +++ b/crates/tek_core/src/render_split.rs @@ -0,0 +1,104 @@ +use crate::*; + +#[derive(Copy, Clone)] +pub enum Direction { + Up, + Down, + Left, + Right +} + +impl Direction { + pub fn split <'a, const N: usize> (&self, items: [&'a (dyn Render + Sync);N]) -> Split<'a, N> { + Split(*self, items) + } + pub fn split_focus <'a> (&self, index: usize, items: Renderables<'a>, style: Style) -> SplitFocus<'a> { + SplitFocus(*self, index, items, style) + } + pub fn is_down (&self) -> bool { + match self { Self::Down => true, _ => false } + } + pub fn is_right (&self) -> bool { + match self { Self::Right => true, _ => false } + } +} + +pub struct Split<'a, const N: usize>( + pub Direction, pub [&'a (dyn Render + Sync);N] +); + +impl<'a, const N: usize> Split<'a, N> { + pub fn down (items: [&'a (dyn Render + Sync);N]) -> Self { + Self(Direction::Down, items) + } + pub fn right (items: [&'a (dyn Render + Sync);N]) -> Self { + Self(Direction::Right, items) + } + pub fn render_areas (&self, buf: &mut Buffer, area: Rect) -> Usually<(Rect, Vec)> { + let Rect { mut x, mut y, mut width, mut height } = area; + let mut areas = vec![]; + for item in self.1 { + if width == 0 || height == 0 { + break + } + let result = item.render(buf, Rect { x, y, width, height })?; + match self.0 { + Direction::Down => { + y = y + result.height; + height = height.saturating_sub(result.height); + }, + Direction::Right => { + x = x + result.width; + width = width.saturating_sub(result.width); + }, + _ => unimplemented!() + }; + areas.push(area); + } + Ok((area, areas)) + } +} + +impl<'a, const N: usize> Render for Split<'a, N> { + fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { + Ok(self.render_areas(buf, area)?.0) + } +} + +type Renderables<'a> = &'a [&'a (dyn Render + Send + Sync)]; + +pub struct SplitFocus<'a>(pub Direction, pub usize, pub Renderables<'a>, pub Style); + +impl<'a> SplitFocus<'a> { + pub fn render_areas (&self, buf: &mut Buffer, area: Rect) -> Usually<(Rect, Vec)> { + let Rect { mut x, mut y, mut width, mut height } = area; + let mut areas = vec![]; + for item in self.2.iter() { + if width == 0 || height == 0 { + break + } + let result = item.render(buf, Rect { x, y, width, height })?; + areas.push(result); + match self.0 { + Direction::Down => { + y = y + result.height; + height = height.saturating_sub(result.height); + }, + Direction::Right => { + x = x + result.width; + width = width.saturating_sub(result.width); + }, + _ => unimplemented!() + } + Lozenge(self.3).draw(buf, result)?; + } + Ok((area, areas)) + } +} + +impl<'a> Render for SplitFocus<'a> { + fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { + Ok(self.render_areas(buf, area)?.0) + } +} + diff --git a/crates/tek_mixer/src/track_view.rs b/crates/tek_mixer/src/track_view.rs index 387983d4..1b9d01c7 100644 --- a/crates/tek_mixer/src/track_view.rs +++ b/crates/tek_mixer/src/track_view.rs @@ -34,6 +34,7 @@ impl<'a> Render for TrackView<'a> { match self.direction { Direction::Down => area.width = area.width.min(40), Direction::Right => area.width = area.width.min(10), + _ => { unimplemented!() }, } fill_bg(buf, area, Nord::bg_lo(self.focused, self.entered)); let devices: Vec<&(dyn Render + Send + Sync)> = chain.devices.as_slice() diff --git a/crates/tek_sequencer/src/arranger.rs b/crates/tek_sequencer/src/arranger.rs index 6784a65a..520a6250 100644 --- a/crates/tek_sequencer/src/arranger.rs +++ b/crates/tek_sequencer/src/arranger.rs @@ -1,6 +1,7 @@ //! Clip launcher and arrangement editor. use crate::*; +use tek_core::Direction; /// Represents the tracks and scenes of the composition. pub struct Arranger { @@ -16,22 +17,52 @@ pub struct Arranger { pub scenes: Vec, pub focused: bool, pub entered: bool, - pub fixed_height: bool, pub transport: Option>>, + pub show_sequencer: Option } +render!(Arranger |self, buf, area| { + let arrangement = |buf, area| match self.mode { + ArrangerViewMode::Horizontal => + super::arranger_view_h::draw(self, buf, area), + ArrangerViewMode::VerticalCompact1 => + super::arranger_view_v::draw_compact_1(self, buf, area), + ArrangerViewMode::VerticalCompact2 => + super::arranger_view_v::draw_compact_2(self, buf, area), + ArrangerViewMode::VerticalExpanded => + super::arranger_view_v::draw_expanded(self, buf, area), + }; + if let Some(direction) = self.show_sequencer { + let used = arrangement(buf, area)?; + match direction { + Direction::Down => { + let area = Rect { + y: area.y + used.height, + height: area.height - used.height, + ..area + }; + self.sequencer().map(|sequencer|sequencer.render(buf, area)); + }, + _ => unimplemented!() + } + Ok(area) + } else { + arrangement(buf, area) + } +}); + impl Arranger { pub fn new (name: &str) -> Self { Self { - name: name.into(), - mode: ArrangerViewMode::Vertical, - selected: ArrangerFocus::Clip(0, 0), - scenes: vec![], - tracks: vec![], - entered: true, - focused: true, - fixed_height: false, - transport: None + name: name.into(), + mode: ArrangerViewMode::VerticalCompact2, + selected: ArrangerFocus::Clip(0, 0), + scenes: vec![], + tracks: vec![], + entered: true, + focused: true, + transport: None, + show_sequencer: Some(Direction::Down), } } pub fn activate (&mut self) { @@ -60,9 +91,10 @@ impl Arranger { .flatten() } pub fn show_phrase (&mut self) -> Usually<()> { - unimplemented!() + //unimplemented!() //let phrase = self.phrase(); //self.sequencer.show(phrase) + Ok(()) } pub fn phrase (&self) -> Option<&Arc>> { let track_id = self.selected.track()?; @@ -142,12 +174,3 @@ impl Arranger { format!("Scene {}", self.scenes.len() + 1) } } - -render!(Arranger |self, buf, area| match self.mode { - ArrangerViewMode::Horizontal => - super::arranger_view_h::draw(self, buf, area), - ArrangerViewMode::Vertical => - super::arranger_view_v::draw_expanded(self, buf, area), - ArrangerViewMode::VerticalCompact => - super::arranger_view_v::draw_compact(self, buf, area), -}); diff --git a/crates/tek_sequencer/src/arranger_cli.rs b/crates/tek_sequencer/src/arranger_cli.rs index 93d63144..1f0828cb 100644 --- a/crates/tek_sequencer/src/arranger_cli.rs +++ b/crates/tek_sequencer/src/arranger_cli.rs @@ -11,9 +11,9 @@ pub struct ArrangerCli { /// Whether to include a transport toolbar (default: true) #[arg(short, long)] transport: Option, /// Number of tracks - #[arg(short = 'x', long)] tracks: Option, + #[arg(short = 'x', long, default_value_t = 1)] tracks: usize, /// Number of scenes - #[arg(short, long)] scenes: Option, + #[arg(short, long, default_value_t = 1)] scenes: usize, } impl Arranger { @@ -26,17 +26,12 @@ impl Arranger { if args.transport == Some(true) { arr.transport = Some(Arc::new(RwLock::new(TransportToolbar::new(None)))); } - if let Some(tracks) = args.tracks { - for _ in 0..tracks { - arr.track_add(None)?; - } + for _ in 0..args.tracks { + arr.track_add(None)?; } - if let Some(scenes) = args.scenes { - for _ in 0..scenes { - arr.scene_add(None)?; - } + for _ in 0..args.scenes { + arr.scene_add(None)?; } Ok(arr) } } - diff --git a/crates/tek_sequencer/src/arranger_handle.rs b/crates/tek_sequencer/src/arranger_handle.rs index b96bbab2..3892e889 100644 --- a/crates/tek_sequencer/src/arranger_handle.rs +++ b/crates/tek_sequencer/src/arranger_handle.rs @@ -52,4 +52,12 @@ pub const KEYMAP_ARRANGER: &'static [KeyBinding] = keymap!(Arranger { arranger.activate(); Ok(true) }], + [Char('a'), CONTROL, "scene_add", "add a new scene", |arranger: &mut Arranger| { + arranger.scene_add(None)?; + Ok(true) + }], + [Char('t'), CONTROL, "track_add", "add a new track", |arranger: &mut Arranger| { + arranger.track_add(None)?; + Ok(true) + }], }); diff --git a/crates/tek_sequencer/src/arranger_view.rs b/crates/tek_sequencer/src/arranger_view.rs index f820426e..a3f48ce9 100644 --- a/crates/tek_sequencer/src/arranger_view.rs +++ b/crates/tek_sequencer/src/arranger_view.rs @@ -1,16 +1,18 @@ /// Display mode of arranger pub enum ArrangerViewMode { - Vertical, - VerticalCompact, + VerticalExpanded, + VerticalCompact1, + VerticalCompact2, Horizontal, } impl ArrangerViewMode { pub fn to_next (&mut self) { *self = match self { - Self::Vertical => Self::VerticalCompact, - Self::VerticalCompact => Self::Horizontal, - Self::Horizontal => Self::Vertical, + Self::VerticalExpanded => Self::VerticalCompact1, + Self::VerticalCompact1 => Self::VerticalCompact2, + Self::VerticalCompact2 => Self::Horizontal, + Self::Horizontal => Self::VerticalExpanded, } } } diff --git a/crates/tek_sequencer/src/arranger_view_v.rs b/crates/tek_sequencer/src/arranger_view_v.rs index 27145c66..9a0bcb46 100644 --- a/crates/tek_sequencer/src/arranger_view_v.rs +++ b/crates/tek_sequencer/src/arranger_view_v.rs @@ -1,14 +1,23 @@ use crate::*; -pub fn draw_expanded (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually { +/// Draw arranger with 1 row per scene. +pub fn draw_compact_1 (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually { let track_cols = track_clip_name_lengths(state.tracks.as_slice()); - let scene_rows = scene_ppqs(state.tracks.as_slice(), state.scenes.as_slice()); + let scene_rows = (0..=state.scenes.len()).map(|i|(96, 96*i)).collect::>(); draw(state, buf, area, track_cols.as_slice(), scene_rows.as_slice()) } -pub fn draw_compact (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually { +/// Draw arranger with 2 rows per scene. +pub fn draw_compact_2 (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually { let track_cols = track_clip_name_lengths(state.tracks.as_slice()); - let scene_rows = (0..=state.scenes.len()+3).map(|i|(96, 96*i)).collect::>(); + let scene_rows = (0..=state.scenes.len()).map(|i|(192, 192*i)).collect::>(); + draw(state, buf, area, track_cols.as_slice(), scene_rows.as_slice()) +} + +/// Draw arranger with number of rows per scene corresponding to duration of scene. +pub fn draw_expanded (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually { + 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, buf, area, track_cols.as_slice(), scene_rows.as_slice()) } diff --git a/crates/tek_sequencer/src/scene.rs b/crates/tek_sequencer/src/scene.rs index 48c07216..568ce878 100644 --- a/crates/tek_sequencer/src/scene.rs +++ b/crates/tek_sequencer/src/scene.rs @@ -39,7 +39,7 @@ pub fn scene_name_max_len (scenes: &[Scene]) -> usize { pub fn scene_ppqs (tracks: &[Sequencer], scenes: &[Scene]) -> Vec<(usize, usize)> { let mut total = 0; let mut scenes: Vec<(usize, usize)> = scenes.iter().map(|scene|{ - let pulses = scene.pulses(tracks); + let pulses = scene.pulses(tracks).max(96); total = total + pulses; (pulses, total - pulses) }).collect();