mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
show sequencer with arranger
This commit is contained in:
parent
964e8382d3
commit
a533951fc6
10 changed files with 185 additions and 137 deletions
|
|
@ -43,8 +43,9 @@ use crossterm::terminal::{
|
|||
|
||||
submod! {
|
||||
exit
|
||||
render
|
||||
handle
|
||||
render
|
||||
render_split
|
||||
time_base
|
||||
time_note
|
||||
time_tick
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
const BG0: Color;
|
||||
const BG1: Color;
|
||||
|
|
|
|||
104
crates/tek_core/src/render_split.rs
Normal file
104
crates/tek_core/src/render_split.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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<Scene>,
|
||||
pub focused: bool,
|
||||
pub entered: bool,
|
||||
pub fixed_height: bool,
|
||||
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 {
|
||||
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<RwLock<Phrase>>> {
|
||||
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),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pub struct ArrangerCli {
|
|||
/// Whether to include a transport toolbar (default: true)
|
||||
#[arg(short, long)] transport: Option<bool>,
|
||||
/// Number of tracks
|
||||
#[arg(short = 'x', long)] tracks: Option<usize>,
|
||||
#[arg(short = 'x', long, default_value_t = 1)] tracks: usize,
|
||||
/// Number of scenes
|
||||
#[arg(short, long)] scenes: Option<usize>,
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,4 +52,12 @@ pub const KEYMAP_ARRANGER: &'static [KeyBinding<Arranger>] = 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)
|
||||
}],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,23 @@
|
|||
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 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())
|
||||
}
|
||||
|
||||
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 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())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue