wip: "multiple cascading refactors"

https://loglog.games/blog/leaving-rust-gamedev/#orphan-rule-should-be-optional is on point
This commit is contained in:
🪞👃🪞 2024-09-09 21:25:04 +03:00
parent 20afc397ea
commit fa8282a9d5
18 changed files with 175 additions and 222 deletions

View file

@ -6,7 +6,7 @@ pub trait App<T: Engine> {
} }
/// Platform backend. /// Platform backend.
pub trait Engine: Sized { pub trait Engine: Send + Sync + Sized {
fn setup (&mut self) -> Usually<()> { Ok(()) } fn setup (&mut self) -> Usually<()> { Ok(()) }
fn exited (&self) -> bool; fn exited (&self) -> bool;
fn teardown (&mut self) -> Usually<()> { Ok(()) } fn teardown (&mut self) -> Usually<()> { Ok(()) }

View file

@ -9,6 +9,7 @@ pub use once_cell::sync::Lazy;
pub use std::sync::atomic::{Ordering, AtomicBool}; pub use std::sync::atomic::{Ordering, AtomicBool};
pub use std::rc::Rc; pub use std::rc::Rc;
pub use std::cell::RefCell; pub use std::cell::RefCell;
pub use std::marker::PhantomData;
pub(crate) use std::error::Error; pub(crate) use std::error::Error;
pub(crate) use std::io::{stdout}; pub(crate) use std::io::{stdout};
pub(crate) use std::thread::{spawn, JoinHandle}; pub(crate) use std::thread::{spawn, JoinHandle};

View file

@ -30,12 +30,13 @@ impl<E: Engine> Process for Mixer<E> {
Control::Continue Control::Continue
} }
} }
impl Render<Tui> for Mixer<Tui> { impl Content for Mixer<Tui> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
let mut tracks = Split::right(); let mut tracks = Split::right();
for channel in self.tracks.iter() { for channel in self.tracks.iter() {
tracks = tracks.add_ref(channel) tracks = tracks.add_ref(channel)
} }
tracks.render(to) tracks
} }
} }

View file

@ -1,13 +1,9 @@
use crate::*; use crate::*;
use tek_core::Direction; use tek_core::Direction;
impl Layout<Tui> for Track<Tui> { impl Content for Track<Tui> {
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { type Engine = Tui;
todo!() fn content (&self) -> impl Widget<Engine = Tui> {
}
}
impl Render<Tui> for Track<Tui> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
TrackView { TrackView {
chain: Some(&self), chain: Some(&self),
direction: tek_core::Direction::Right, direction: tek_core::Direction::Right,
@ -26,7 +22,7 @@ impl Render<Tui> for Track<Tui> {
//pub output_ports: Vec<Port<AudioOut>>, //pub output_ports: Vec<Port<AudioOut>>,
//pub post_fader_meter: f64, //pub post_fader_meter: f64,
//pub route: String, //pub route: String,
}.render(to) }
} }
} }
pub struct TrackView<'a, E: Engine> { pub struct TrackView<'a, E: Engine> {
@ -35,7 +31,11 @@ pub struct TrackView<'a, E: Engine> {
pub focused: bool, pub focused: bool,
pub entered: bool, pub entered: bool,
} }
impl<'a> Render<Tui> for TrackView<'a, Tui> { impl<'a> Widget for TrackView<'a, Tui> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!()
}
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
todo!(); todo!();
//let mut area = to.area(); //let mut area = to.area();

View file

