mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
arranger, transport: despaghettify
This commit is contained in:
parent
33bdf65e8d
commit
1104093395
10 changed files with 430 additions and 253 deletions
|
|
@ -53,6 +53,7 @@ submod! {
|
|||
render_border
|
||||
render_collect
|
||||
render_fill
|
||||
render_layered
|
||||
render_split
|
||||
render_theme
|
||||
time_base
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
use crate::*;
|
||||
pub(crate) use ratatui::prelude::CrosstermBackend;
|
||||
pub(crate) use ratatui::style::{Stylize, Style, Color};
|
||||
pub(crate) use ratatui::style::Style;
|
||||
pub(crate) use ratatui::layout::Rect;
|
||||
pub(crate) use ratatui::buffer::{Buffer, Cell};
|
||||
use ratatui::widgets::WidgetRef;
|
||||
|
||||
/// Main thread render loop
|
||||
pub fn render_thread (
|
||||
|
|
@ -37,7 +36,7 @@ pub fn render_thread (
|
|||
}
|
||||
|
||||
/// Trait for things that render to the display.
|
||||
pub trait Render {
|
||||
pub trait Render: Send + Sync {
|
||||
// Render something to an area of the buffer.
|
||||
// Returns area used by component.
|
||||
// This is insufficient but for the most basic dynamic layout algorithms.
|
||||
|
|
@ -76,23 +75,23 @@ impl Render for () {
|
|||
}
|
||||
}
|
||||
|
||||
//impl<T: Render> Render for &T {
|
||||
//fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
//(*self).render(buf, area)
|
||||
//}
|
||||
//fn into_collected <'a> (self) -> Collected<'a> where Self: Sized + 'a {
|
||||
//Collected::Ref(self)
|
||||
//}
|
||||
//}
|
||||
impl<T: Render> Render for &T {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
(*self).render(buf, area)
|
||||
}
|
||||
fn into_collected <'a> (self) -> Collected<'a> where Self: Sized + 'a {
|
||||
Collected::Ref(self)
|
||||
}
|
||||
}
|
||||
|
||||
//impl<T: Render> Render for &mut T {
|
||||
//fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
//(**self).render(buf, area)
|
||||
//}
|
||||
//fn into_collected <'a> (self) -> Collected<'a> where Self: Sized + 'a {
|
||||
//Collected::Ref(self)
|
||||
//}
|
||||
//}
|
||||
impl<T: Render> Render for &mut T {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
(**self).render(buf, area)
|
||||
}
|
||||
fn into_collected <'a> (self) -> Collected<'a> where Self: Sized + 'a {
|
||||
Collected::Ref(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Render> Render for Option<T> {
|
||||
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
|
|
@ -112,18 +111,18 @@ impl<'a> Render for Box<dyn Render + 'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Fn(&mut Buffer, Rect) -> Usually<Rect> + Send + Sync + 'a> Render for T {
|
||||
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
(*self)(b, a)
|
||||
}
|
||||
}
|
||||
|
||||
//impl<'a> Render for Box<dyn Fn(&mut Buffer, Rect) -> Usually<Rect> + Send + Sync + 'a> {
|
||||
//impl<'a, T: Fn(&mut Buffer, Rect) -> Usually<Rect> + Send + Sync + 'a> Render for T {
|
||||
//fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
//(*self)(b, a)
|
||||
//}
|
||||
//}
|
||||
|
||||
impl<'a> Render for Box<dyn Fn(&mut Buffer, Rect) -> Usually<Rect> + Send + Sync + 'a> {
|
||||
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
(*self)(b, a)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Render> Render for Arc<Mutex<T>> {
|
||||
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
self.lock().unwrap().render(b, a)
|
||||
|
|
@ -203,17 +202,6 @@ impl BigBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Layered<'a, const N: usize>(pub [&'a (dyn Render + Sync); N]);
|
||||
|
||||
impl<'a, const N: usize> Render for Layered<'a, N> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
for layer in self.0.iter() {
|
||||
layer.render(buf, area)?;
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct If<'a>(pub bool, pub &'a (dyn Render + Sync));
|
||||
|
||||
impl<'a> Render for If<'a> {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ impl<'a> Render for Collected<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Collection<'a>(Vec<Collected<'a>>);
|
||||
pub struct Collection<'a>(pub Vec<Collected<'a>>);
|
||||
|
||||
impl<'a> Collection<'a> {
|
||||
pub fn new () -> Self {
|
||||
|
|
@ -27,6 +27,9 @@ impl<'a> Collection<'a> {
|
|||
pub trait Collect<'a> {
|
||||
fn add_box (self, item: Box<dyn Render + 'a>) -> Self;
|
||||
fn add_ref (self, item: &'a dyn Render) -> Self;
|
||||
fn add <T: Render + Sized + 'a> (self, item: T) -> Self where Self: Sized {
|
||||
self.add_box(Box::new(item))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Collect<'a> for Collection<'a> {
|
||||
|
|
|
|||
29
crates/tek_core/src/render_layered.rs
Normal file
29
crates/tek_core/src/render_layered.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct Layered<'a>(Collection<'a>);
|
||||
|
||||
impl<'a> Layered<'a> {
|
||||
pub fn new () -> Self {
|
||||
Self(Collection::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Render for Layered<'a> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
for layer in self.0.0.iter() {
|
||||
layer.render(buf, area)?;
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Collect<'a> for Layered<'a> {
|
||||
fn add_box (mut self, item: Box<dyn Render + 'a>) -> Self {
|
||||
self.0 = self.0.add_box(item);
|
||||
self
|
||||
}
|
||||
fn add_ref (mut self, item: &'a dyn Render) -> Self {
|
||||
self.0 = self.0.add_ref(item);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
@ -9,9 +9,9 @@ pub enum Direction {
|
|||
}
|
||||
|
||||
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 <'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)
|
||||
}
|
||||
|
|
@ -23,26 +23,27 @@ impl Direction {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Split<'a, const N: usize>(
|
||||
pub Direction, pub [&'a (dyn Render + Sync);N]
|
||||
);
|
||||
pub struct Split<'a>(Collection<'a>, Direction);
|
||||
|
||||
impl<'a, const N: usize> Split<'a, N> {
|
||||
pub fn down (items: [&'a (dyn Render + Sync);N]) -> Self {
|
||||
Self(Direction::Down, items)
|
||||
impl<'a> Split<'a> {
|
||||
pub fn new (direction: Direction) -> Self {
|
||||
Self(Collection::new(), direction)
|
||||
}
|
||||
pub fn right (items: [&'a (dyn Render + Sync);N]) -> Self {
|
||||
Self(Direction::Right, items)
|
||||
pub fn down () -> Self {
|
||||
Self(Collection::new(), Direction::Down)
|
||||
}
|
||||
pub fn right () -> Self {
|
||||
Self(Collection::new(), Direction::Right)
|
||||
}
|
||||
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 {
|
||||
for item in self.0.0.iter() {
|
||||
if width == 0 || height == 0 {
|
||||
break
|
||||
}
|
||||
let result = item.render(buf, Rect { x, y, width, height })?;
|
||||
match self.0 {
|
||||
match self.1 {
|
||||
Direction::Down => {
|
||||
y = y + result.height;
|
||||
height = height.saturating_sub(result.height);
|
||||
|
|
@ -59,12 +60,65 @@ impl<'a, const N: usize> Split<'a, N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, const N: usize> Render for Split<'a, N> {
|
||||
impl<'a> Render for Split<'a> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
Ok(self.render_areas(buf, area)?.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Collect<'a> for Split<'a> {
|
||||
fn add_box (mut self, item: Box<dyn Render + 'a>) -> Self {
|
||||
self.0 = self.0.add_box(item);
|
||||
self
|
||||
}
|
||||
fn add_ref (mut self, item: &'a dyn Render) -> Self {
|
||||
self.0 = self.0.add_ref(item);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
//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);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::*;
|
||||
use ratatui::style::Modifier;
|
||||
|
||||
pub trait Theme {
|
||||
const BG0: Color;
|
||||
|
|
@ -69,3 +70,75 @@ impl Theme for Nord {
|
|||
const PLAYING: Color = Color::Rgb(60, 100, 50);
|
||||
const SEPARATOR: Color = Color::Rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
pub const GRAY: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::empty(),
|
||||
};
|
||||
|
||||
pub const GRAY_NOT_DIM: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const DIM: Style = Style {
|
||||
fg: None,
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::DIM,
|
||||
sub_modifier: Modifier::empty(),
|
||||
};
|
||||
|
||||
pub const GRAY_DIM: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::DIM,
|
||||
sub_modifier: Modifier::empty(),
|
||||
};
|
||||
|
||||
pub const WHITE_NOT_DIM_BOLD: Style = Style {
|
||||
fg: Some(Color::White),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::BOLD,
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const GRAY_NOT_DIM_BOLD: Style = Style {
|
||||
fg: Some(Color::Gray),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::BOLD,
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const NOT_DIM: Style = Style {
|
||||
fg: None,
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::empty(),
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const NOT_DIM_GREEN: Style = Style {
|
||||
fg: Some(Color::Green),
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::BOLD,
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
||||
pub const NOT_DIM_BOLD: Style = Style {
|
||||
fg: None,
|
||||
bg: None,
|
||||
underline_color: None,
|
||||
add_modifier: Modifier::BOLD,
|
||||
sub_modifier: Modifier::DIM,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,31 +20,6 @@ impl ArrangerViewMode {
|
|||
}
|
||||
}
|
||||
|
||||
struct Split<'a>(Collection<'a>, Direction);
|
||||
|
||||
impl<'a> Split<'a> {
|
||||
fn new (direction: Direction) -> Self {
|
||||
Self(Collection::new(), direction)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Render for Split<'a> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
Ok(area)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Collect<'a> for Split<'a> {
|
||||
fn add_box (mut self, item: Box<dyn Render + 'a>) -> Self {
|
||||
self.0 = self.0.add_box(item);
|
||||
self
|
||||
}
|
||||
fn add_ref (mut self, item: &'a dyn Render) -> Self {
|
||||
self.0 = self.0.add_ref(item);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
render!(Arranger |self, buf, area| {
|
||||
|
||||
let arrangement = Box::new(|buf: &mut Buffer, area: Rect| {
|
||||
|
|
|
|||
|
|
@ -2,34 +2,36 @@ use crate::*;
|
|||
|
||||
pub fn draw (state: &Arranger, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
||||
area.height = area.height.min((2 + state.tracks.len() * 2) as u16);
|
||||
Layered([
|
||||
&FillBg(Nord::bg_lo(state.focused, state.entered)),
|
||||
&Split::right([
|
||||
&track_name_column(state),
|
||||
&track_mon_column(state),
|
||||
&track_rec_column(state),
|
||||
&track_ovr_column(state),
|
||||
&track_del_column(state),
|
||||
&track_gain_column(state),
|
||||
&track_scenes_column(state),
|
||||
]),
|
||||
]).render(buf, area)
|
||||
let tracks = state.tracks.as_slice();
|
||||
Layered::new()
|
||||
.add(FillBg(Nord::bg_lo(state.focused, state.entered)))
|
||||
.add(Split::right()
|
||||
.add(TrackNameColumn(tracks, state.selected))
|
||||
.add(TrackMonitorColumn(tracks))
|
||||
.add(TrackRecordColumn(tracks))
|
||||
.add(TrackOverdubColumn(tracks))
|
||||
.add(TrackEraseColumn(tracks))
|
||||
.add(TrackGainColumn(tracks))
|
||||
.add(TrackScenesColumn(tracks, state.scenes.as_slice(), state.selected)))
|
||||
.render(buf, area)
|
||||
}
|
||||
|
||||
fn track_name_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
||||
let dim = Some(Style::default().dim());
|
||||
let yellow = Some(Style::default().yellow().bold().not_dim());
|
||||
let white = Some(Style::default().white().bold().not_dim());
|
||||
move |buf: &mut Buffer, mut area: Rect| {
|
||||
area.width = 3 + 5.max(track_name_max_len(state.tracks.as_slice())) as u16;
|
||||
struct TrackNameColumn<'a>(&'a [Sequencer], ArrangerFocus);
|
||||
|
||||
impl<'a> Render for TrackNameColumn<'a> {
|
||||
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
||||
let Self(tracks, selected) = self;
|
||||
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, dim)?;
|
||||
"Mixer".blit(buf, 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) = state.tracks.get(index) {
|
||||
let selected = state.selected.track() == Some(index);
|
||||
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.blit(buf, area.x + 4, area.y + y, style)?;
|
||||
|
|
@ -40,17 +42,20 @@ fn track_name_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
|||
}
|
||||
}
|
||||
|
||||
fn track_mon_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
||||
let on = Some(Style::default().not_dim().green().bold());
|
||||
let off = Some(Style::default().dim());
|
||||
move |buf: &mut Buffer, mut area: Rect| {
|
||||
struct TrackMonitorColumn<'a>(&'a [Sequencer]);
|
||||
|
||||
impl<'a> Render for TrackMonitorColumn<'a> {
|
||||
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
||||
let Self(tracks) = self;
|
||||
let on = Some(Style::default().not_dim().green().bold());
|
||||
let off = Some(DIM);
|
||||
area.x = area.x + 1;
|
||||
for y in 0..area.height {
|
||||
if y == 0 {
|
||||
//" MON ".blit(buf, area.x, area.y + y, style2)?;
|
||||
} else if y % 2 == 0 {
|
||||
let index = (y as usize - 2) / 2;
|
||||
if let Some(track) = state.tracks.get(index) {
|
||||
if let Some(track) = tracks.get(index) {
|
||||
let style = if track.monitoring { on } else { off };
|
||||
" MON ".blit(buf, area.x, area.y + y, style)?;
|
||||
} else {
|
||||
|
|
@ -64,17 +69,20 @@ fn track_mon_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
|||
}
|
||||
}
|
||||
|
||||
fn track_rec_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
||||
let on = Some(Style::default().not_dim().red().bold());
|
||||
let off = Some(Style::default().dim());
|
||||
move |buf: &mut Buffer, mut area: Rect| {
|
||||
struct TrackRecordColumn<'a>(&'a [Sequencer]);
|
||||
|
||||
impl<'a> Render for TrackRecordColumn<'a> {
|
||||
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
||||
let Self(tracks) = self;
|
||||
let on = Some(Style::default().not_dim().red().bold());
|
||||
let off = Some(Style::default().dim());
|
||||
area.x = area.x + 1;
|
||||
for y in 0..area.height {
|
||||
if y == 0 {
|
||||
//" REC ".blit(buf, area.x, area.y + y, style2)?;
|
||||
} else if y % 2 == 0 {
|
||||
let index = (y as usize - 2) / 2;
|
||||
if let Some(track) = state.tracks.get(index) {
|
||||
if let Some(track) = tracks.get(index) {
|
||||
let style = if track.recording { on } else { off };
|
||||
" REC ".blit(buf, area.x, area.y + y, style)?;
|
||||
} else {
|
||||
|
|
@ -88,17 +96,20 @@ fn track_rec_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
|||
}
|
||||
}
|
||||
|
||||
fn track_ovr_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
||||
let on = Some(Style::default().not_dim().yellow().bold());
|
||||
let off = Some(Style::default().dim());
|
||||
move |buf: &mut Buffer, mut area: Rect| {
|
||||
struct TrackOverdubColumn<'a>(&'a [Sequencer]);
|
||||
|
||||
impl<'a> Render for TrackOverdubColumn<'a> {
|
||||
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
||||
let Self(tracks) = self;
|
||||
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)?;
|
||||
} else if y % 2 == 0 {
|
||||
let index = (y as usize - 2) / 2;
|
||||
if let Some(track) = state.tracks.get(index) {
|
||||
if let Some(track) = tracks.get(index) {
|
||||
" OVR ".blit(buf, area.x, area.y + y, if track.overdub {
|
||||
on
|
||||
} else {
|
||||
|
|
@ -115,16 +126,19 @@ fn track_ovr_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
|||
}
|
||||
}
|
||||
|
||||
fn track_del_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
||||
let off = Some(Style::default().dim());
|
||||
move |buf: &mut Buffer, mut area: Rect| {
|
||||
struct TrackEraseColumn<'a>(&'a [Sequencer]);
|
||||
|
||||
impl<'a> Render for TrackEraseColumn<'a> {
|
||||
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
||||
let Self(tracks) = self;
|
||||
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)?;
|
||||
} else if y % 2 == 0 {
|
||||
let index = (y as usize - 2) / 2;
|
||||
if let Some(_) = state.tracks.get(index) {
|
||||
if let Some(_) = tracks.get(index) {
|
||||
" DEL ".blit(buf, area.x, area.y + y, off)?;
|
||||
} else {
|
||||
area.height = y;
|
||||
|
|
@ -137,16 +151,19 @@ fn track_del_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
|||
}
|
||||
}
|
||||
|
||||
fn track_gain_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
||||
let off = Some(Style::default().dim());
|
||||
move |buf: &mut Buffer, mut area: Rect| {
|
||||
struct TrackGainColumn<'a>(&'a [Sequencer]);
|
||||
|
||||
impl<'a> Render for TrackGainColumn<'a> {
|
||||
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
||||
let Self(tracks) = self;
|
||||
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)?;
|
||||
} else if y % 2 == 0 {
|
||||
let index = (y as usize - 2) / 2;
|
||||
if let Some(_) = state.tracks.get(index) {
|
||||
if let Some(_) = tracks.get(index) {
|
||||
" +0.0 ".blit(buf, area.x, area.y + y, off)?;
|
||||
} else {
|
||||
area.height = y;
|
||||
|
|
@ -159,12 +176,15 @@ fn track_gain_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
|||
}
|
||||
}
|
||||
|
||||
fn track_scenes_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
||||
|buf: &mut Buffer, area: Rect| {
|
||||
struct TrackScenesColumn<'a>(&'a [Sequencer], &'a [Scene], ArrangerFocus);
|
||||
|
||||
impl<'a> Render for TrackScenesColumn<'a> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let Self(tracks, scenes, selected) = self;
|
||||
let mut x2 = 0;
|
||||
let Rect { x, y, height, .. } = area;
|
||||
for (scene_index, scene) in state.scenes.iter().enumerate() {
|
||||
let active_scene = state.selected.scene() == Some(scene_index);
|
||||
for (scene_index, scene) in scenes.iter().enumerate() {
|
||||
let active_scene = selected.scene() == Some(scene_index);
|
||||
let sep = Some(if active_scene {
|
||||
Style::default().yellow().not_dim()
|
||||
} else {
|
||||
|
|
@ -176,10 +196,10 @@ fn track_scenes_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
|||
let mut x3 = scene.name.len() as u16;
|
||||
scene.name.blit(buf, x + x2, y, sep)?;
|
||||
for (i, clip) in scene.clips.iter().enumerate() {
|
||||
let active_track = state.selected.track() == Some(i);
|
||||
let active_track = selected.track() == Some(i);
|
||||
if let Some(clip) = clip {
|
||||
let y2 = y + 2 + i as u16 * 2;
|
||||
let label = match state.tracks[i].phrases.get(*clip) {
|
||||
let label = match tracks[i].phrases.get(*clip) {
|
||||
Some(phrase) => &format!("{}", phrase.read().unwrap().name),
|
||||
None => "...."
|
||||
};
|
||||
|
|
@ -196,4 +216,3 @@ fn track_scenes_column <'a> (state: &'a Arranger) -> impl Render + 'a {
|
|||
Ok(Rect { x, y, height, width: x2 })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,20 +30,25 @@ pub fn draw (
|
|||
) -> Usually<Rect> {
|
||||
area.height = 2 + (rows[rows.len() - 1].1 / 96) as u16;
|
||||
let offset = 3 + scene_name_max_len(state.scenes.as_ref()) as u16;
|
||||
Layered([
|
||||
&FillBg(Nord::bg_lo(state.focused, state.entered)),
|
||||
&column_separators(offset, cols),
|
||||
&cursor_focus(state, offset, cols, rows),
|
||||
&Split::down([
|
||||
&tracks_header(state, cols, offset),
|
||||
&scene_rows(state, cols, rows, offset),
|
||||
]),
|
||||
&row_separators(rows),
|
||||
]).render(buf, area)
|
||||
let Arranger { focus_sequencer, focused, entered, selected, .. } = *state;
|
||||
let tracks = state.tracks.as_ref();
|
||||
let scenes = state.scenes.as_ref();
|
||||
Layered::new()
|
||||
.add(FillBg(Nord::bg_lo(state.focused, state.entered)))
|
||||
.add(ColumnSeparators(offset, cols))
|
||||
.add(CursorFocus(focus_sequencer, focused, entered, selected, offset, cols, rows))
|
||||
.add(Split::down()
|
||||
.add(TracksHeader(offset, cols, tracks))
|
||||
.add(SceneRows(offset, cols, rows, tracks, scenes)))
|
||||
.add(RowSeparators(rows))
|
||||
.render(buf, area)
|
||||
}
|
||||
|
||||
fn column_separators <'a> (offset: u16, cols: &'a [(usize, usize)]) -> impl Render + 'a {
|
||||
move |buf: &mut Buffer, area: Rect|{
|
||||
struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]);
|
||||
|
||||
impl<'a> Render for ColumnSeparators<'a> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
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;
|
||||
|
|
@ -55,8 +60,11 @@ fn column_separators <'a> (offset: u16, cols: &'a [(usize, usize)]) -> impl Rend
|
|||
}
|
||||
}
|
||||
|
||||
fn row_separators <'a> (rows: &'a [(usize, usize)]) -> impl Render + 'a {
|
||||
move |buf: &mut Buffer, area: Rect| {
|
||||
struct RowSeparators<'a>(&'a [(usize, usize)]);
|
||||
|
||||
impl<'a> Render for RowSeparators<'a> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let Self(rows) = self;
|
||||
for (_, y) in rows.iter() {
|
||||
let y = area.y + (*y / 96) as u16 + 1;
|
||||
if y >= buf.area.height {
|
||||
|
|
@ -72,16 +80,19 @@ fn row_separators <'a> (rows: &'a [(usize, usize)]) -> impl Render + 'a {
|
|||
}
|
||||
}
|
||||
|
||||
fn cursor_focus <'a> (
|
||||
state: &'a Arranger, offset: u16, cols: &'a [(usize, usize)], rows: &'a [(usize, usize)],
|
||||
) -> impl Render + 'a {
|
||||
move |buf: &mut Buffer, area: Rect| {
|
||||
let area = match state.selected {
|
||||
ArrangerFocus::Mix => if state.focused
|
||||
&& state.entered
|
||||
&& state.selected == ArrangerFocus::Mix
|
||||
struct CursorFocus<'a>(
|
||||
bool, bool, bool, ArrangerFocus, u16, &'a [(usize, usize)], &'a [(usize, usize)]
|
||||
);
|
||||
|
||||
impl<'a> Render for CursorFocus<'a> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let Self(focus_sequencer, focused, entered, selected, offset, cols, rows) = *self;
|
||||
let area = match selected {
|
||||
ArrangerFocus::Mix => if focused
|
||||
&& entered
|
||||
&& selected == ArrangerFocus::Mix
|
||||
{
|
||||
fill_bg(buf, area, Nord::bg_hi(state.focused, state.entered));
|
||||
fill_bg(buf, area, Nord::bg_hi(focused, entered));
|
||||
area
|
||||
} else {
|
||||
area
|
||||
|
|
@ -93,7 +104,7 @@ fn cursor_focus <'a> (
|
|||
width: cols[t].0 as u16,
|
||||
height: area.height
|
||||
};
|
||||
fill_bg(buf, area, Nord::bg_hi(state.focused, state.entered));
|
||||
fill_bg(buf, area, Nord::bg_hi(focused, entered));
|
||||
area
|
||||
},
|
||||
ArrangerFocus::Scene(s) => {
|
||||
|
|
@ -103,7 +114,7 @@ fn cursor_focus <'a> (
|
|||
width: area.width,
|
||||
height: (rows[s].0 / 96) as u16
|
||||
};
|
||||
fill_bg(buf, area, Nord::bg_hi(state.focused, state.entered));
|
||||
fill_bg(buf, area, Nord::bg_hi(focused, entered));
|
||||
area
|
||||
},
|
||||
ArrangerFocus::Clip(t, s) => {
|
||||
|
|
@ -125,29 +136,28 @@ fn cursor_focus <'a> (
|
|||
width: cols[t].0 as u16,
|
||||
height: (rows[s].0 / 96) as u16
|
||||
};
|
||||
let lo = Nord::bg_hi(state.focused, state.entered);
|
||||
let hi = Nord::bg_hier(state.focused, state.entered);
|
||||
let lo = Nord::bg_hi(focused, entered);
|
||||
let hi = Nord::bg_hier(focused, entered);
|
||||
fill_bg(buf, track_area, lo);
|
||||
fill_bg(buf, scene_area, lo);
|
||||
fill_bg(buf, area, hi);
|
||||
area
|
||||
},
|
||||
};
|
||||
if !state.focus_sequencer {
|
||||
if !focus_sequencer {
|
||||
Corners(Style::default().green().not_dim()).draw(buf, area)?;
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tracks_header <'a> (
|
||||
state: &'a Arranger,
|
||||
track_cols: &'a [(usize, usize)],
|
||||
offset: u16,
|
||||
) -> impl Render + 'a {
|
||||
move |buf: &mut Buffer, area: Rect| {
|
||||
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> {
|
||||
let Self(offset, track_cols, tracks) = *self;
|
||||
let Rect { y, width, .. } = area;
|
||||
for (track, (_, x)) in state.tracks.iter().zip(track_cols) {
|
||||
for (track, (_, x)) in tracks.iter().zip(track_cols) {
|
||||
let x = *x as u16;
|
||||
if x > width {
|
||||
break
|
||||
|
|
@ -158,13 +168,11 @@ pub fn tracks_header <'a> (
|
|||
}
|
||||
}
|
||||
|
||||
pub fn scene_rows <'a> (
|
||||
state: &'a Arranger,
|
||||
track_cols: &'a [(usize, usize)],
|
||||
scene_rows: &'a [(usize, usize)],
|
||||
offset: u16,
|
||||
) -> impl Render + 'a {
|
||||
move |buf: &mut Buffer, area: Rect| {
|
||||
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> {
|
||||
let Self(offset, track_cols, scene_rows, tracks, scenes) = *self;
|
||||
let black = Some(Style::default().fg(Nord::SEPARATOR));
|
||||
let Rect { mut y, height, .. } = area;
|
||||
for (_, x) in track_cols.iter() {
|
||||
|
|
@ -175,12 +183,12 @@ pub fn scene_rows <'a> (
|
|||
}
|
||||
}
|
||||
}
|
||||
for (scene, (pulses, _)) in state.scenes.iter().zip(scene_rows) {
|
||||
for (scene, (pulses, _)) in scenes.iter().zip(scene_rows) {
|
||||
if y > height {
|
||||
break
|
||||
}
|
||||
let h = 1.max((pulses / 96) as u16);
|
||||
scene_row(state, buf, Rect {
|
||||
scene_row(tracks, buf, Rect {
|
||||
x: area.x,
|
||||
y,
|
||||
width: area.width,
|
||||
|
|
@ -192,8 +200,8 @@ pub fn scene_rows <'a> (
|
|||
}
|
||||
}
|
||||
|
||||
fn scene_row (
|
||||
state: &Arranger,
|
||||
fn scene_row <'a> (
|
||||
tracks: &'a[Sequencer],
|
||||
buf: &mut Buffer,
|
||||
area: Rect,
|
||||
scene: &Scene,
|
||||
|
|
@ -201,7 +209,6 @@ fn scene_row (
|
|||
offset: u16
|
||||
) -> Usually<u16> {
|
||||
let Rect { y, width, .. } = area;
|
||||
let tracks = state.tracks.as_ref();
|
||||
let playing = scene.is_playing(tracks);
|
||||
(if playing { "▶" } else { " " }).blit(buf, area.x, y, None)?;
|
||||
scene.name.blit(buf, area.x + 1, y, None)?;
|
||||
|
|
|
|||
|
|
@ -1,84 +1,112 @@
|
|||
use crate::*;
|
||||
|
||||
const CORNERS: Corners = Corners(NOT_DIM_GREEN);
|
||||
|
||||
render!(TransportToolbar |self, buf, area| {
|
||||
let mut area = area;
|
||||
area.height = 2;
|
||||
let gray = Style::default().gray();
|
||||
let not_dim = Style::default().not_dim();
|
||||
let not_dim_bold = not_dim.bold();
|
||||
let corners = Corners(Style::default().green().not_dim());
|
||||
let ppq = self.ppq();
|
||||
let bpm = self.bpm();
|
||||
let pulse = self.pulse();
|
||||
let usecs = self.usecs();
|
||||
let mut area = area;
|
||||
area.height = 2;
|
||||
let ppq = self.ppq();
|
||||
let bpm = self.bpm();
|
||||
let pulse = self.pulse();
|
||||
let usecs = self.usecs();
|
||||
let Self { quant, sync, focused, entered, .. } = self;
|
||||
fill_bg(buf, area, Nord::bg_lo(*focused, *entered));
|
||||
Split::right([
|
||||
|
||||
// Play/Pause button
|
||||
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
||||
let style = Some(match self.playing {
|
||||
Some(TransportState::Stopped) => gray.dim().bold(),
|
||||
Some(TransportState::Starting) => gray.not_dim().bold(),
|
||||
Some(TransportState::Rolling) => gray.not_dim().white().bold(),
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let label = match self.playing {
|
||||
Some(TransportState::Rolling) => "▶ PLAYING",
|
||||
Some(TransportState::Starting) => "READY ...",
|
||||
Some(TransportState::Stopped) => "⏹ STOPPED",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let mut result = label.blit(buf, x + 1, y, style)?;
|
||||
result.width = result.width + 1;
|
||||
Ok(result)
|
||||
},
|
||||
|
||||
// Beats per minute
|
||||
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
||||
"BPM".blit(buf, x, y, Some(not_dim))?;
|
||||
let width = format!("{}.{:03}", bpm, bpm * 1000 % 1000).blit(buf, x, y + 1, Some(not_dim_bold))?.width;
|
||||
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
||||
if self.focused && self.entered && self.selected == TransportFocus::BPM {
|
||||
corners.draw(buf, Rect { x: area.x - 1, ..area })?;
|
||||
}
|
||||
Ok(area)
|
||||
},
|
||||
|
||||
// Quantization
|
||||
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
||||
"QUANT".blit(buf, x, y, Some(not_dim))?;
|
||||
let width = ppq_to_name(*quant as usize).blit(buf, x, y + 1, Some(not_dim_bold))?.width;
|
||||
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
||||
if self.focused && self.entered && self.selected == TransportFocus::Quant {
|
||||
corners.draw(buf, Rect { x: area.x - 1, ..area })?;
|
||||
}
|
||||
Ok(area)
|
||||
},
|
||||
|
||||
// Clip launch sync
|
||||
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
||||
"SYNC".blit(buf, x, y, Some(not_dim))?;
|
||||
let width = ppq_to_name(*sync as usize).blit(buf, x, y + 1, Some(not_dim_bold))?.width;
|
||||
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
||||
if self.focused && self.entered && self.selected == TransportFocus::Sync {
|
||||
corners.draw(buf, Rect { x: area.x - 1, ..area })?;
|
||||
}
|
||||
Ok(area)
|
||||
},
|
||||
|
||||
// Clock
|
||||
&|buf: &mut Buffer, Rect { x, y, width, .. }: Rect|{
|
||||
let (beats, pulses) = (pulse / ppq, pulse % ppq);
|
||||
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))?;
|
||||
let timer = format!("{minutes}:{seconds:02}:{msecs:03}");
|
||||
timer.blit(buf, x + width - timer.len() as u16 - 1, y + 1, Some(not_dim))?;
|
||||
Ok(area)
|
||||
}
|
||||
|
||||
]).render(buf, area)
|
||||
let active = self.focused && self.entered;
|
||||
let playing = TransportPlayPauseButton(self.playing);
|
||||
let bpm = TransportBPM(bpm, active && self.selected == TransportFocus::BPM);
|
||||
let quant = TransportQuantize(*quant, active && self.selected == TransportFocus::Quant);
|
||||
let sync = TransportSync(*sync, active && self.selected == TransportFocus::Sync);
|
||||
let clock = TransportClock(pulse, ppq, usecs);
|
||||
Split::right()
|
||||
.add_ref(&playing)
|
||||
.add_ref(&bpm)
|
||||
.add_ref(&quant)
|
||||
.add_ref(&sync)
|
||||
.add_ref(&clock)
|
||||
.render(buf, area)
|
||||
});
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct TransportPlayPauseButton(Option<TransportState>);
|
||||
|
||||
render!(TransportPlayPauseButton |self, buf, area| {
|
||||
let Rect { x, y, .. } = area;
|
||||
let gray = Style::default().gray();
|
||||
let style = Some(match self.0 {
|
||||
Some(TransportState::Stopped) => GRAY_DIM.bold(),
|
||||
Some(TransportState::Starting) => GRAY_NOT_DIM_BOLD,
|
||||
Some(TransportState::Rolling) => WHITE_NOT_DIM_BOLD,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let label = match self.0 {
|
||||
Some(TransportState::Rolling) => "▶ PLAYING",
|
||||
Some(TransportState::Starting) => "READY ...",
|
||||
Some(TransportState::Stopped) => "⏹ STOPPED",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let mut result = label.blit(buf, x + 1, y, style)?;
|
||||
result.width = result.width + 1;
|
||||
Ok(result)
|
||||
});
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct TransportBPM(usize, bool);
|
||||
|
||||
render!(TransportBPM |self, buf, area| {
|
||||
let Rect { x, y, .. } = area;
|
||||
let Self(bpm, highlight) = self;
|
||||
"BPM".blit(buf, x, y, Some(NOT_DIM))?;
|
||||
let width = format!("{}.{:03}", bpm, bpm * 1000 % 1000).blit(buf, x, y + 1, Some(NOT_DIM_BOLD))?.width;
|
||||
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
||||
if *highlight {
|
||||
CORNERS.draw(buf, Rect { x: area.x - 1, ..area })?;
|
||||
}
|
||||
Ok(area)
|
||||
});
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct TransportQuantize(usize, bool);
|
||||
|
||||
render!(TransportQuantize |self, buf, area| {
|
||||
let Rect { x, y, .. } = area;
|
||||
let Self(quant, highlight) = self;
|
||||
"QUANT".blit(buf, x, y, Some(NOT_DIM))?;
|
||||
let width = ppq_to_name(*quant as usize).blit(buf, x, y + 1, Some(NOT_DIM_BOLD))?.width;
|
||||
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
||||
if *highlight {
|
||||
CORNERS.draw(buf, Rect { x: area.x - 1, ..area })?;
|
||||
}
|
||||
Ok(area)
|
||||
});
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct TransportSync(usize, bool);
|
||||
|
||||
render!(TransportSync |self, buf, area| {
|
||||
let Rect { x, y, .. } = area;
|
||||
let Self(sync, highlight) = self;
|
||||
"SYNC".blit(buf, x, y, Some(NOT_DIM))?;
|
||||
let width = ppq_to_name(*sync as usize).blit(buf, x, y + 1, Some(NOT_DIM_BOLD))?.width;
|
||||
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
||||
if *highlight {
|
||||
CORNERS.draw(buf, Rect { x: area.x - 1, ..area })?;
|
||||
}
|
||||
Ok(area)
|
||||
});
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct TransportClock(usize, usize, usize);
|
||||
|
||||
render!(TransportClock |self, buf, area| {
|
||||
let Rect { x, y, width, .. } = area;
|
||||
let Self(pulse, ppq, usecs) = self;
|
||||
let (beats, pulses) = (pulse / ppq, pulse % ppq);
|
||||
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))?;
|
||||
let timer = format!("{minutes}:{seconds:02}:{msecs:03}");
|
||||
timer.blit(buf, x + width - timer.len() as u16 - 1, y + 1, Some(NOT_DIM))?;
|
||||
Ok(area)
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue