diff --git a/src/app.rs b/src/app.rs index 0be37c36..85d60d81 100644 --- a/src/app.rs +++ b/src/app.rs @@ -35,7 +35,7 @@ pub mod size; pub use self::size::*; /// Must not be dropped for the duration of the process pub jack: Jack<'static>, /// Display size - pub size: Size, + pub size: Sizer, /// Performance counter pub perf: PerfModel, /// Available view modes and input bindings diff --git a/src/app/draw.rs b/src/app/draw.rs index 0c8578b0..35bc6697 100644 --- a/src/app/draw.rs +++ b/src/app/draw.rs @@ -1,27 +1,25 @@ use crate::*; -tui_view!(|self: App| { - "" -}); - /// The [Draw] implementation for [App] handles the loaded view, /// which is defined in terms of [dizzle] DSL. /// /// If there is an error, the error is displayed. FIXME: overlay it /// Then, every top-level form of the DSL description is rendered. -impl Draw for App { - fn draw (self, to: &mut Tui) -> Usually> { - let xywh = to.area().into(); - if let Some(e) = self.error.read().unwrap().as_ref() { - to.show(xywh, e.as_ref())?; - } - for (index, dsl) in self.mode.view.iter().enumerate() { - if let Err(e) = self.interpret(to, dsl) { - *self.error.write().unwrap() = Some(format!("view #{index}: {e}").into()); - break; +impl View for App { + fn view (&self) -> impl Draw { + thunk(|to: &mut Tui|{ + let xywh = to.area().into(); + if let Some(e) = self.error.read().unwrap().as_ref() { + to.show(Layout::XYWH(xywh, e.as_ref()))?; } - } - Ok(xywh) + for (index, dsl) in self.mode.view.iter().enumerate() { + if let Err(e) = self.interpret(to, dsl) { + *self.error.write().unwrap() = Some(format!("view #{index}: {e}").into()); + break; + } + } + Ok(xywh) + }) } } @@ -127,7 +125,7 @@ pub fn draw_dialog ( if *selected == index { Rgb(240,200,180) } else { Rgb(200, 200, 200) }, if *selected == index { Rgb(80, 80, 50) } else { Rgb(30, 30, 30) }, h_exact(2, origin_n(w_full(item))) - )).draw(to); + )).draw(to)?; } Ok(to.area().into()) }))) @@ -364,7 +362,7 @@ pub fn view_scenes_clips <'a, S: ScenesSizes<'a>> ( tracks: impl TracksSizes<'a>, select: &Selection, editor: Option<&MidiEditor>, - size: &Size, + size: &Sizer, editing: bool, ) -> impl Draw { let status = wh_full(origin_se(fg(Green, format!("{}x{}", size.w(), size.h())))); @@ -465,7 +463,7 @@ pub fn view_track_names ( }, south(w_full(origin_nw(east( format!("·t{index:02} "), fg(Rgb(255, 255, 255), bold(true, &track.name)) - ))), ""))) ).draw(to); + ))), ""))) ).draw(to)?; } Ok(XYWH(0, 0, 0, 0)) })))) @@ -490,7 +488,7 @@ pub fn view_track_outputs ( h_exact(1, bg(track.color.dark.term, w_full(origin_w( format!("·o{index:02} {}", port.port_name())))))); w_exact(track_width(index, track), - origin_nw(h_full(iter_south(iter, draw)))).draw(to); + origin_nw(h_full(iter_south(iter, draw)))).draw(to)?; } Ok(XYWH(0, 0, 0, 0)) })))) @@ -513,7 +511,7 @@ pub fn view_track_inputs ( iter_south(||track.sequencer.midi_ins.iter(), |port, _|fg_bg(Rgb(255, 255, 255), track.color.dark.term, w_full(origin_w(format!("·i{index:02} {}", port.port_name())))))))) - .draw(to); + .draw(to)?; } Ok(XYWH(0, 0, 0, 0)) })))) @@ -527,7 +525,7 @@ pub fn view_scenes_names ( ) -> impl Draw { w_exact(20, thunk(move |to: &mut Tui|{ for (index, scene, ..) in scenes { - view_scene_name(select, editor, index, scene, editing).draw(to); + view_scene_name(select, editor, index, scene, editing).draw(to)?; } Ok(XYWH(1, 1, 1, 1)) })) @@ -639,10 +637,10 @@ pub fn view_outputs ( origin_w(east(" ● ", fg(Rgb(255,255,255), bold(true, port.port_name())))), w_full(origin_e(format!("{}/{} ", port.port().get_connections().len(), - port.connections.len())))))).draw(to); + port.connections.len())))))).draw(to)?; for (index, conn) in port.connections.iter().enumerate() { h_exact(1, w_full(origin_w(format!(" c{index:02}{}", conn.info())))) - .draw(to); + .draw(to)?; } } todo!(); @@ -658,14 +656,14 @@ pub fn view_outputs ( h_exact(1, origin_w(east( either(true, fg(Green, "play "), "play "), either(false, fg(Yellow, "solo "), "solo "), - ))).draw(to); + ))).draw(to)?; for (_index, port) in midi_outs.iter().enumerate() { h_exact(1, origin_w(east( either(true, fg(Green, " ● "), " · "), either(false, fg(Yellow, " ● "), " · "), - ))).draw(to); + ))).draw(to)?; for (_index, _conn) in port.connections.iter().enumerate() { - h_exact(1, w_full("")).draw(to); + h_exact(1, w_full("")).draw(to)?; } } todo!() @@ -734,7 +732,7 @@ pub fn view_sessions () -> impl Draw { let b = if index == 0 { Rgb(50,50,50) } else { Rgb(40,40,40) }; let y = (2 * index) as u16; let h = 2; - y_push(y, &h_exact(h, w_full(bg(b, origin_w(fg(f, name)))))).draw(to); + y_push(y, h_exact(h, w_full(bg(b, origin_w(fg(f, *name)))))).draw(to)?; } Ok(to.area().into()) }))) diff --git a/src/app/size.rs b/src/app/size.rs index 2d336b34..8c80c077 100644 --- a/src/app/size.rs +++ b/src/app/size.rs @@ -1,6 +1,6 @@ use crate::*; -impl_has!(Size: |self: App|self.size); +impl_has!(Sizer: |self: App|self.size); /// Define a type alias for iterators of sized items (columns). macro_rules! def_sizes_iter { diff --git a/src/device/arrange.rs b/src/device/arrange.rs index 8c3f51d8..e3836693 100644 --- a/src/device/arrange.rs +++ b/src/device/arrange.rs @@ -16,9 +16,9 @@ use crate::*; /// TODO rename to "render_cache" or smth pub arranger: Arc>, /// Display size - pub size: Size, + pub size: Sizer, /// Display size of clips area - pub size_inner: Size, + pub size_inner: Sizer, /// Source of time #[cfg(feature = "clock")] pub clock: Clock, /// Allows one MIDI clip to be edited @@ -47,7 +47,7 @@ use crate::*; impl HasJack<'static> for Arrangement { fn jack (&self) -> &Jack<'static> { &self.jack } } impl_has!(Jack<'static>: |self: Arrangement| self.jack); -impl_has!(Size: |self: Arrangement| self.size); +impl_has!(Sizer: |self: Arrangement| self.size); impl_has!(Vec: |self: Arrangement| self.midi_ins); impl_has!(Vec: |self: Arrangement| self.midi_outs); impl_has!(Clock: |self: Arrangement| self.clock); diff --git a/src/device/arrange/clip.rs b/src/device/arrange/clip.rs index 06679935..e572a9da 100644 --- a/src/device/arrange/clip.rs +++ b/src/device/arrange/clip.rs @@ -37,10 +37,6 @@ pub trait ClipsView: TracksView + ScenesView { } } -impl HasClipsSize for Arrangement { - fn clips_size (&self) -> &Size { &self.size_inner } -} - def_command!(ClipCommand: |clip: MidiClip| { SetColor { color: Option } => { //(SetColor [t: usize, s: usize, c: ItemTheme] @@ -86,8 +82,14 @@ impl Arrangement { } } -pub trait HasClipsSize { fn clips_size (&self) -> &Size; } +pub trait HasClipsSize { + fn clips_size (&self) -> &Sizer; +} impl HasClipsSize for App { - fn clips_size (&self) -> &Size { &self.project.size_inner } + fn clips_size (&self) -> &Sizer { &self.project.size_inner } +} + +impl HasClipsSize for Arrangement { + fn clips_size (&self) -> &Sizer { &self.size_inner } } diff --git a/src/device/browse.rs b/src/device/browse.rs index 4f0e444a..f8a196b8 100644 --- a/src/device/browse.rs +++ b/src/device/browse.rs @@ -21,7 +21,7 @@ def_command!(FileBrowserCommand: |sampler: Sampler|{ pub filter: String, pub index: usize, pub scroll: usize, - pub size: Size, + pub size: Sizer, } pub(crate) struct EntriesIterator<'a, S: Screen> { diff --git a/src/device/clock.rs b/src/device/clock.rs index ab53bbe5..5781a743 100644 --- a/src/device/clock.rs +++ b/src/device/clock.rs @@ -1,7 +1,12 @@ use crate::*; -use ::std::sync::{Arc, RwLock, atomic::{AtomicUsize, Ordering::*}}; +use ::std::sync::{Arc, RwLock, atomic::AtomicUsize}; use ::atomic_float::AtomicF64; -use ::tengri::{draw::*, term::*}; + +mod memo; pub use self::memo::*; +mod moment; pub use self::moment::*; +mod ticker; pub use self::ticker::*; +mod timebase; pub use self::timebase::*; +mod clock_view; pub use self::clock_view::*; impl +AsMut> HasClock for T {} pub trait HasClock: AsRef + AsMut { @@ -396,8 +401,3 @@ impl_time_unit!(Ppq); impl_time_unit!(Pulse); impl_time_unit!(Bpm); impl_time_unit!(LaunchSync); - -mod moment; pub use self::moment::*; -mod ticker; pub use self::ticker::*; -mod timebase; pub use self::timebase::*; -mod clock_view; pub use self::clock_view::*; diff --git a/src/device/clock/memo.rs b/src/device/clock/memo.rs new file mode 100644 index 00000000..91a3290c --- /dev/null +++ b/src/device/clock/memo.rs @@ -0,0 +1,53 @@ +use crate::*; + +#[macro_export] macro_rules! rewrite { + ($buf:ident, $($rest:tt)*) => { + |$buf,_,_|{ + $buf.clear(); + write!($buf, $($rest)*) + } + }; +} + +pub struct Memo { + value: T, + view: U, +} + +impl Memo { + pub fn new (value: T, view: U) -> Self { + Self { value, view } + } +} + +impl Debug for Memo { + fn fmt (&self, _: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + todo!() + } +} + +impl Memo { + pub fn update (&mut self, newval: T, render: impl Fn(&mut U, &T, &T)->R) -> Option { + if newval != self.value { + let result = render(&mut self.view, &newval, &self.value); + self.value = newval; + return Some(result); + } + None + } +} + +pub trait Gettable { + /// Returns current value + fn get (&self) -> T; +} + +pub trait Mutable: Gettable { + /// Sets new value, returns old + fn set (&mut self, value: T) -> T; +} + +pub trait InteriorMutable: Gettable { + /// Sets new value, returns old + fn set (&self, value: T) -> T; +} diff --git a/src/device/editor.rs b/src/device/editor.rs index a7455a22..80133a4d 100644 --- a/src/device/editor.rs +++ b/src/device/editor.rs @@ -19,12 +19,12 @@ pub struct MidiEditor { /// View mode and state of editor pub mode: PianoHorizontal, /// Size of editor on screen - pub size: Size, + pub size: Sizer, } impl_default!(MidiEditor: Self { mode: PianoHorizontal::new(None), - size: Size( + size: Sizer( Arc::new(0usize.into()), Arc::new(0usize.into()), ), @@ -255,13 +255,7 @@ pub trait MidiViewer: MidiRange + MidiPoint + Debug + Send + Sync { } } -impl_has!(Size: |self: MidiEditor| self.size); - -impl Draw for MidiEditor { - fn draw (self, to: &mut Tui) -> Usually> { - self.mode.tui().draw(to) - } -} +impl_has!(Sizer: |self: MidiEditor| self.size); impl std::fmt::Debug for MidiEditor { fn fmt (&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { @@ -303,7 +297,7 @@ impl View for MidiEditor { fn view (&self) -> impl Draw { self.autoscroll(); /*self.autozoom();*/ - self.size.of(&self.mode) + self.size.of(self.mode.view()) } } diff --git a/src/device/editor/piano.rs b/src/device/editor/piano.rs index d0b4d8f4..6f163b60 100644 --- a/src/device/editor/piano.rs +++ b/src/device/editor/piano.rs @@ -11,7 +11,7 @@ pub struct PianoHorizontal { /// Buffer where the whole clip is rerendered on change pub buffer: Arc>, /// Size of actual notes area - pub size: Size, + pub size: Sizer, /// The display window pub range: MidiSelection, /// The note cursor @@ -22,17 +22,21 @@ pub struct PianoHorizontal { pub keys_width: u16, } -impl_has!(Size: |self: PianoHorizontal| self.size); -impl Draw for PianoHorizontal { - fn draw (self, to: &mut Tui) -> Usually> { - self.tui().draw(to) +impl_has!(Sizer: |self: PianoHorizontal| self.size); + +impl View for PianoHorizontal { + fn view (&self) -> impl Draw { + south( + east(w_exact(5, format!("{}x{}", self.size.w(), self.size.h())), self.timeline()), + east(self.keys(), self.size.of(below(wh_full(self.notes()), wh_full(self.cursor())))), + ) } } impl PianoHorizontal { pub fn new (clip: Option<&Arc>>) -> Self { - let size = Size::default(); + let size = Sizer::default(); let mut range = MidiSelection::from((12, true)); range.time_axis = size.0.clone(); range.note_axis = size.1.clone(); @@ -49,13 +53,6 @@ impl PianoHorizontal { piano } - pub fn tui (&self) -> impl Draw { - south( - east(w_exact(5, format!("{}x{}", self.size.w(), self.size.h())), self.timeline()), - east(self.keys(), self.size.of(below(wh_full(self.notes()), wh_full(self.cursor())))), - ) - } - /// Draw the piano roll background. /// /// This mode uses full blocks on note on and half blocks on legato: █▄ █▄ █▄ diff --git a/src/device/sampler.rs b/src/device/sampler.rs index a537a795..40bd30a3 100644 --- a/src/device/sampler.rs +++ b/src/device/sampler.rs @@ -44,7 +44,7 @@ mod sample_kit; pub use self::sample_kit::*; /// Currently active modal, if any. pub mode: Option, /// Size of rendered sampler. - pub size: Size, + pub size: Sizer, /// Lowest note displayed. pub note_lo: AtomicUsize, /// Currently selected note. diff --git a/tengri b/tengri index 90ebae0f..0b9e9c06 160000 --- a/tengri +++ b/tengri @@ -1 +1 @@ -Subproject commit 90ebae0f26fc4524659704df274d0aec815dba2e +Subproject commit 0b9e9c06962e5cd55b639d9c0a7cef6f11405d81