@ -6,7 +6,7 @@ pub struct Arranger<E: Engine> {
/// Name of arranger /// Name of arranger
pub name: Arc<RwLock<String>>, pub name: Arc<RwLock<String>>,
/// Collection of tracks. /// Collection of tracks.
pub tracks: Vec<Sequencer>, pub tracks: Vec<Sequencer<E>>,
/// Collection of scenes. /// Collection of scenes.
pub scenes: Vec<Scene>, pub scenes: Vec<Scene>,
/// Currently selected element. /// Currently selected element.
@ -45,12 +45,12 @@ impl<E: Engine> Arranger<E> {
_ => {} _ => {}
} }
} }
pub fn sequencer (&self) -> Option<&Sequencer> { pub fn sequencer (&self) -> Option<&Sequencer<E>> {
self.selected.track() self.selected.track()
.map(|track|self.tracks.get(track)) .map(|track|self.tracks.get(track))
.flatten() .flatten()
} }
pub fn sequencer_mut (&mut self) -> Option<&mut Sequencer> { pub fn sequencer_mut (&mut self) -> Option<&mut Sequencer<E>> {
self.selected.track() self.selected.track()
.map(|track|self.tracks.get_mut(track)) .map(|track|self.tracks.get_mut(track))
.flatten() .flatten()
@ -99,13 +99,11 @@ impl ArrangerViewMode {
} }
} }
} }
impl Layout<Tui> for Arranger<Tui> { impl Widget for Arranger<Tui> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
/// Render arranger to terminal
impl Render<Tui> for Arranger<Tui> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = (|to|match self.mode { let area = (|to|match self.mode {
ArrangerViewMode::Horizontal => ArrangerViewMode::Horizontal =>

View file

@ -13,16 +13,18 @@ impl Arranger<Tui> {
} }
} }
/// Appears on first run (i.e. if state dir is missing). /// Appears on first run (i.e. if state dir is missing).
pub struct ArrangerRenameModal { pub struct ArrangerRenameModal<E: Engine> {
_engine: std::marker::PhantomData<E>,
done: bool, done: bool,
target: ArrangerFocus, target: ArrangerFocus,
value: String, value: String,
result: Arc<RwLock<String>>, result: Arc<RwLock<String>>,
cursor: usize cursor: usize
} }
impl ArrangerRenameModal { impl<E: Engine> ArrangerRenameModal<E> {
pub fn new (target: ArrangerFocus, value: &Arc<RwLock<String>>) -> Self { pub fn new (target: ArrangerFocus, value: &Arc<RwLock<String>>) -> Self {
Self { Self {
_engine: Default::default(),
done: false, done: false,
target, target,
value: value.read().unwrap().clone(), value: value.read().unwrap().clone(),
@ -31,7 +33,11 @@ impl ArrangerRenameModal {
} }
} }
} }
impl Render<Tui> for ArrangerRenameModal { impl Widget for ArrangerRenameModal<Tui> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!()
}
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let y = area.y() + area.h() / 2; let y = area.y() + area.h() / 2;
@ -53,7 +59,7 @@ impl Render<Tui> for ArrangerRenameModal {
Ok(Some(area)) Ok(Some(area))
} }
} }
impl Handle<Tui> for ArrangerRenameModal { impl Handle<Tui> for ArrangerRenameModal<Tui> {
fn handle (&mut self, from: &Tui) -> Perhaps<bool> { fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
match from.event() { match from.event() {
TuiEvent::Input(Event::Key(k)) => { TuiEvent::Input(Event::Key(k)) => {
@ -91,7 +97,7 @@ impl Handle<Tui> for ArrangerRenameModal {
} }
} }
} }
impl Exit for ArrangerRenameModal { impl<E: Engine + Send> Exit for ArrangerRenameModal<E> {
fn exited (&self) -> bool { fn exited (&self) -> bool {
self.done self.done
} }
@ -99,8 +105,3 @@ impl Exit for ArrangerRenameModal {
self.done = true self.done = true
} }
} }
impl Layout<Tui> for ArrangerRenameModal {
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!()
}
}

View file

