show sequencer with arranger

This commit is contained in:
🪞👃🪞 2024-08-18 18:43:58 +03:00
parent 964e8382d3
commit a533951fc6
10 changed files with 185 additions and 137 deletions

View file

@ -43,8 +43,9 @@ use crossterm::terminal::{
submod! { submod! {
exit exit
render
handle handle
render
render_split
time_base time_base
time_note time_note
time_tick time_tick

View file

@ -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<Rect>)> {
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<Rect> {
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<Rect>)> {
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<Rect> {
Ok(self.render_areas(buf, area)?.0)
}
}
pub trait Theme { pub trait Theme {
const BG0: Color; const BG0: Color;
const BG1: Color; const BG1: Color;

View file

@ -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<Rect>)> {
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<Rect> {
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<Rect>)> {
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<Rect> {
Ok(self.render_areas(buf, area)?.0)
}
}

View file

@ -34,6 +34,7 @@ impl<'a> Render for TrackView<'a> {
match self.direction { match self.direction {
Direction::Down => area.width = area.width.min(40), Direction::Down => area.width = area.width.min(40),
Direction::Right => area.width = area.width.min(10), Direction::Right => area.width = area.width.min(10),
_ => { unimplemented!() },
} }
fill_bg(buf, area, Nord::bg_lo(self.focused, self.entered)); fill_bg(buf, area, Nord::bg_lo(self.focused, self.entered));
let devices: Vec<&(dyn Render + Send + Sync)> = chain.devices.as_slice() let devices: Vec<&(dyn Render + Send + Sync)> = chain.devices.as_slice()

View file

@ -1,6 +1,7 @@
//! Clip launcher and arrangement editor. //! Clip launcher and arrangement editor.
use crate::*; use crate::*;
use tek_core::Direction;
/// Represents the tracks and scenes of the composition. /// Represents the tracks and scenes of the composition.
pub struct Arranger { pub struct Arranger {
@ -16,22 +17,52 @@ pub struct Arranger {
pub scenes: Vec<Scene>, pub scenes: Vec<Scene>,
pub focused: bool, pub focused: bool,
pub entered: bool, pub entered: bool,
pub fixed_height: bool,
pub transport: Option<Arc<RwLock<TransportToolbar>>>, pub transport: Option<Arc<RwLock<TransportToolbar>>>,
pub show_sequencer: Option<Direction>
} }
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 { impl Arranger {
pub fn new (name: &str) -> Self { pub fn new (name: &str) -> Self {
Self { Self {
name: name.into(), name: name.into(),
mode: ArrangerViewMode::Vertical, mode: ArrangerViewMode::VerticalCompact2,
selected: ArrangerFocus::Clip(0, 0), selected: ArrangerFocus::Clip(0, 0),
scenes: vec![], scenes: vec![],
tracks: vec![], tracks: vec![],
entered: true, entered: true,
focused: true, focused: true,
fixed_height: false, transport: None,
transport: None show_sequencer: Some(Direction::Down),
} }
} }
pub fn activate (&mut self) { pub fn activate (&mut self) {
@ -60,9 +91,10 @@ impl Arranger {
.flatten() .flatten()
} }
pub fn show_phrase (&mut self) -> Usually<()> { pub fn show_phrase (&mut self) -> Usually<()> {
unimplemented!() //unimplemented!()
//let phrase = self.phrase(); //let phrase = self.phrase();
//self.sequencer.show(phrase) //self.sequencer.show(phrase)
Ok(())
} }
pub fn phrase (&self) -> Option<&Arc<RwLock<Phrase>>> { pub fn phrase (&self) -> Option<&Arc<RwLock<Phrase>>> {
let track_id = self.selected.track()?; let track_id = self.selected.track()?;
@ -142,12 +174,3 @@ impl Arranger {
format!("Scene {}", self.scenes.len() + 1) 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),
});

View file

@ -11,9 +11,9 @@ pub struct ArrangerCli {
/// Whether to include a transport toolbar (default: true) /// Whether to include a transport toolbar (default: true)
#[arg(short, long)] transport: Option<bool>, #[arg(short, long)] transport: Option<bool>,
/// Number of tracks /// Number of tracks
#[arg(short = 'x', long)] tracks: Option<usize>, #[arg(short = 'x', long, default_value_t = 1)] tracks: usize,
/// Number of scenes /// Number of scenes
#[arg(short, long)] scenes: Option<usize>, #[arg(short, long, default_value_t = 1)] scenes: usize,
} }
impl Arranger { impl Arranger {
@ -26,17 +26,12 @@ impl Arranger {
if args.transport == Some(true) { if args.transport == Some(true) {
arr.transport = Some(Arc::new(RwLock::new(TransportToolbar::new(None)))); arr.transport = Some(Arc::new(RwLock::new(TransportToolbar::new(None))));
} }
if let Some(tracks) = args.tracks { for _ in 0..args.tracks {
for _ in 0..tracks {
arr.track_add(None)?; arr.track_add(None)?;
} }
} for _ in 0..args.scenes {
if let Some(scenes) = args.scenes {
for _ in 0..scenes {
arr.scene_add(None)?; arr.scene_add(None)?;
} }
}
Ok(arr) Ok(arr)
} }
} }

View file

@ -52,4 +52,12 @@ pub const KEYMAP_ARRANGER: &'static [KeyBinding<Arranger>] = keymap!(Arranger {
arranger.activate(); arranger.activate();
Ok(true) 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)
}],
}); });

View file

@ -1,16 +1,18 @@
/// Display mode of arranger /// Display mode of arranger
pub enum ArrangerViewMode { pub enum ArrangerViewMode {
Vertical, VerticalExpanded,
VerticalCompact, VerticalCompact1,
VerticalCompact2,
Horizontal, Horizontal,
} }
impl ArrangerViewMode { impl ArrangerViewMode {
pub fn to_next (&mut self) { pub fn to_next (&mut self) {
*self = match self { *self = match self {
Self::Vertical => Self::VerticalCompact, Self::VerticalExpanded => Self::VerticalCompact1,
Self::VerticalCompact => Self::Horizontal, Self::VerticalCompact1 => Self::VerticalCompact2,
Self::Horizontal => Self::Vertical, Self::VerticalCompact2 => Self::Horizontal,
Self::Horizontal => Self::VerticalExpanded,
} }
} }
} }

View file

@ -1,14 +1,23 @@
use crate::*; use crate::*;
pub fn draw_expanded (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually<Rect> { /// Draw arranger with 1 row per scene.
pub fn draw_compact_1 (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let track_cols = track_clip_name_lengths(state.tracks.as_slice()); 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::<Vec<_>>();
draw(state, buf, area, track_cols.as_slice(), scene_rows.as_slice()) draw(state, buf, area, track_cols.as_slice(), scene_rows.as_slice())
} }
pub fn draw_compact (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually<Rect> { /// Draw arranger with 2 rows per scene.
pub fn draw_compact_2 (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let track_cols = track_clip_name_lengths(state.tracks.as_slice()); 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::<Vec<_>>(); let scene_rows = (0..=state.scenes.len()).map(|i|(192, 192*i)).collect::<Vec<_>>();
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<Rect> {
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()) draw(state, buf, area, track_cols.as_slice(), scene_rows.as_slice())
} }

View file

@ -39,7 +39,7 @@ pub fn scene_name_max_len (scenes: &[Scene]) -> usize {
pub fn scene_ppqs (tracks: &[Sequencer], scenes: &[Scene]) -> Vec<(usize, usize)> { pub fn scene_ppqs (tracks: &[Sequencer], scenes: &[Scene]) -> Vec<(usize, usize)> {
let mut total = 0; let mut total = 0;
let mut scenes: Vec<(usize, usize)> = scenes.iter().map(|scene|{ 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; total = total + pulses;
(pulses, total - pulses) (pulses, total - pulses)
}).collect(); }).collect();