wip: borrow checker battles

This commit is contained in:
🪞👃🪞 2024-09-04 16:57:48 +03:00
parent 1d4db3c629
commit 7fbb40fad6
38 changed files with 778 additions and 708 deletions

View file

@ -2,7 +2,7 @@
use crate::*;
/// Represents the tracks and scenes of the composition.
pub struct Arranger {
pub struct Arranger<T, U> {
/// Name of arranger
pub name: Arc<RwLock<String>>,
/// Collection of tracks.
@ -14,14 +14,11 @@ pub struct Arranger {
/// Display mode of arranger
pub mode: ArrangerViewMode,
/// Slot for modal dialog displayed on top of app.
pub modal: Option<Box<dyn ExitableComponent>>,
pub modal: Option<Box<dyn ExitableComponent<T, U>>>,
/// Whether the arranger is currently focused
pub focused: bool
}
focusable!(Arranger (focused));
impl Arranger {
impl<T, U> Arranger<T, U> {
pub fn new (name: &str) -> Self {
Self {
name: Arc::new(RwLock::new(name.into())),
@ -83,3 +80,56 @@ impl Arranger {
}
}
}
/// Display mode of arranger
pub enum ArrangerViewMode {
VerticalExpanded,
VerticalCompact1,
VerticalCompact2,
Horizontal,
}
/// Arranger display mode can be cycled
impl ArrangerViewMode {
/// Cycle arranger display mode
pub fn to_next (&mut self) {
*self = match self {
Self::VerticalExpanded => Self::VerticalCompact1,
Self::VerticalCompact1 => Self::VerticalCompact2,
Self::VerticalCompact2 => Self::Horizontal,
Self::Horizontal => Self::VerticalExpanded,
}
}
}
/// Render arranger to terminal
impl<'a> Render<TuiOutput<'a>, Rect> for Arranger<TuiOutput<'a>, Rect> {
fn render (&'a self, to: &'a mut TuiOutput<'a>) -> Perhaps<Rect> {
let area = (|to|match self.mode {
ArrangerViewMode::Horizontal =>
super::arranger_view_h::draw(self, to),
ArrangerViewMode::VerticalCompact1 =>
super::arranger_view_v::draw_compact_1(self, to),
ArrangerViewMode::VerticalCompact2 =>
super::arranger_view_v::draw_compact_2(self, to),
ArrangerViewMode::VerticalExpanded =>
super::arranger_view_v::draw_expanded(self, to),
})(&mut to.area(Rect {
x: to.area.x + 1,
width: to.area.width - 2,
y: to.area.y + 1,
height: to.area.height - 2
}))?.unwrap();
Lozenge(Style::default().fg(Nord::BG2)).draw(&mut to.area(Rect {
x: area.x.saturating_sub(1),
width: area.width + 2,
y: area.y.saturating_sub(1),
height: area.height + 2,
}))
}
}
impl<'a> Focusable<TuiOutput<'a>, Rect> for Arranger<TuiOutput<'a>, Rect> {
fn is_focused (&self) -> bool {
self.focused
}
fn set_focused (&mut self, focused: bool) {
self.focused = focused
}
}

View file

@ -1,86 +1,71 @@
use crate::*;
handle!(Arranger |self, e| {
match self.modal.as_mut() {
Some(modal) => {
let result = modal.handle(e)?;
if modal.exited() {
self.modal = None;
impl<T, U> Handle for Arranger<T, U> {
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
match self.modal.as_mut() {
Some(modal) => {
let result = modal.handle(event)?;
if modal.exited() {
self.modal = None;
}
Ok(result)
},
None => match event {
AppEvent::Input(crossterm::event::Event::Key(event)) => {
for (code, modifiers, _, _, command) in [
key!(Char('`'), NONE, "mode_switch", "switch the display mode", || {
self.mode.to_next();
Ok(true)
}),
key!(Up, NONE, "cursor_up", "move cursor up", || {
match self.mode {
ArrangerViewMode::Horizontal => self.track_prev(),
_ => self.scene_prev(),
};
self.show_phrase()?;
Ok(true)
}),
key!(Down, NONE, "cursor_down", "move cursor down", || {
match self.mode {
ArrangerViewMode::Horizontal => self.track_next(),
_ => self.scene_next(),
};
self.show_phrase()?;
Ok(true)
}),
key!(Left, NONE, "cursor_left", "move cursor left", || {
match self.mode {
ArrangerViewMode::Horizontal => self.scene_prev(),
_ => self.track_prev(),
};
self.show_phrase()?;
Ok(true)
}),
key!(Right, NONE, "cursor_right", "move cursor right", || {
match self.mode {
ArrangerViewMode::Horizontal => self.scene_next(),
_ => self.track_next(),
};
self.show_phrase()?;
Ok(true)
}),
key!(Char('.'), NONE, "increment", "set next clip at cursor", || { self.phrase_next(); Ok(true) }),
key!(Char(','), NONE, "decrement", "set previous clip at cursor", || { self.phrase_prev(); Ok(true) }),
key!(Enter, NONE, "activate", "activate item at cursor", || { self.activate(); Ok(true) }),
key!(Char('a'), CONTROL, "scene_add", "add a new scene", || { self.scene_add(None)?; Ok(true) }),
key!(Char('t'), CONTROL, "track_add", "add a new track", || { self.track_add(None)?; Ok(true) }),
key!(Char('n'), NONE, "rename", "rename item at cursor", || { self.rename_selected(); Ok(true) }),
key!(Char('l'), NONE, "length", "set length of item at cursor", || { todo!(); Ok(true) }),
key!(Char('c'), NONE, "color", "set color of item at cursor", || { todo!(); Ok(true) })
].iter() {
if *code == event.code && modifiers.bits() == event.modifiers.bits() {
return command()
}
}
return Ok(false)
},
_ => Ok(false)
}
Ok(result)
},
None => handle_keymap(self, e, KEYMAP_ARRANGER)
}
}
});
/// Key bindings for arranger section.
pub const KEYMAP_ARRANGER: &'static [KeyBinding<Arranger>] = keymap!(Arranger {
[Char('`'), NONE, "mode_switch", "switch the display mode", |arranger: &mut Arranger| {
arranger.mode.to_next();
Ok(true)
}],
[Up, NONE, "cursor_up", "move cursor up", |arranger: &mut Arranger| {
match arranger.mode {
ArrangerViewMode::Horizontal => arranger.track_prev(),
_ => arranger.scene_prev(),
};
arranger.show_phrase()?;
Ok(true)
}],
[Down, NONE, "cursor_down", "move cursor down", |arranger: &mut Arranger| {
match arranger.mode {
ArrangerViewMode::Horizontal => arranger.track_next(),
_ => arranger.scene_next(),
};
arranger.show_phrase()?;
Ok(true)
}],
[Left, NONE, "cursor_left", "move cursor left", |arranger: &mut Arranger| {
match arranger.mode {
ArrangerViewMode::Horizontal => arranger.scene_prev(),
_ => arranger.track_prev(),
};
arranger.show_phrase()?;
Ok(true)
}],
[Right, NONE, "cursor_right", "move cursor right", |arranger: &mut Arranger| {
match arranger.mode {
ArrangerViewMode::Horizontal => arranger.scene_next(),
_ => arranger.track_next(),
};
arranger.show_phrase()?;
Ok(true)
}],
[Char('.'), NONE, "increment", "set next clip at cursor", |arranger: &mut Arranger| {
arranger.phrase_next();
Ok(true)
}],
[Char(','), NONE, "decrement", "set previous clip at cursor", |arranger: &mut Arranger| {
arranger.phrase_prev();
Ok(true)
}],
[Enter, NONE, "activate", "activate item at cursor", |arranger: &mut 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)
}],
[Char('n'), NONE, "rename", "rename item at cursor", |arranger: &mut Arranger| {
arranger.rename_selected();
Ok(true)
}],
[Char('l'), NONE, "length", "set length of item at cursor", |arranger: &mut Arranger| {
todo!();
Ok(true)
}],
[Char('c'), NONE, "color", "set color of item at cursor", |arranger: &mut Arranger| {
todo!();
Ok(true)
}]
});
}