@ -4,10 +4,10 @@ use super::Arranger;
/// Track management methods /// Track management methods
impl<E: Engine> Arranger<E> { impl<E: Engine> Arranger<E> {
pub fn track (&self) -> Option<&Sequencer> { pub fn track (&self) -> Option<&Sequencer<E>> {
self.selected.track().map(|t|self.tracks.get(t)).flatten() self.selected.track().map(|t|self.tracks.get(t)).flatten()
} }
pub fn track_mut (&mut self) -> Option<&mut Sequencer> { pub fn track_mut (&mut self) -> Option<&mut Sequencer<E>> {
self.selected.track().map(|t|self.tracks.get_mut(t)).flatten() self.selected.track().map(|t|self.tracks.get_mut(t)).flatten()
} }
pub fn track_next (&mut self) { pub fn track_next (&mut self) {
@ -16,7 +16,7 @@ impl<E: Engine> Arranger<E> {
pub fn track_prev (&mut self) { pub fn track_prev (&mut self) {
self.selected.track_prev() self.selected.track_prev()
} }
pub fn track_add (&mut self, name: Option<&str>) -> Usually<&mut Sequencer> { pub fn track_add (&mut self, name: Option<&str>) -> Usually<&mut Sequencer<E>> {
self.tracks.push(name.map_or_else( self.tracks.push(name.map_or_else(
|| Sequencer::new(&self.track_default_name()), || Sequencer::new(&self.track_default_name()),
|name| Sequencer::new(name), |name| Sequencer::new(name),
@ -32,13 +32,13 @@ impl<E: Engine> Arranger<E> {
} }
} }
pub fn track_name_max_len (tracks: &[Sequencer]) -> usize { pub fn track_name_max_len <E: Engine> (tracks: &[Sequencer<E>]) -> usize {
tracks.iter() tracks.iter()
.map(|s|s.name.read().unwrap().len()) .map(|s|s.name.read().unwrap().len())
.fold(0, usize::max) .fold(0, usize::max)
} }
pub fn track_clip_name_lengths (tracks: &[Sequencer]) -> Vec<(usize, usize)> { pub fn track_clip_name_lengths <E: Engine> (tracks: &[Sequencer<E>]) -> Vec<(usize, usize)> {
let mut total = 0; let mut total = 0;
let mut lengths: Vec<(usize, usize)> = tracks.iter().map(|track|{ let mut lengths: Vec<(usize, usize)> = tracks.iter().map(|track|{
let len = 4 + track.phrases let len = 4 + track.phrases

View file

@ -17,15 +17,13 @@ pub fn draw (state: &Arranger<Tui>, to: &mut Tui) -> Perhaps<[u16;4]> {
]).render(to.with_rect(area)) ]).render(to.with_rect(area))
} }
struct TrackNameColumn<'a>(&'a [Sequencer], ArrangerFocus); struct TrackNameColumn<'a>(&'a [Sequencer<Tui>], ArrangerFocus);
impl<'a> Layout<Tui> for TrackNameColumn<'a> { impl<'a> Widget for TrackNameColumn<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for TrackNameColumn<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
todo!(); todo!();
//let Self(tracks, selected) = self; //let Self(tracks, selected) = self;
@ -51,15 +49,13 @@ impl<'a> Render<Tui> for TrackNameColumn<'a> {
} }
} }
struct TrackMonitorColumn<'a>(&'a [Sequencer]); struct TrackMonitorColumn<'a>(&'a [Sequencer<Tui>]);
impl<'a> Layout<Tui> for TrackMonitorColumn<'a> { impl<'a> Widget for TrackMonitorColumn<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for TrackMonitorColumn<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
todo!(); todo!();
//let Self(tracks) = self; //let Self(tracks) = self;
@ -86,15 +82,13 @@ impl<'a> Render<Tui> for TrackMonitorColumn<'a> {
} }
} }
struct TrackRecordColumn<'a>(&'a [Sequencer]); struct TrackRecordColumn<'a>(&'a [Sequencer<Tui>]);
impl<'a> Layout<Tui> for TrackRecordColumn<'a> { impl<'a> Widget for TrackRecordColumn<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for TrackRecordColumn<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
todo!(); todo!();
//let Self(tracks) = self; //let Self(tracks) = self;
@ -121,15 +115,13 @@ impl<'a> Render<Tui> for TrackRecordColumn<'a> {
} }
} }
struct TrackOverdubColumn<'a>(&'a [Sequencer]); struct TrackOverdubColumn<'a>(&'a [Sequencer<Tui>]);
impl<'a> Layout<Tui> for TrackOverdubColumn<'a> { impl<'a> Widget for TrackOverdubColumn<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for TrackOverdubColumn<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
todo!(); todo!();
//let Self(tracks) = self; //let Self(tracks) = self;
@ -159,15 +151,13 @@ impl<'a> Render<Tui> for TrackOverdubColumn<'a> {
} }
} }
struct TrackEraseColumn<'a>(&'a [Sequencer]); struct TrackEraseColumn<'a>(&'a [Sequencer<Tui>]);
impl<'a> Layout<Tui> for TrackEraseColumn<'a> { impl<'a> Widget for TrackEraseColumn<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for TrackEraseColumn<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
todo!(); todo!();
//let Self(tracks) = self; //let Self(tracks) = self;
@ -192,15 +182,13 @@ impl<'a> Render<Tui> for TrackEraseColumn<'a> {
} }
} }
struct TrackGainColumn<'a>(&'a [Sequencer]); struct TrackGainColumn<'a>(&'a [Sequencer<Tui>]);
impl<'a> Layout<Tui> for TrackGainColumn<'a> { impl<'a> Widget for TrackGainColumn<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for TrackGainColumn<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
todo!(); todo!();
//let Self(tracks) = self; //let Self(tracks) = self;
@ -225,15 +213,13 @@ impl<'a> Render<Tui> for TrackGainColumn<'a> {
} }
} }
struct TrackScenesColumn<'a>(&'a [Sequencer], &'a [Scene], ArrangerFocus); struct TrackScenesColumn<'a>(&'a [Sequencer<Tui>], &'a [Scene], ArrangerFocus);
impl<'a> Layout<Tui> for TrackScenesColumn<'a> { impl<'a> Widget for TrackScenesColumn<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for TrackScenesColumn<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let Self(tracks, scenes, selected) = self; let Self(tracks, scenes, selected) = self;
let area = to.area(); let area = to.area();

View file

@ -51,13 +51,11 @@ pub fn draw <'a, 'b> (
struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]); struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]);
impl<'a> Layout<Tui> for ColumnSeparators<'a> { impl<'a> Widget for ColumnSeparators<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for ColumnSeparators<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let Self(offset, cols) = self; let Self(offset, cols) = self;
@ -74,13 +72,11 @@ impl<'a> Render<Tui> for ColumnSeparators<'a> {
struct RowSeparators<'a>(&'a [(usize, usize)]); struct RowSeparators<'a>(&'a [(usize, usize)]);
impl<'a> Layout<Tui> for RowSeparators<'a> { impl<'a> Widget for RowSeparators<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for RowSeparators<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let Self(rows) = self; let Self(rows) = self;
@ -103,13 +99,11 @@ struct CursorFocus<'a>(
ArrangerFocus, u16, &'a [(usize, usize)], &'a [(usize, usize)] ArrangerFocus, u16, &'a [(usize, usize)], &'a [(usize, usize)]
); );
impl<'a> Layout<Tui> for CursorFocus<'a> { impl<'a> Widget for CursorFocus<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for CursorFocus<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let Self(selected, offset, cols, rows) = *self; let Self(selected, offset, cols, rows) = *self;
@ -173,15 +167,13 @@ impl<'a> Render<Tui> for CursorFocus<'a> {
} }
} }
struct TracksHeader<'a>(u16, &'a[(usize, usize)], &'a [Sequencer]); struct TracksHeader<'a>(u16, &'a[(usize, usize)], &'a [Sequencer<Tui>]);
impl<'a> Layout<Tui> for TracksHeader<'a> { impl<'a> Widget for TracksHeader<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for TracksHeader<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let Self(offset, track_cols, tracks) = *self; let Self(offset, track_cols, tracks) = *self;
@ -199,15 +191,13 @@ impl<'a> Render<Tui> for TracksHeader<'a> {
} }
} }
struct SceneRows<'a>(u16, &'a[(usize, usize)], &'a[(usize, usize)], &'a[Sequencer], &'a[Scene]); struct SceneRows<'a>(u16, &'a[(usize, usize)], &'a[(usize, usize)], &'a[Sequencer<Tui>], &'a[Scene]);
impl<'a> Layout<Tui> for SceneRows<'a> { impl<'a> Widget for SceneRows<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for SceneRows<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let Self(offset, track_cols, scene_rows, tracks, scenes) = *self; let Self(offset, track_cols, scene_rows, tracks, scenes) = *self;
@ -234,15 +224,13 @@ impl<'a> Render<Tui> for SceneRows<'a> {
} }
} }
struct SceneRow<'a>(&'a[Sequencer], &'a Scene, &'a[(usize, usize)], u16); struct SceneRow<'a>(&'a[Sequencer<Tui>], &'a Scene, &'a[(usize, usize)], u16);
impl<'a> Layout<Tui> for SceneRow<'a> { impl<'a> Widget for SceneRow<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for SceneRow<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let Self(tracks, scene, track_cols, offset) = self; let Self(tracks, scene, track_cols, offset) = self;
@ -266,15 +254,13 @@ impl<'a> Render<Tui> for SceneRow<'a> {
} }
} }
struct SceneClip<'a>(&'a Sequencer, usize); struct SceneClip<'a>(&'a Sequencer<Tui>, usize);
impl<'a> Layout<Tui> for SceneClip<'a> { impl<'a> Widget for SceneClip<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for SceneClip<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let Self(track, clip) = self; let Self(track, clip) = self;

View file

@ -37,7 +37,7 @@ impl Scene {
Self { name, clips, } Self { name, clips, }
} }
/// Returns the pulse length of the longest phrase in the scene /// Returns the pulse length of the longest phrase in the scene
pub fn pulses (&self, tracks: &[Sequencer]) -> usize { pub fn pulses <E: Engine> (&self, tracks: &[Sequencer<E>]) -> usize {
self.clips.iter().enumerate() self.clips.iter().enumerate()
.filter_map(|(i, c)|c .filter_map(|(i, c)|c
.map(|c|tracks .map(|c|tracks
@ -50,7 +50,7 @@ impl Scene {
.fold(0, |a, p|a.max(p.read().unwrap().length)) .fold(0, |a, p|a.max(p.read().unwrap().length))
} }
/// Returns true if all phrases in the scene are currently playing /// Returns true if all phrases in the scene are currently playing
pub fn is_playing (&self, tracks: &[Sequencer]) -> bool { pub fn is_playing <E: Engine> (&self, tracks: &[Sequencer<E>]) -> bool {
self.clips.iter().enumerate() self.clips.iter().enumerate()
.all(|(track_index, phrase_index)|match phrase_index { .all(|(track_index, phrase_index)|match phrase_index {
Some(i) => tracks Some(i) => tracks
@ -68,7 +68,10 @@ pub fn scene_name_max_len (scenes: &[Scene]) -> usize {
.fold(0, usize::max) .fold(0, usize::max)
} }
pub fn scene_ppqs (tracks: &[Sequencer], scenes: &[Scene]) -> Vec<(usize, usize)> { pub fn scene_ppqs <E: Engine> (
tracks: &[Sequencer<E>],
scenes: &[Scene]
) -> Vec<(usize, usize)> {
let mut total = 0; let mut total = 0;
let mut scenes: Vec<(usize, usize)> = scenes.iter().map(|scene|{ let mut scenes: Vec<(usize, usize)> = scenes.iter().map(|scene|{
let pulses = scene.pulses(tracks).max(96); let pulses = scene.pulses(tracks).max(96);

View file

@ -2,14 +2,14 @@ use crate::*;
use tek_core::Direction; use tek_core::Direction;
/// Phrase editor. /// Phrase editor.
pub struct Sequencer { pub struct Sequencer<E: Engine> {
pub name: Arc<RwLock<String>>, pub name: Arc<RwLock<String>>,
pub mode: bool, pub mode: bool,
pub focused: bool, pub focused: bool,
pub entered: bool, pub entered: bool,
pub phrase: Option<Arc<RwLock<Phrase>>>, pub phrase: Option<Arc<RwLock<Phrase>>>,
pub transport: Option<Arc<RwLock<TransportToolbar>>>, pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
pub buffer: BigBuffer, pub buffer: BigBuffer,
pub keys: Buffer, pub keys: Buffer,
/// Highlight input keys /// Highlight input keys
@ -43,7 +43,7 @@ pub struct Sequencer {
pub notes_out: [bool;128], pub notes_out: [bool;128],
} }
impl Sequencer { impl<E: Engine> Sequencer<E> {
pub fn new (name: &str) -> Self { pub fn new (name: &str) -> Self {
Self { Self {
name: Arc::new(RwLock::new(name.into())), name: Arc::new(RwLock::new(name.into())),

View file

@ -14,7 +14,7 @@ pub struct SequencerCli {
#[arg(short, long)] transport: Option<bool> #[arg(short, long)] transport: Option<bool>
} }
impl Sequencer { impl Sequencer<Tui> {
pub fn from_args () -> Self { pub fn from_args () -> Self {
let args = SequencerCli::parse(); let args = SequencerCli::parse();
let mut seq = Self::new(""); let mut seq = Self::new("");

View file

@ -1,6 +1,6 @@
use crate::*; use crate::*;
impl Handle<Tui> for Sequencer { impl Handle<Tui> for Sequencer<Tui> {
fn handle (&mut self, from: &Tui) -> Perhaps<bool> { fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
match from.event() { match from.event() {
// NONE, "seq_cursor_up", "move cursor up", |sequencer: &mut Sequencer| { // NONE, "seq_cursor_up", "move cursor up", |sequencer: &mut Sequencer| {

View file

@ -1,11 +1,10 @@
use crate::*; use crate::*;
impl Layout<Tui> for Sequencer { impl Widget for Sequencer<Tui> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for Sequencer {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
self.horizontal_draw(to)?; self.horizontal_draw(to)?;
if self.focused && self.entered { if self.focused && self.entered {
@ -15,7 +14,7 @@ impl<'a> Render<Tui> for Sequencer {
} }
} }
impl Sequencer { impl<E: Engine> Sequencer<E> {
/// Select which pattern to display. This pre-renders it to the buffer at full resolution. /// Select which pattern to display. This pre-renders it to the buffer at full resolution.
pub fn show (&mut self, phrase: Option<&Arc<RwLock<Phrase>>>) -> Usually<()> { pub fn show (&mut self, phrase: Option<&Arc<RwLock<Phrase>>>) -> Usually<()> {
self.phrase = phrase.map(Clone::clone); self.phrase = phrase.map(Clone::clone);

View file

@ -1,6 +1,6 @@
use crate::*; use crate::*;
impl Sequencer { impl Sequencer<Tui> {
const H_KEYS_OFFSET: usize = 5; const H_KEYS_OFFSET: usize = 5;
@ -43,15 +43,13 @@ const STYLE_VALUE: Option<Style> = Some(Style {
sub_modifier: Modifier::DIM, sub_modifier: Modifier::DIM,
}); });
struct SequenceName<'a>(&'a Sequencer); struct SequenceName<'a>(&'a Sequencer<Tui>);
impl<'a> Layout<Tui> for SequenceName<'a> { impl<'a> Widget for SequenceName<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for SequenceName<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let [x, y, ..] = to.area(); let [x, y, ..] = to.area();
let frame = [x, y, 10, 4]; let frame = [x, y, 10, 4];
@ -64,13 +62,11 @@ impl<'a> Render<Tui> for SequenceName<'a> {
struct SequenceRange; struct SequenceRange;
impl Layout<Tui> for SequenceRange { impl Widget for SequenceRange {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for SequenceRange {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let [x, y, ..] = to.area(); let [x, y, ..] = to.area();
let frame = [x, y, 10, 6]; let frame = [x, y, 10, 6];
@ -85,13 +81,11 @@ impl<'a> Render<Tui> for SequenceRange {
struct SequenceLoopRange; struct SequenceLoopRange;
impl Layout<Tui> for SequenceLoopRange { impl Widget for SequenceLoopRange {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for SequenceLoopRange {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let [x, y, ..] = to.area(); let [x, y, ..] = to.area();
let range = [x, y, 10, 7]; let range = [x, y, 10, 7];
@ -107,13 +101,11 @@ impl<'a> Render<Tui> for SequenceLoopRange {
struct SequenceNoteRange; struct SequenceNoteRange;
impl Layout<Tui> for SequenceNoteRange { impl Widget for SequenceNoteRange {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for SequenceNoteRange {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let [x, y, ..] = to.area(); let [x, y, ..] = to.area();
let range = [x, y, 10, 9]; let range = [x, y, 10, 9];
@ -129,15 +121,13 @@ impl<'a> Render<Tui> for SequenceNoteRange {
} }
} }
struct SequenceKeys<'a>(&'a Sequencer); struct SequenceKeys<'a>(&'a Sequencer<Tui>);
impl<'a> Layout<Tui> for SequenceKeys<'a> { impl<'a> Widget for SequenceKeys<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for SequenceKeys<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
if area.h() < 2 { if area.h() < 2 {
@ -154,15 +144,13 @@ impl<'a> Render<Tui> for SequenceKeys<'a> {
} }
} }
struct SequenceNotes<'a>(&'a Sequencer); struct SequenceNotes<'a>(&'a Sequencer<Tui>);
impl<'a> Layout<Tui> for SequenceNotes<'a> { impl<'a> Widget for SequenceNotes<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for SequenceNotes<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
if area.h() < 2 { if area.h() < 2 {
@ -189,15 +177,13 @@ impl<'a> Render<Tui> for SequenceNotes<'a> {
} }
} }
struct SequenceCursor<'a>(&'a Sequencer); struct SequenceCursor<'a>(&'a Sequencer<Tui>);
impl<'a> Layout<Tui> for SequenceCursor<'a> { impl<'a> Widget for SequenceCursor<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for SequenceCursor<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
if let (Some(time), Some(note)) = (self.0.time_axis.point, self.0.note_axis.point) { if let (Some(time), Some(note)) = (self.0.time_axis.point, self.0.note_axis.point) {
@ -211,15 +197,13 @@ impl<'a> Render<Tui> for SequenceCursor<'a> {
} }
} }
struct SequenceZoom<'a>(&'a Sequencer); struct SequenceZoom<'a>(&'a Sequencer<Tui>);
impl<'a> Layout<Tui> for SequenceZoom<'a> { impl<'a> Widget for SequenceZoom<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for SequenceZoom<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let quant = ppq_to_name(self.0.time_axis.scale); let quant = ppq_to_name(self.0.time_axis.scale);
@ -229,15 +213,13 @@ impl<'a> Render<Tui> for SequenceZoom<'a> {
} }
} }
struct SequenceTimer<'a>(&'a Sequencer, Arc<RwLock<Phrase>>); struct SequenceTimer<'a>(&'a Sequencer<Tui>, Arc<RwLock<Phrase>>);
impl<'a> Layout<Tui> for SequenceTimer<'a> { impl<'a> Widget for SequenceTimer<'a> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!() todo!()
} }
}
impl<'a> Render<Tui> for SequenceTimer<'a> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let phrase = self.1.read().unwrap(); let phrase = self.1.read().unwrap();
@ -250,7 +232,7 @@ impl<'a> Render<Tui> for SequenceTimer<'a> {
for x in x2..x3 { for x in x2..x3 {
let step = (time0 + x2) * time_z; let step = (time0 + x2) * time_z;
let next_step = (time0 + x2 + 1) * time_z; let next_step = (time0 + x2 + 1) * time_z;
let style = Sequencer::style_timer_step(now, step as usize, next_step as usize); let style = Sequencer::<Tui>::style_timer_step(now, step as usize, next_step as usize);
to.blit(&"-", x as u16, area.y(), Some(style))?; to.blit(&"-", x as u16, area.y(), Some(style))?;
} }
return Ok(Some([area.x(), area.y(), area.w(), 1])) return Ok(Some([area.x(), area.y(), area.w(), 1]))

View file

@ -1,7 +1,7 @@
use crate::*; use crate::*;
/// Stores and displays time-related state. /// Stores and displays time-related state.
pub struct TransportToolbar { pub struct TransportToolbar<E: Engine> {
/// Enable metronome? /// Enable metronome?
pub metronome: bool, pub metronome: bool,
/// Current sample rate, tempo, and PPQ. /// Current sample rate, tempo, and PPQ.
@ -16,13 +16,13 @@ pub struct TransportToolbar {
pub focused: bool, pub focused: bool,
pub focus: usize, pub focus: usize,
pub playing: TransportPlayPauseButton, pub playing: TransportPlayPauseButton<E>,
pub bpm: TransportBPM, pub bpm: TransportBPM<E>,
pub quant: TransportQuantize, pub quant: TransportQuantize<E>,
pub sync: TransportSync, pub sync: TransportSync<E>,
pub clock: TransportClock, pub clock: TransportClock<E>,
} }
impl TransportToolbar { impl<E: Engine> TransportToolbar<E> {
pub fn standalone () -> Usually<Arc<RwLock<Self>>> { pub fn standalone () -> Usually<Arc<RwLock<Self>>> {
let mut transport = Self::new(None); let mut transport = Self::new(None);
transport.focused = true; transport.focused = true;
@ -34,7 +34,7 @@ impl TransportToolbar {
transport.write().unwrap().jack = Some( transport.write().unwrap().jack = Some(
jack.activate( jack.activate(
&transport.clone(), &transport.clone(),
|state: &Arc<RwLock<TransportToolbar>>, client, scope| { |state: &Arc<RwLock<TransportToolbar<E>>>, client, scope| {
state.write().unwrap().process(client, scope) state.write().unwrap().process(client, scope)
} }
)? )?
@ -48,22 +48,27 @@ impl TransportToolbar {
focus: 0, focus: 0,
playing: TransportPlayPauseButton { playing: TransportPlayPauseButton {
_engine: Default::default(),
value: Some(TransportState::Stopped), value: Some(TransportState::Stopped),
focused: true focused: true
}, },
bpm: TransportBPM { bpm: TransportBPM {
_engine: Default::default(),
value: timebase.bpm(), value: timebase.bpm(),
focused: false focused: false
}, },
quant: TransportQuantize { quant: TransportQuantize {
_engine: Default::default(),
value: 24, value: 24,
focused: false focused: false
}, },
sync: TransportSync { sync: TransportSync {
_engine: Default::default(),
value: timebase.ppq() as usize * 4, value: timebase.ppq() as usize * 4,
focused: false focused: false
}, },
clock: TransportClock { clock: TransportClock {
_engine: Default::default(),
frame: 0, frame: 0,
pulse: 0, pulse: 0,
ppq: 0, ppq: 0,
@ -142,7 +147,7 @@ impl TransportToolbar {
self.timebase.frame_to_usec(self.clock.frame as f64) as usize self.timebase.frame_to_usec(self.clock.frame as f64) as usize
} }
} }
impl Focus<5, Tui> for TransportToolbar { impl Focus<5, Tui> for TransportToolbar<Tui> {
fn focus (&self) -> usize { fn focus (&self) -> usize {
self.focus self.focus
} }
@ -168,7 +173,7 @@ impl Focus<5, Tui> for TransportToolbar {
] ]
} }
} }
impl Focusable<Tui> for TransportToolbar { impl Focusable<Tui> for TransportToolbar<Tui> {
fn is_focused (&self) -> bool { fn is_focused (&self) -> bool {
self.focused self.focused
} }
@ -176,16 +181,19 @@ impl Focusable<Tui> for TransportToolbar {
self.focused = focused self.focused = focused
} }
} }
process!(TransportToolbar |self, _client, scope| { impl<E: Engine> Process for TransportToolbar<E> {
self.update(&scope); fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
Control::Continue self.update(&scope);
}); Control::Continue
}
}
pub struct TransportPlayPauseButton { pub struct TransportPlayPauseButton<E: Engine> {
pub _engine: PhantomData<E>,
pub value: Option<TransportState>, pub value: Option<TransportState>,
pub focused: bool pub focused: bool
} }
impl Focusable<Tui> for TransportPlayPauseButton { impl Focusable<Tui> for TransportPlayPauseButton<Tui> {
fn is_focused (&self) -> bool { fn is_focused (&self) -> bool {
self.focused self.focused
} }
@ -194,11 +202,12 @@ impl Focusable<Tui> for TransportPlayPauseButton {
} }
} }
pub struct TransportBPM { pub struct TransportBPM<E: Engine> {
pub _engine: PhantomData<E>,
pub value: f64, pub value: f64,
pub focused: bool pub focused: bool
} }
impl Focusable<Tui> for TransportBPM { impl Focusable<Tui> for TransportBPM<Tui> {
fn is_focused (&self) -> bool { fn is_focused (&self) -> bool {
self.focused self.focused
} }
@ -207,11 +216,12 @@ impl Focusable<Tui> for TransportBPM {
} }
} }
pub struct TransportQuantize { pub struct TransportQuantize<E: Engine> {
pub _engine: PhantomData<E>,
pub value: usize, pub value: usize,
pub focused: bool pub focused: bool
} }
impl Focusable<Tui> for TransportQuantize { impl Focusable<Tui> for TransportQuantize<Tui> {
fn is_focused (&self) -> bool { fn is_focused (&self) -> bool {
self.focused self.focused
} }
@ -220,11 +230,12 @@ impl Focusable<Tui> for TransportQuantize {
} }
} }
pub struct TransportSync { pub struct TransportSync<E: Engine> {
pub _engine: PhantomData<E>,
pub value: usize, pub value: usize,
pub focused: bool pub focused: bool
} }
impl Focusable<Tui> for TransportSync { impl Focusable<Tui> for TransportSync<Tui> {
fn is_focused (&self) -> bool { fn is_focused (&self) -> bool {
self.focused self.focused
} }
@ -233,14 +244,15 @@ impl Focusable<Tui> for TransportSync {
} }
} }
pub struct TransportClock { pub struct TransportClock<E: Engine> {
pub _engine: PhantomData<E>,
pub frame: usize, pub frame: usize,
pub pulse: usize, pub pulse: usize,
pub ppq: usize, pub ppq: usize,
pub usecs: usize, pub usecs: usize,
pub focused: bool, pub focused: bool,
} }
impl Focusable<Tui> for TransportClock { impl Focusable<Tui> for TransportClock<Tui> {
fn is_focused (&self) -> bool { fn is_focused (&self) -> bool {
self.focused self.focused
} }

View file

@ -1,6 +1,6 @@
use crate::*; use crate::*;
impl Handle<Tui> for TransportToolbar { impl Handle<Tui> for TransportToolbar<Tui> {
fn handle (&mut self, from: &Tui) -> Perhaps<bool> { fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
Ok(None) Ok(None)
//Ok( //Ok(
@ -13,13 +13,13 @@ impl Handle<Tui> for TransportToolbar {
} }
} }
impl Handle<Tui> for TransportPlayPauseButton { impl Handle<Tui> for TransportPlayPauseButton<Tui> {
fn handle (&mut self, from: &Tui) -> Perhaps<bool> { fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
Ok(None) Ok(None)
} }
} }
impl Handle<Tui> for TransportBPM { impl Handle<Tui> for TransportBPM<Tui> {
fn handle (&mut self, from: &Tui) -> Perhaps<bool> { fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
//TransportFocus::BPM => { //TransportFocus::BPM => {
//transport.timebase.bpm.fetch_add(1.0, Ordering::Relaxed); //transport.timebase.bpm.fetch_add(1.0, Ordering::Relaxed);
@ -31,7 +31,7 @@ impl Handle<Tui> for TransportBPM {
} }
} }
impl Handle<Tui> for TransportQuantize { impl Handle<Tui> for TransportQuantize<Tui> {
fn handle (&mut self, from: &Tui) -> Perhaps<bool> { fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
//TransportFocus::Quant => { //TransportFocus::Quant => {
//transport.quant.value = next_note_length(transport.quant) //transport.quant.value = next_note_length(transport.quant)
@ -43,7 +43,7 @@ impl Handle<Tui> for TransportQuantize {
} }
} }
impl Handle<Tui> for TransportSync { impl Handle<Tui> for TransportSync<Tui> {
fn handle (&mut self, from: &Tui) -> Perhaps<bool> { fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
//TransportFocus::Sync => { //TransportFocus::Sync => {
//transport.sync.value = next_note_length(transport.sync) //transport.sync.value = next_note_length(transport.sync)
@ -55,7 +55,7 @@ impl Handle<Tui> for TransportSync {
} }
} }
impl Handle<Tui> for TransportClock { impl Handle<Tui> for TransportClock<Tui> {
fn handle (&mut self, from: &Tui) -> Perhaps<bool> { fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
//TransportFocus::Sync => { //TransportFocus::Sync => {
//transport.sync.value = next_note_length(transport.sync) //transport.sync.value = next_note_length(transport.sync)

View file

@ -2,34 +2,26 @@ use crate::*;
const CORNERS: Corners = Corners(NOT_DIM_GREEN); const CORNERS: Corners = Corners(NOT_DIM_GREEN);
impl Layout<Tui> for TransportToolbar { impl Content for TransportToolbar<Tui> {
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { type Engine = Tui;
Ok(Some([area.x(), area.y(), area.w(), 2])) fn content (&self) -> impl Widget<Engine = Tui> {
}
}
impl Render<Tui> for TransportToolbar {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
Split::right() Split::right()
.add_ref(&self.playing) .add_ref(&self.playing)
.add_ref(&self.bpm) .add_ref(&self.bpm)
.add_ref(&self.quant) .add_ref(&self.quant)
.add_ref(&self.sync) .add_ref(&self.sync)
.add_ref(&self.clock) .add_ref(&self.clock)
.render(to.with_rect(self.layout(to.area())?.unwrap()))
} }
} }
impl Layout<Tui> for TransportPlayPauseButton { impl Widget for TransportPlayPauseButton<Tui> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
area.expect_min(10, 1)?; area.expect_min(10, 1)?;
Ok(Some([area.x(), area.y(), 10, 1])) Ok(Some([area.x(), area.y(), 10, 1]))
} }
}
impl Render<Tui> for TransportPlayPauseButton {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let Self { value, focused } = &self; let Self { value, focused, .. } = &self;
Layers(&[ Layers(&[
&focused.then_some(CORNERS), &focused.then_some(CORNERS),
&Inset::W(1, Styled(match value { &Inset::W(1, Styled(match value {
@ -70,18 +62,16 @@ impl Render<Tui> for TransportPlayPauseButton {
} }
} }
impl Layout<Tui> for TransportBPM { impl Widget for TransportBPM<Tui> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
area.expect_min(10, 1)?; area.expect_min(10, 1)?;
Ok(Some([area.x(), area.y(), 10, 1])) Ok(Some([area.x(), area.y(), 10, 1]))
} }
}
impl Render<Tui> for TransportBPM {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = to.area(); let area = to.area();
let [x, y, ..] = area; let [x, y, ..] = area;
let Self { value, focused } = self; let Self { value, focused, .. } = self;
to.blit(&"BPM", x, y, Some(NOT_DIM))?; to.blit(&"BPM", x, y, Some(NOT_DIM))?;
let bpm = format!("{}.{:03}", value, (value * 1000.0) % 1000.0); let bpm = format!("{}.{:03}", value, (value * 1000.0) % 1000.0);
to.blit(&bpm, x, y + 1, Some(NOT_DIM_BOLD))?; to.blit(&bpm, x, y + 1, Some(NOT_DIM_BOLD))?;
@ -96,17 +86,15 @@ impl Render<Tui> for TransportBPM {
} }
} }
impl Layout<Tui> for TransportQuantize { impl Widget for TransportQuantize<Tui> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
area.expect_min(10, 1)?; area.expect_min(10, 1)?;
Ok(Some([area.x(), area.y(), 10, 1])) Ok(Some([area.x(), area.y(), 10, 1]))
} }
}
impl Render<Tui> for TransportQuantize {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let [x, y, ..] = to.area(); let [x, y, ..] = to.area();
let Self { value, focused } = self; let Self { value, focused, .. } = self;
to.blit(&"QUANT", x, y, Some(NOT_DIM))?; to.blit(&"QUANT", x, y, Some(NOT_DIM))?;
let name = ppq_to_name(*value as usize); let name = ppq_to_name(*value as usize);
let width = name.len() as u16; let width = name.len() as u16;
@ -121,17 +109,15 @@ impl Render<Tui> for TransportQuantize {
} }
} }
impl Layout<Tui> for TransportSync { impl Widget for TransportSync<Tui> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
area.expect_min(10, 1)?; area.expect_min(10, 1)?;
Ok(Some([area.x(), area.y(), 10, 1])) Ok(Some([area.x(), area.y(), 10, 1]))
} }
}
impl Render<Tui> for TransportSync {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let [x, y, ..] = to.area(); let [x, y, ..] = to.area();
let Self { value, focused } = self; let Self { value, focused, .. } = self;
to.blit(&"SYNC", x, y, Some(NOT_DIM))?; to.blit(&"SYNC", x, y, Some(NOT_DIM))?;
let name = ppq_to_name(*value as usize); let name = ppq_to_name(*value as usize);
let width = name.len() as u16; let width = name.len() as u16;
@ -146,17 +132,15 @@ impl Render<Tui> for TransportSync {
} }
} }
impl Layout<Tui> for TransportClock { impl Widget for TransportClock<Tui> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
area.expect_min(10, 1)?; area.expect_min(10, 1)?;
Ok(Some([area.x(), area.y(), 20, 1])) Ok(Some([area.x(), area.y(), 20, 1]))
} }
}
impl Render<Tui> for TransportClock {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let [x, y, width, _] = to.area(); let [x, y, width, _] = to.area();
let Self { frame: _frame, pulse, ppq, usecs, focused } = self; let Self { frame: _frame, pulse, ppq, usecs, focused, .. } = self;
let (beats, pulses) = if *ppq > 0 { (pulse / ppq, pulse % ppq) } else { (0, 0) }; let (beats, pulses) = if *ppq > 0 { (pulse / ppq, pulse % ppq) } else { (0, 0) };
let (bars, beats) = ((beats / 4) + 1, (beats % 4) + 1); let (bars, beats) = ((beats / 4) + 1, (beats % 4) + 1);
let (seconds, msecs) = (usecs / 1000000, usecs / 1000 % 1000); let (seconds, msecs) = (usecs / 1000000, usecs / 1000 % 1000);