View file

@ -1,6 +1,5 @@
use crate::*;
impl Arranger {
impl<'a> Arranger<TuiOutput<'a>, Rect> {
pub fn rename_selected (&mut self) {
self.modal = Some(Box::new(ArrangerRenameModal::new(
self.selected,
@ -13,7 +12,6 @@ impl Arranger {
)));
}
}
/// Appears on first run (i.e. if state dir is missing).
pub struct ArrangerRenameModal {
done: bool,
@ -22,7 +20,6 @@ pub struct ArrangerRenameModal {
result: Arc<RwLock<String>>,
cursor: usize
}
impl ArrangerRenameModal {
pub fn new (target: ArrangerFocus, value: &Arc<RwLock<String>>) -> Self {
Self {
@ -34,18 +31,17 @@ impl ArrangerRenameModal {
}
}
}
impl<'a> Render<TuiOutput<'a>, Rect> for ArrangerRenameModal {
fn render (&self, to: &mut TuiOutput<'a>) -> Usually<Rect> {
let y = area.y + area.height / 2;
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let y = to.area.y + to.area.height / 2;
let bg_area = Rect {
x: 1,
y: y - 1,
width: area.width - 2,
width: to.area.width - 2,
height: 3
};
fill_bg(buf, bg_area, Nord::BG0);
Lozenge(Style::default().bold().white().dim()).draw(buf, bg_area)?;
fill_bg(to.buffer, bg_area, Nord::BG0);
Lozenge(Style::default().bold().white().dim()).draw(to.buffer, bg_area)?;
let label = match self.target {
ArrangerFocus::Mix => "Rename project:",
ArrangerFocus::Track(_) => "Rename track:",
@ -53,15 +49,14 @@ impl<'a> Render<TuiOutput<'a>, Rect> for ArrangerRenameModal {
ArrangerFocus::Clip(_, _) => "Rename clip:",
};
let style = Some(Style::default().not_bold().white().not_dim());
label.blit(buf, area.x + 3, y, style)?;
label.blit(to.buffer, to.area.x + 3, y, style)?;
let style = Some(Style::default().bold().white().not_dim());
self.value.blit(buf, area.x + 3 + label.len() as u16 + 1, y, style)?;
self.value.blit(to.buffer, to.area.x + 3 + label.len() as u16 + 1, y, style)?;
let style = Some(Style::default().bold().white().not_dim().reversed());
"".blit(buf, area.x + 3 + label.len() as u16 + 1 + self.cursor as u16, y, style)?;
Ok(area)
"".blit(to.buffer, to.area.x + 3 + label.len() as u16 + 1 + self.cursor as u16, y, style)?;
Ok(Some(to.area))
}
}
handle!(ArrangerRenameModal |self, e| {
match e {
AppEvent::Input(Event::Key(k)) => {
@ -98,7 +93,6 @@ handle!(ArrangerRenameModal |self, e| {
_ => Ok(false),
}
});
impl Exit for ArrangerRenameModal {
fn exited (&self) -> bool {
self.done

View file

@ -3,7 +3,7 @@ use crate::*;
use super::Arranger;
/// Track management methods
impl Arranger {
impl<T, U> Arranger<T, U> {
pub fn track (&self) -> Option<&Sequencer> {
self.selected.track().map(|t|self.tracks.get(t)).flatten()
}

View file

@ -1,48 +0,0 @@
use crate::*;
/// Display mode of arranger
pub enum ArrangerViewMode {
VerticalExpanded,
VerticalCompact1,
VerticalCompact2,
Horizontal,
}
impl ArrangerViewMode {
pub fn to_next (&mut self) {
*self = match self {
Self::VerticalExpanded => Self::VerticalCompact1,
Self::VerticalCompact1 => Self::VerticalCompact2,
Self::VerticalCompact2 => Self::Horizontal,
Self::Horizontal => Self::VerticalExpanded,
}
}
}
impl<'a> Render<TuiOutput<'a>, Rect> for Arranger {
fn render (&self, to: &mut TuiOutput<'a>) -> Usually<Rect> {
let area = Rect {
x: to.area.x + 1,
width: to.area.width - 2,
y: to.area.y + 1,
height: to.area.height - 2
};
let area = match self.mode {
ArrangerViewMode::Horizontal =>
super::arranger_view_h::draw(self, to.buffer, area),
ArrangerViewMode::VerticalCompact1 =>
super::arranger_view_v::draw_compact_1(self, to.buffer, area),
ArrangerViewMode::VerticalCompact2 =>
super::arranger_view_v::draw_compact_2(self, to.buffer, area),
ArrangerViewMode::VerticalExpanded =>
super::arranger_view_v::draw_expanded(self, to.buffer, area),
}?;
let area = Rect {
x: area.x - 1,
width: area.width + 2,
y: area.y - 1,
height: area.height + 2,
};
Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, area)
}
}

View file

@ -1,6 +1,9 @@
use crate::*;
pub fn draw (state: &Arranger, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
pub fn draw <'a> (
state: &Arranger<TuiOutput<'a>, Rect>, to: &mut TuiOutput<'a>
) -> Perhaps<Rect> {
let mut area = to.area;
area.height = area.height.min((2 + state.tracks.len() * 2) as u16);
let tracks = state.tracks.as_slice();
Layered::new()
@ -13,51 +16,53 @@ pub fn draw (state: &Arranger, buf: &mut Buffer, mut area: Rect) -> Usually<Rect
.add(TrackEraseColumn(tracks))
.add(TrackGainColumn(tracks))
.add(TrackScenesColumn(tracks, state.scenes.as_slice(), state.selected)))
.render(buf, area)
.render(to)
}
struct TrackNameColumn<'a>(&'a [Sequencer], ArrangerFocus);
impl<'a> Render for TrackNameColumn<'a> {
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for TrackNameColumn<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Self(tracks, selected) = self;
let mut area = to.area;
let yellow = Some(Style::default().yellow().bold().not_dim());
let white = Some(Style::default().white().bold().not_dim());
area.width = 3 + 5.max(track_name_max_len(tracks)) as u16;
let offset = 0; // track scroll offset
for y in 0..area.height {
if y == 0 {
"Mixer".blit(buf, area.x + 1, area.y + y, Some(DIM))?;
"Mixer".blit(to.buffer, area.x + 1, area.y + y, Some(DIM))?;
} else if y % 2 == 0 {
let index = (y as usize - 2) / 2 + offset;
if let Some(track) = tracks.get(index) {
let selected = selected.track() == Some(index);
let style = if selected { yellow } else { white };
format!(" {index:>02} ").blit(buf, area.x, area.y + y, style)?;
track.name.read().unwrap().blit(buf, area.x + 4, area.y + y, style)?;
format!(" {index:>02} ").blit(to.buffer, area.x, area.y + y, style)?;
track.name.read().unwrap().blit(to.buffer, area.x + 4, area.y + y, style)?;
}
}
}
Ok(area)
Ok(Some(area))
}
}
struct TrackMonitorColumn<'a>(&'a [Sequencer]);
impl<'a> Render for TrackMonitorColumn<'a> {
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for TrackMonitorColumn<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Self(tracks) = self;
let mut area = to.area;
let on = Some(Style::default().not_dim().green().bold());
let off = Some(DIM);
area.x += 1;
for y in 0..area.height {
if y == 0 {
//" MON ".blit(buf, area.x, area.y + y, style2)?;
//" MON ".blit(to.buffer, area.x, area.y + y, style2)?;
} else if y % 2 == 0 {
let index = (y as usize - 2) / 2;
if let Some(track) = tracks.get(index) {
let style = if track.monitoring { on } else { off };
" MON ".blit(buf, area.x, area.y + y, style)?;
" MON ".blit(to.buffer, area.x, area.y + y, style)?;
} else {
area.height = y;
break
@ -65,26 +70,27 @@ impl<'a> Render for TrackMonitorColumn<'a> {
}
}
area.width = 4;
Ok(area)
Ok(Some(area))
}
}
struct TrackRecordColumn<'a>(&'a [Sequencer]);
impl<'a> Render for TrackRecordColumn<'a> {
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for TrackRecordColumn<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Self(tracks) = self;
let mut area = to.area;
let on = Some(Style::default().not_dim().red().bold());
let off = Some(Style::default().dim());
area.x += 1;
for y in 0..area.height {
if y == 0 {
//" REC ".blit(buf, area.x, area.y + y, style2)?;
//" REC ".blit(to.buffer, area.x, area.y + y, style2)?;
} else if y % 2 == 0 {
let index = (y as usize - 2) / 2;
if let Some(track) = tracks.get(index) {
let style = if track.recording { on } else { off };
" REC ".blit(buf, area.x, area.y + y, style)?;
" REC ".blit(to.buffer, area.x, area.y + y, style)?;
} else {
area.height = y;
break
@ -92,25 +98,26 @@ impl<'a> Render for TrackRecordColumn<'a> {
}
}
area.width = 4;
Ok(area)
Ok(Some(area))
}
}
struct TrackOverdubColumn<'a>(&'a [Sequencer]);
impl<'a> Render for TrackOverdubColumn<'a> {
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for TrackOverdubColumn<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Self(tracks) = self;
let mut area = to.area;
let on = Some(Style::default().not_dim().yellow().bold());
let off = Some(Style::default().dim());
area.x = area.x + 1;
for y in 0..area.height {
if y == 0 {
//" OVR ".blit(buf, area.x, area.y + y, style2)?;
//" OVR ".blit(to.buffer, area.x, area.y + y, style2)?;
} else if y % 2 == 0 {
let index = (y as usize - 2) / 2;
if let Some(track) = tracks.get(index) {
" OVR ".blit(buf, area.x, area.y + y, if track.overdub {
" OVR ".blit(to.buffer, area.x, area.y + y, if track.overdub {
on
} else {
off
@ -122,24 +129,25 @@ impl<'a> Render for TrackOverdubColumn<'a> {
}
}
area.width = 4;
Ok(area)
Ok(Some(area))
}
}
struct TrackEraseColumn<'a>(&'a [Sequencer]);
impl<'a> Render for TrackEraseColumn<'a> {
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for TrackEraseColumn<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Self(tracks) = self;
let mut area = to.area;
let off = Some(Style::default().dim());
area.x = area.x + 1;
for y in 0..area.height {
if y == 0 {
//" DEL ".blit(buf, area.x, area.y + y, style2)?;
//" DEL ".blit(to.buffer, area.x, area.y + y, style2)?;
} else if y % 2 == 0 {
let index = (y as usize - 2) / 2;
if let Some(_) = tracks.get(index) {
" DEL ".blit(buf, area.x, area.y + y, off)?;
" DEL ".blit(to.buffer, area.x, area.y + y, off)?;
} else {
area.height = y;
break
@ -147,24 +155,25 @@ impl<'a> Render for TrackEraseColumn<'a> {
}
}
area.width = 4;
Ok(area)
Ok(Some(area))
}
}
struct TrackGainColumn<'a>(&'a [Sequencer]);
impl<'a> Render for TrackGainColumn<'a> {
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for TrackGainColumn<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Self(tracks) = self;
let mut area = to.area;
let off = Some(Style::default().dim());
area.x = area.x + 1;
for y in 0..area.height {
if y == 0 {
//" GAIN ".blit(buf, area.x, area.y + y, style2)?;
//" GAIN ".blit(to.buffer, area.x, area.y + y, style2)?;
} else if y % 2 == 0 {
let index = (y as usize - 2) / 2;
if let Some(_) = tracks.get(index) {
" +0.0 ".blit(buf, area.x, area.y + y, off)?;
" +0.0 ".blit(to.buffer, area.x, area.y + y, off)?;
} else {
area.height = y;
break
@ -172,15 +181,16 @@ impl<'a> Render for TrackGainColumn<'a> {
}
}
area.width = 7;
Ok(area)
Ok(Some(area))
}
}
struct TrackScenesColumn<'a>(&'a [Sequencer], &'a [Scene], ArrangerFocus);
impl<'a> Render for TrackScenesColumn<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for TrackScenesColumn<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Self(tracks, scenes, selected) = self;
let area = to.area;
let mut x2 = 0;
let Rect { x, y, height, .. } = area;
for (scene_index, scene) in scenes.iter().enumerate() {
@ -191,11 +201,11 @@ impl<'a> Render for TrackScenesColumn<'a> {
Style::default().dim()
});
for y in y+1..y+height {
"".blit(buf, x + x2, y, sep)?;
"".blit(to.buffer, x + x2, y, sep)?;
}
let name = scene.name.read().unwrap();
let mut x3 = name.len() as u16;
name.blit(buf, x + x2, y, sep)?;
name.blit(to.buffer, x + x2, y, sep)?;
for (i, clip) in scene.clips.iter().enumerate() {
let active_track = selected.track() == Some(i);
if let Some(clip) = clip {
@ -204,7 +214,7 @@ impl<'a> Render for TrackScenesColumn<'a> {
Some(phrase) => &format!("{}", phrase.read().unwrap().name.read().unwrap()),
None => "...."
};
label.blit(buf, x + x2, y2, Some(if active_track && active_scene {
label.blit(to.buffer, x + x2, y2, Some(if active_track && active_scene {
Style::default().not_dim().yellow().bold()
} else {
Style::default().not_dim()
@ -214,6 +224,6 @@ impl<'a> Render for TrackScenesColumn<'a> {
}
x2 = x2 + x3 + 1;
}
Ok(Rect { x, y, height, width: x2 })
Ok(Some(Rect { x, y, height, width: x2 }))
}
}

View file

@ -1,33 +1,39 @@
use crate::*;
/// Draw arranger with 1 row per scene.
pub fn draw_compact_1 (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
pub fn draw_compact_1 <'a> (
state: &Arranger<TuiOutput<'a>, Rect>, to: &mut TuiOutput<'a>
) -> Perhaps<Rect> {
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, buf, area, track_cols.as_slice(), scene_rows.as_slice())
draw(state, to, track_cols.as_slice(), scene_rows.as_slice())
}
/// Draw arranger with 2 rows per scene.
pub fn draw_compact_2 (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
pub fn draw_compact_2 <'a> (
state: &Arranger<TuiOutput<'a>, Rect>, to: &mut TuiOutput<'a>
) -> Perhaps<Rect> {
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, buf, area, track_cols.as_slice(), scene_rows.as_slice())
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 (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
pub fn draw_expanded <'a> (
state: &Arranger<TuiOutput<'a>, Rect>, to: &mut TuiOutput<'a>
) -> Perhaps<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, to, track_cols.as_slice(), scene_rows.as_slice())
}
pub fn draw (
state: &Arranger,
buf: &mut Buffer,
mut area: Rect,
cols: &[(usize, usize)],
rows: &[(usize, usize)],
) -> Usually<Rect> {
pub fn draw <'a, 'b> (
state: &Arranger<TuiOutput<'a>, Rect>,
to: &mut TuiOutput<'a>,
cols: &'b [(usize, usize)],
rows: &'b [(usize, usize)],
) -> Perhaps<Rect> {
let mut area = to.area;
area.height = 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();
@ -40,42 +46,44 @@ pub fn draw (
.add_ref(&Split::down()
.add_ref(&TracksHeader(offset, cols, tracks))
.add_ref(&SceneRows(offset, cols, rows, tracks, scenes)))
.render(buf, area)
.render(to)
}
struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]);
impl<'a> Render for ColumnSeparators<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for ColumnSeparators<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
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.height+area.y {
"".blit(buf, x, y, style)?;
"".blit(to.buffer, x, y, style)?;
}
}
Ok(area)
Ok(Some(area))
}
}
struct RowSeparators<'a>(&'a [(usize, usize)]);
impl<'a> Render for RowSeparators<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for RowSeparators<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let mut area = to.area;
let Self(rows) = self;
for (_, y) in rows.iter() {
let y = area.y + (*y / 96) as u16 + 1;
if y >= buf.area.height {
if y >= to.buffer.area.height {
break
}
for x in area.x..area.width+area.y-2 {
let cell = buf.get_mut(x, y);
let cell = to.buffer.get_mut(x, y);
cell.modifier = Modifier::UNDERLINED;
cell.underline_color = Nord::SEPARATOR;
}
}
Ok(area)
Ok(Some(area))
}
}
@ -83,8 +91,9 @@ struct CursorFocus<'a>(
ArrangerFocus, u16, &'a [(usize, usize)], &'a [(usize, usize)]
);
impl<'a> Render for CursorFocus<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for CursorFocus<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let mut area = to.area;
let Self(selected, offset, cols, rows) = *self;
let get_track_area = |t: usize| Rect {
x: offset + area.x + cols[t].1 as u16 - 1,
@ -109,7 +118,7 @@ impl<'a> Render for CursorFocus<'a> {
let mut clip_area: Option<Rect> = None;
let area = match selected {
ArrangerFocus::Mix => {
fill_bg(buf, area, COLOR_BG0);
fill_bg(to.buffer, area, COLOR_BG0);
area
},
ArrangerFocus::Track(t) => {
@ -128,19 +137,19 @@ impl<'a> Render for CursorFocus<'a> {
},
};
if let Some(Rect { x, y, width, height }) = track_area {
fill_fg(buf, Rect { x, y, width: 1, height }, COLOR_BG5);
fill_fg(buf, Rect { x: x + width, y, width: 1, height }, COLOR_BG5);
fill_fg(to.buffer, Rect { x, y, width: 1, height }, COLOR_BG5);
fill_fg(to.buffer, Rect { x: x + width, y, width: 1, height }, COLOR_BG5);
}
if let Some(Rect { y, height, .. }) = scene_area {
fill_ul(buf, Rect { x: area.x, y: y - 1, width: area.width, height: 1 }, COLOR_BG5);
fill_ul(buf, Rect { x: area.x, y: y + height - 1, width: area.width, height: 1 }, COLOR_BG5);
fill_ul(to.buffer, Rect { x: area.x, y: y - 1, width: area.width, height: 1 }, COLOR_BG5);
fill_ul(to.buffer, Rect { x: area.x, y: y + height - 1, width: area.width, height: 1 }, COLOR_BG5);
}
if let Some(clip_area) = clip_area {
fill_bg(buf, clip_area, COLOR_BG0);
fill_bg(to.buffer, clip_area, COLOR_BG0);
} else if let Some(track_area) = track_area {
fill_bg(buf, track_area, COLOR_BG0);
fill_bg(to.buffer, track_area, COLOR_BG0);
} else if let Some(scene_area) = scene_area {
fill_bg(buf, scene_area, COLOR_BG0);
fill_bg(to.buffer, scene_area, COLOR_BG0);
}
Ok(area)
}
@ -148,8 +157,9 @@ impl<'a> Render for CursorFocus<'a> {
struct TracksHeader<'a>(u16, &'a[(usize, usize)], &'a [Sequencer]);
impl<'a> Render for TracksHeader<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for TracksHeader<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let mut area = to.area;
let Self(offset, track_cols, tracks) = *self;
let Rect { y, width, .. } = area;
for (track, (w, x)) in tracks.iter().zip(track_cols) {
@ -158,17 +168,18 @@ impl<'a> Render for TracksHeader<'a> {
break
}
let name = track.name.read().unwrap();
fill_bg(buf, Rect { x: offset + x, y, width: *w as u16, height: 2 }, COLOR_BG1);
name.blit(buf, offset + x + 1, y, Some(Style::default().white()))?;
fill_bg(to.buffer, Rect { x: offset + x, y, width: *w as u16, height: 2 }, COLOR_BG1);
name.blit(to.buffer, offset + x + 1, y, Some(Style::default().white()))?;
}
Ok(Rect { x: area.x, y, width, height: 2 })
Ok(Some(Rect { x: area.x, y, width, height: 2 }))
}
}
struct SceneRows<'a>(u16, &'a[(usize, usize)], &'a[(usize, usize)], &'a[Sequencer], &'a[Scene]);
impl<'a> Render for SceneRows<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for SceneRows<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let area = to.area;
let Self(offset, track_cols, scene_rows, tracks, scenes) = *self;
let black = Some(Style::default().fg(Nord::SEPARATOR));
let Rect { mut y, height, .. } = area;
@ -176,7 +187,7 @@ impl<'a> Render for SceneRows<'a> {
let x = *x as u16;
if x > 0 {
for y in area.y-2..y-2 {
"".blit(buf, x - 1, y, black)?;
"".blit(to.buffer, x - 1, y, black)?;
}
}
}
@ -185,28 +196,27 @@ impl<'a> Render for SceneRows<'a> {
//break
//}
let h = 1.max((pulses / 96) as u16);
SceneRow(tracks, scene, track_cols, offset).render(buf, Rect {
x: area.x,
y,
width: area.width,
height: h,//.min(area.height - y)
SceneRow(tracks, scene, track_cols, offset).render(&mut TuiOutput {
buffer: to.buffer,
area: Rect { x: area.x, y, width: area.width, height: h, }
})?;
y = y + h
}
Ok(area)
Ok(Some(area))
}
}
struct SceneRow<'a>(&'a[Sequencer], &'a Scene, &'a[(usize, usize)], u16);
impl<'a> Render for SceneRow<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for SceneRow<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let mut area = to.area;
let Self(tracks, scene, track_cols, offset) = self;
let Rect { x, y, width, .. } = area;
let playing = scene.is_playing(tracks);
(if playing { "" } else { " " }).blit(buf, x, y, None)?;
scene.name.read().unwrap().blit(buf, x + 1, y, Some(Style::default().white()))?;
fill_bg(buf, Rect { x: x, y, width: offset.saturating_sub(1), height: area.height }, COLOR_BG1);
(if playing { "" } else { " " }).blit(to.buffer, x, y, None)?;
scene.name.read().unwrap().blit(to.buffer, x + 1, y, Some(Style::default().white()))?;
fill_bg(to.buffer, Rect { x: x, y, width: offset.saturating_sub(1), height: area.height }, COLOR_BG1);
for (track, (w, x)) in track_cols.iter().enumerate() {
let x = *x as u16 + offset;
@ -217,33 +227,36 @@ impl<'a> Render for SceneRow<'a> {
if let (Some(track), Some(Some(clip))) = (
tracks.get(track), scene.clips.get(track)
) {
let area = Rect { x, y, width: *w as u16, height: area.height, };
SceneClip(track, *clip).render(buf, area)?;
SceneClip(track, *clip).render(&mut TuiOutput {
buffer: to.buffer,
area: Rect { x, y, width: *w as u16, height: area.height, }
})?;
}
}
Ok(area)
Ok(Some(area))
}
}
struct SceneClip<'a>(&'a Sequencer, usize);
impl<'a> Render for SceneClip<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
impl<'a> Render<TuiOutput<'a>, Rect> for SceneClip<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
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();
format!("{clip:02} {name}").blit(buf, area.x + 1, area.y, style)?;
fill_bg(buf, area, if track.sequence == Some(*clip) {
format!("{clip:02} {name}").blit(to.buffer, area.x + 1, area.y, style)?;
fill_bg(to.buffer, area, if track.sequence == Some(*clip) {
Nord::PLAYING
} else {
COLOR_BG1
});
} else {
fill_bg(buf, area, COLOR_BG0)
fill_bg(to.buffer, area, COLOR_BG0)
}
Ok(area)
Ok(Some(area))
}
}

View file

@ -12,7 +12,6 @@ submod! {
arranger_focus
arranger_handle
arranger_track
arranger_view
arranger_rename
midi
phrase

View file

@ -146,7 +146,7 @@ impl Phrase {
}}
}
impl Arranger {
impl<T, U> Arranger<T, U> {
pub fn phrase (&self) -> Option<&Arc<RwLock<Phrase>>> {
let track_id = self.selected.track()?;
self.tracks.get(track_id)?.phrases.get((*self.scene()?.clips.get(track_id)?)?)

View file

@ -79,7 +79,7 @@ pub fn scene_ppqs (tracks: &[Sequencer], scenes: &[Scene]) -> Vec<(usize, usize)
scenes
}
impl Arranger {
impl<T, U> Arranger<T, U> {
pub fn scene (&self) -> Option<&Scene> {
self.selected.scene().map(|s|self.scenes.get(s)).flatten()
}

View file

@ -1,12 +1,12 @@
use crate::*;
impl<'a> Render<TuiOutput<'a>, Rect> for Sequencer {
fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps<Rect> {
self.horizontal_draw(target)?;
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
self.horizontal_draw(to)?;
if self.focused && self.entered {
Corners(Style::default().green().not_dim()).draw(buf, area)?;
Corners(Style::default().green().not_dim()).draw(to.buffer, to.area)?;
}
Ok(area)
Ok(Some(to.area))
}
}

View file

@ -4,17 +4,26 @@ impl Sequencer {
const H_KEYS_OFFSET: usize = 5;
pub(crate) fn horizontal_draw (&self, buf: &mut Buffer, mut area: Rect) -> Usually<()> {
pub(crate) fn horizontal_draw <'a> (&self, to: &mut TuiOutput<'a>) -> Usually<()> {
let mut area = to.area;
Split::down()
.add_ref(&SequenceName(&self))
.add_ref(&SequenceRange)
.add_ref(&SequenceLoopRange)
.add_ref(&SequenceNoteRange)
.render(buf, Rect { x: area.x, y: area.y, width: 10, height: area.height })?;
.render(&mut TuiOutput {
buffer: to.buffer,
area: Rect {
x: area.x,
y: area.y,
height: area.height,
width: 10,
}
})?;
area.x = area.x + 10;
area.width = area.width.saturating_sub(10);
area.height = area.height.min(66);
Lozenge(Style::default().fg(Nord::BG2)).draw(buf, area)?;
Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, area)?;
area.x = area.x + 1;
area.width = area.width.saturating_sub(1);
Layered::new()
@ -23,7 +32,7 @@ impl Sequencer {
.add_ref(&SequenceNotes(&self))
.add_ref(&SequenceCursor(&self))
.add_ref(&SequenceZoom(&self))
.render(buf, area)?;
.render(&mut TuiOutput { buffer: to.buffer, area: area })?;
Ok(())
}
@ -48,102 +57,102 @@ const STYLE_VALUE: Option<Style> = Some(Style {
struct SequenceName<'a>(&'a Sequencer);
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceName<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, .. } = area;
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, .. } = to.area;
let frame = Rect { x, y, width: 10, height: 4 };
Lozenge(Style::default().fg(Nord::BG2)).draw(buf, frame)?;
"Name:".blit(buf, x + 1, y + 1, STYLE_LABEL)?;
self.0.name.read().unwrap().blit(buf, x + 1, y + 2, STYLE_VALUE)?;
Ok(frame)
Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, frame)?;
"Name:".blit(to.buffer, x + 1, y + 1, STYLE_LABEL)?;
self.0.name.read().unwrap().blit(to.buffer, x + 1, y + 2, STYLE_VALUE)?;
Ok(Some(frame))
}
}
struct SequenceRange;
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceRange {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, .. } = area;
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, .. } = to.area;
let frame = Rect { x, y, width: 10, height: 6 };
Lozenge(Style::default().fg(Nord::BG2)).draw(buf, frame)?;
"Start: ".blit(buf, x + 1, y + 1, STYLE_LABEL)?;
" 1.1.1".blit(buf, x + 1, y + 2, STYLE_VALUE)?;
"End: ".blit(buf, x + 1, y + 3, STYLE_LABEL)?;
" 2.1.1".blit(buf, x + 1, y + 4, STYLE_VALUE)?;
Ok(frame)
Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, frame)?;
"Start: ".blit(to.buffer, x + 1, y + 1, STYLE_LABEL)?;
" 1.1.1".blit(to.buffer, x + 1, y + 2, STYLE_VALUE)?;
"End: ".blit(to.buffer, x + 1, y + 3, STYLE_LABEL)?;
" 2.1.1".blit(to.buffer, x + 1, y + 4, STYLE_VALUE)?;
Ok(Some(frame))
}
}
struct SequenceLoopRange;
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceLoopRange {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, .. } = area;
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, .. } = to.area;
let range = Rect { x, y, width: 10, height: 7 };
Lozenge(Style::default().fg(Nord::BG2)).draw(buf, range)?;
"Loop [ ]".blit(buf, x + 1, y + 1, STYLE_LABEL)?;
"From: ".blit(buf, x + 1, y + 2, STYLE_LABEL)?;
" 1.1.1".blit(buf, x + 1, y + 3, STYLE_VALUE)?;
"Length: ".blit(buf, x + 1, y + 4, STYLE_LABEL)?;
" 1.0.0".blit(buf, x + 1, y + 5, STYLE_VALUE)?;
Ok(range)
Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, range)?;
"Loop [ ]".blit(to.buffer, x + 1, y + 1, STYLE_LABEL)?;
"From: ".blit(to.buffer, x + 1, y + 2, STYLE_LABEL)?;
" 1.1.1".blit(to.buffer, x + 1, y + 3, STYLE_VALUE)?;
"Length: ".blit(to.buffer, x + 1, y + 4, STYLE_LABEL)?;
" 1.0.0".blit(to.buffer, x + 1, y + 5, STYLE_VALUE)?;
Ok(Some(range))
}
}
struct SequenceNoteRange;
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceNoteRange {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, .. } = area;
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, .. } = to.area;
let range = Rect { x, y, width: 10, height: 9 };
Lozenge(Style::default().fg(Nord::BG2)).draw(buf, range)?;
"Notes: ".blit(buf, x + 1, y + 1, STYLE_LABEL)?;
"C#0-C#9 ".blit(buf, x + 1, y + 2, STYLE_VALUE)?;
"[ /2 ]".blit(buf, x + 1, y + 3, STYLE_LABEL)?;
"[ x2 ]".blit(buf, x + 1, y + 4, STYLE_LABEL)?;
"[ Rev ]".blit(buf, x + 1, y + 5, STYLE_LABEL)?;
"[ Inv ]".blit(buf, x + 1, y + 6, STYLE_LABEL)?;
"[ Dup ]".blit(buf, x + 1, y + 7, STYLE_LABEL)?;
Ok(area)
Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, range)?;
"Notes: ".blit(to.buffer, x + 1, y + 1, STYLE_LABEL)?;
"C#0-C#9 ".blit(to.buffer, x + 1, y + 2, STYLE_VALUE)?;
"[ /2 ]".blit(to.buffer, x + 1, y + 3, STYLE_LABEL)?;
"[ x2 ]".blit(to.buffer, x + 1, y + 4, STYLE_LABEL)?;
"[ Rev ]".blit(to.buffer, x + 1, y + 5, STYLE_LABEL)?;
"[ Inv ]".blit(to.buffer, x + 1, y + 6, STYLE_LABEL)?;
"[ Dup ]".blit(to.buffer, x + 1, y + 7, STYLE_LABEL)?;
Ok(Some(to.area))
}
}
struct SequenceKeys<'a>(&'a Sequencer);
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceKeys<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
if area.height < 2 {
return Ok(area)
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
if to.area.height < 2 {
return Ok(Some(to.area))
}
let area = Rect {
x: area.x,
y: area.y + 1,
x: to.area.x,
y: to.area.y + 1,
width: 5,
height: area.height - 2
height: to.area.height - 2
};
buffer_update(buf, area, &|cell, x, y|{
buffer_update(to.buffer, area, &|cell, x, y|{
let y = y + self.0.note_axis.start as u16;
if x < self.0.keys.area.width && y < self.0.keys.area.height {
*cell = self.0.keys.get(x, y).clone()
}
});
Ok(area)
Ok(Some(to.area))
}
}
struct SequenceNotes<'a>(&'a Sequencer);
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceNotes<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
if area.height < 2 {
return Ok(area)
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
if to.area.height < 2 {
return Ok(Some(to.area))
}
let area = Rect {
x: area.x + Sequencer::H_KEYS_OFFSET as u16,
y: area.y + 1,
width: area.width - Sequencer::H_KEYS_OFFSET as u16,
height: area.height - 2
x: to.area.x + Sequencer::H_KEYS_OFFSET as u16,
y: to.area.y + 1,
width: to.area.width - Sequencer::H_KEYS_OFFSET as u16,
height: to.area.height - 2
};
buffer_update(buf, area, &move |cell, x, y|{
buffer_update(to.buffer, area, &move |cell, x, y|{
let src_x = ((x as usize + self.0.time_axis.start) * self.0.time_axis.scale) as usize;
let src_y = (y as usize + self.0.note_axis.start) as usize;
if src_x < self.0.buffer.width && src_y < self.0.buffer.height - 1 {
@ -154,21 +163,21 @@ impl<'a> Render<TuiOutput<'a>, Rect> for SequenceNotes<'a> {
});
}
});
Ok(area)
Ok(Some(to.area))
}
}
struct SequenceCursor<'a>(&'a Sequencer);
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceCursor<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
if let (Some(time), Some(note)) = (self.0.time_axis.point, self.0.note_axis.point) {
let x = area.x + Sequencer::H_KEYS_OFFSET as u16 + time as u16;
let y = area.y + 1 + note as u16 / 2;
let x = to.area.x + Sequencer::H_KEYS_OFFSET as u16 + time as u16;
let y = to.area.y + 1 + note as u16 / 2;
let c = if note % 2 == 0 { "" } else { "" };
c.blit(buf, x, y, self.0.style_focus())
c.blit(to.buffer, x, y, self.0.style_focus())
} else {
Ok(Rect::default())
Ok(Some(Rect::default()))
}
}
}
@ -176,31 +185,31 @@ impl<'a> Render<TuiOutput<'a>, Rect> for SequenceCursor<'a> {
struct SequenceZoom<'a>(&'a Sequencer);
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceZoom<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let quant = ppq_to_name(self.0.time_axis.scale);
let quant_x = area.x + area.width - 1 - quant.len() as u16;
let quant_y = area.y + area.height - 2;
quant.blit(buf, quant_x, quant_y, self.0.style_focus())
let quant_x = to.area.x + to.area.width - 1 - quant.len() as u16;
let quant_y = to.area.y + to.area.height - 2;
quant.blit(to.buffer, quant_x, quant_y, self.0.style_focus())
}
}
struct SequenceTimer<'a>(&'a Sequencer, Arc<RwLock<Phrase>>);
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceTimer<'a> {
fn render (&self, to: &mut TuiOutput<'a>) -> Usually<Rect> {
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let phrase = self.1.read().unwrap();
let (time0, time_z, now) = (
self.0.time_axis.start, self.0.time_axis.scale, self.0.now % phrase.length
);
let Rect { x, width, .. } = area;
let Rect { x, width, .. } = to.area;
let x2 = x as usize + Sequencer::H_KEYS_OFFSET;
let x3 = x as usize + width as usize;
for x in x2..x3 {
let step = (time0 + x2) * time_z;
let next_step = (time0 + x2 + 1) * time_z;
let style = Sequencer::style_timer_step(now, step as usize, next_step as usize);
"-".blit(buf, x as u16, area.y, Some(style))?;
"-".blit(to.buffer, x as u16, to.area.y, Some(style))?;
}
return Ok(Rect { x: area.x, y: area.y, width: area.width, height: 1 })
return Ok(Some(Rect { x: to.area.x, y: to.area.y, width: to.area.width, height: 1 }))
}
}

View file

@ -22,52 +22,7 @@ pub struct TransportToolbar {
pub sync: TransportSync,
pub clock: TransportClock,
}
focusable!(TransportToolbar);
focus!(TransportToolbar (focus) : 5 => [
playing, bpm, quant, sync, clock
]);
process!(TransportToolbar |self, _client, scope| {
self.update(&scope);
Control::Continue
});
pub struct TransportPlayPauseButton {
pub value: Option<TransportState>,
pub focused: bool
}
focusable!(TransportPlayPauseButton);
pub struct TransportBPM {
pub value: f64,
pub focused: bool
}
focusable!(TransportBPM);
pub struct TransportQuantize {
pub value: usize,
pub focused: bool
}
focusable!(TransportQuantize);
pub struct TransportSync {
pub value: usize,
pub focused: bool
}
focusable!(TransportSync);
pub struct TransportClock {
pub frame: usize,
pub pulse: usize,
pub ppq: usize,
pub usecs: usize,
pub focused: bool,
}
focusable!(TransportClock);
impl TransportToolbar {
pub fn standalone () -> Usually<Arc<RwLock<Self>>> {
let mut transport = Self::new(None);
transport.focused = true;
@ -86,7 +41,6 @@ impl TransportToolbar {
);
Ok(transport)
}
pub fn new (transport: Option<Transport>) -> Self {
let timebase = Arc::new(Timebase::default());
Self {
@ -124,7 +78,6 @@ impl TransportToolbar {
jack: None,
}
}
pub fn toggle_play (&mut self) -> Usually<()> {
let transport = self.transport.as_ref().unwrap();
self.playing.value = match self.playing.value.expect("1st frame has not been processed yet") {
@ -140,7 +93,6 @@ impl TransportToolbar {
};
Ok(())
}
pub fn update (&mut self, scope: &ProcessScope) -> (bool, usize, usize, usize, usize, f64) {
let CycleTimes {
current_frames,
@ -177,21 +129,122 @@ impl TransportToolbar {
period_usecs as f64
)
}
pub fn bpm (&self) -> usize {
self.timebase.bpm() as usize
}
pub fn ppq (&self) -> usize {
self.timebase.ppq() as usize
}
pub fn pulse (&self) -> usize {
self.timebase.frame_to_pulse(self.clock.frame as f64) as usize
}
pub fn usecs (&self) -> usize {
self.timebase.frame_to_usec(self.clock.frame as f64) as usize
}
}
impl<'a> Focus<5, TuiOutput<'a>, Rect> for TransportToolbar {
fn focus (&self) -> usize {
self.focus
}
fn focus_mut (&mut self) -> &mut usize {
&mut self.focus
}
fn focusable (&self) -> [&dyn Focusable<TuiOutput<'a>, Rect>;5] {
[
&self.playing as &dyn Focusable<TuiOutput<'a>, Rect>,
&self.bpm as &dyn Focusable<TuiOutput<'a>, Rect>,
&self.quant as &dyn Focusable<TuiOutput<'a>, Rect>,
&self.sync as &dyn Focusable<TuiOutput<'a>, Rect>,
&self.clock as &dyn Focusable<TuiOutput<'a>, Rect>,
]
}
fn focusable_mut (&mut self) -> [&mut dyn Focusable<TuiOutput<'a>, Rect>;5] {
[
&mut self.playing as &mut dyn Focusable<TuiOutput<'a>, Rect>,
&mut self.bpm as &mut dyn Focusable<TuiOutput<'a>, Rect>,
&mut self.quant as &mut dyn Focusable<TuiOutput<'a>, Rect>,
&mut self.sync as &mut dyn Focusable<TuiOutput<'a>, Rect>,
&mut self.clock as &mut dyn Focusable<TuiOutput<'a>, Rect>,
]
}
}
impl<'a> Focusable<TuiOutput<'a>, Rect> for TransportToolbar {
fn is_focused (&self) -> bool {
self.focused
}
fn set_focused (&mut self, focused: bool) {
self.focused = focused
}
}
process!(TransportToolbar |self, _client, scope| {
self.update(&scope);
Control::Continue
});
pub struct TransportPlayPauseButton {
pub value: Option<TransportState>,
pub focused: bool
}
impl<'a> Focusable<TuiOutput<'a>, Rect> for TransportPlayPauseButton {
fn is_focused (&self) -> bool {
self.focused
}
fn set_focused (&mut self, focused: bool) {
self.focused = focused
}
}
pub struct TransportBPM {
pub value: f64,
pub focused: bool
}
impl<'a> Focusable<TuiOutput<'a>, Rect> for TransportBPM {
fn is_focused (&self) -> bool {
self.focused
}
fn set_focused (&mut self, focused: bool) {
self.focused = focused
}
}
pub struct TransportQuantize {
pub value: usize,
pub focused: bool
}
impl<'a> Focusable<TuiOutput<'a>, Rect> for TransportQuantize {
fn is_focused (&self) -> bool {
self.focused
}
fn set_focused (&mut self, focused: bool) {
self.focused = focused
}
}
pub struct TransportSync {
pub value: usize,
pub focused: bool
}
impl<'a> Focusable<TuiOutput<'a>, Rect> for TransportSync {
fn is_focused (&self) -> bool {
self.focused
}
fn set_focused (&mut self, focused: bool) {
self.focused = focused
}
}
pub struct TransportClock {
pub frame: usize,
pub pulse: usize,
pub ppq: usize,
pub usecs: usize,
pub focused: bool,
}
impl<'a> Focusable<TuiOutput<'a>, Rect> for TransportClock {
fn is_focused (&self) -> bool {
self.focused
}
fn set_focused (&mut self, focused: bool) {
self.focused = focused
}
}

View file

@ -22,8 +22,8 @@ impl<'a> Render<TuiOutput<'a>, Rect> for TransportToolbar {
}
impl<'a> Render<TuiOutput<'a>, Rect> for TransportPlayPauseButton {
fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, .. } = target.area;
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, .. } = to.area;
let Self { value, focused } = &self;
let style = Some(match value {
Some(TransportState::Stopped) => GRAY_DIM.bold(),
@ -37,85 +37,86 @@ impl<'a> Render<TuiOutput<'a>, Rect> for TransportPlayPauseButton {
Some(TransportState::Stopped) => "⏹ STOPPED",
_ => unreachable!(),
};
let mut area = label.blit(target.buffer, x + 1, y, style)?.unwrap();
let mut area = label.blit(to.buffer, x + 1, y, style)?.unwrap();
area.width = area.width + 1;
area.height = area.height + 1;
if *focused {
let area = Rect { x: area.x - 1, width: area.width - 1, ..area };
CORNERS.draw(target)?;
fill_bg(target.buffer, target.area, COLOR_BG1);
CORNERS.draw(to.buffer, to.area)?;
fill_bg(to.buffer, to.area, COLOR_BG1);
}
Ok(area)
Ok(Some(area))
}
}
impl<'a> Render<TuiOutput<'a>, Rect> for TransportBPM {
fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, .. } = area;
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, .. } = to.area;
let Self { value, focused } = self;
"BPM".blit(buf, x, y, Some(NOT_DIM))?;
let width = format!("{}.{:03}", value, (value * 1000.0) % 1000.0).blit(buf, x, y + 1, Some(NOT_DIM_BOLD))?.width;
"BPM".blit(to.buffer, x, y, Some(NOT_DIM))?;
let width = format!("{}.{:03}", value, (value * 1000.0) % 1000.0)
.blit(to.buffer, x, y + 1, Some(NOT_DIM_BOLD))?.width;
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
if *focused {
let area = Rect { x: area.x - 1, width: area.width - 1, ..area };
CORNERS.draw(buf, area)?;
fill_bg(buf, area, COLOR_BG1);
CORNERS.draw(to.buffer, area)?;
fill_bg(to.buffer, area, COLOR_BG1);
}
Ok(area)
Ok(Some(area))
}
}
impl<'a> Render<TuiOutput<'a>, Rect> for TransportQuantize {
fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, .. } = area;
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, .. } = to.area;
let Self { value, focused } = self;
"QUANT".blit(buf, x, y, Some(NOT_DIM))?;
let width = ppq_to_name(*value as usize).blit(buf, x, y + 1, Some(NOT_DIM_BOLD))?.width;
"QUANT".blit(to.buffer, x, y, Some(NOT_DIM))?;
let width = ppq_to_name(*value as usize).blit(to.buffer, x, y + 1, Some(NOT_DIM_BOLD))?.width;
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
if *focused {
let area = Rect { x: area.x - 1, width: area.width - 1, ..area };
CORNERS.draw(buf, area)?;
fill_bg(buf, area, COLOR_BG1);
CORNERS.draw(to.buffer, area)?;
fill_bg(to.buffer, area, COLOR_BG1);
}
Ok(area)
Ok(Some(area))
}
}
impl<'a> Render<TuiOutput<'a>, Rect> for TransportSync {
fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, .. } = area;
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, .. } = to.area;
let Self { value, focused } = self;
"SYNC".blit(buf, x, y, Some(NOT_DIM))?;
let width = ppq_to_name(*value as usize).blit(buf, x, y + 1, Some(NOT_DIM_BOLD))?.width;
"SYNC".blit(to.buffer, x, y, Some(NOT_DIM))?;
let width = ppq_to_name(*value as usize).blit(to.buffer, x, y + 1, Some(NOT_DIM_BOLD))?.width;
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
if *focused {
let area = Rect { x: area.x - 1, width: area.width - 1, ..area };
CORNERS.draw(buf, area)?;
fill_bg(buf, area, COLOR_BG1);
CORNERS.draw(to.buffer, area)?;
fill_bg(to.buffer, area, COLOR_BG1);
}
Ok(area)
Ok(Some(area))
}
}
impl<'a> Render<TuiOutput<'a>, Rect> for TransportClock {
fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, width, .. } = area;
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
let Rect { x, y, width, .. } = to.area;
let Self { frame, pulse, ppq, usecs, focused } = self;
let (beats, pulses) = if *ppq > 0 { (pulse / ppq, pulse % ppq) } else { (0, 0) };
let (bars, beats) = ((beats / 4) + 1, (beats % 4) + 1);
let (seconds, msecs) = (usecs / 1000000, usecs / 1000 % 1000);
let (minutes, seconds) = (seconds / 60, seconds % 60);
let timer = format!("{bars}.{beats}.{pulses:02}");
timer.blit(buf, x + width - timer.len() as u16 - 1, y + 0, Some(NOT_DIM))?;
timer.blit(to.buffer, x + width - timer.len() as u16 - 1, y + 0, Some(NOT_DIM))?;
let timer = format!("{minutes}:{seconds:02}:{msecs:03}");
timer.blit(buf, x + width - timer.len() as u16 - 1, y + 1, Some(NOT_DIM))?;
let mut area = area;
timer.blit(to.buffer, x + width - timer.len() as u16 - 1, y + 1, Some(NOT_DIM))?;
let mut area = to.area;
area.width = area.width + 1;
if *focused {
let area = Rect { x: area.x - 1, ..area };
CORNERS.draw(buf, area)?;
fill_bg(buf, area, COLOR_BG1);
CORNERS.draw(to.buffer, area)?;
fill_bg(to.buffer, area, COLOR_BG1);
}
Ok(area)
Ok(Some(area))
}
}