diff --git a/crates/tek_core/src/focus.rs b/crates/tek_core/src/focus.rs index 3134fdd2..ccc04306 100644 --- a/crates/tek_core/src/focus.rs +++ b/crates/tek_core/src/focus.rs @@ -1,7 +1,7 @@ use crate::*; /// A component that may contain [Focusable] components. -pub trait Focus : Render + Handle { +pub trait Focus <'a, const N: usize, T, U>: Render<'a, T, U> + Handle { fn focus (&self) -> usize; fn focus_mut (&mut self) -> &mut usize; fn focusable (&self) -> [&dyn Focusable;N]; @@ -33,13 +33,13 @@ pub trait Focus : Render + Handle { } /// A component that may be focused. -pub trait Focusable: Render + Handle { +pub trait Focusable<'a, T, U>: Render<'a, T, U> + Handle { fn is_focused (&self) -> bool; fn set_focused (&mut self, focused: bool); } -impl, T, U> Focusable for Option - where Option: Render +impl<'a, F: Focusable<'a, T, U>, T, U> Focusable<'a, T, U> for Option + where Option: Render<'a, T, U> { fn is_focused (&self) -> bool { match self { @@ -59,21 +59,21 @@ impl, T, U> Focusable for Option ($struct:ident ($focus:ident) : $count:expr => [ $($focusable:ident),* ]) => { - impl Focus<$count> for $struct { + impl Focus<$count, T, U> for $struct { fn focus (&self) -> usize { self.$focus } fn focus_mut (&mut self) -> &mut usize { &mut self.$focus } - fn focusable (&self) -> [&dyn Focusable;$count] { + fn focusable (&self) -> [&dyn Focusable;$count] { [ - $(&self.$focusable as &dyn Focusable,)* + $(&self.$focusable as &dyn Focusable,)* ] } - fn focusable_mut (&mut self) -> [&mut dyn Focusable;$count] { + fn focusable_mut (&mut self) -> [&mut dyn Focusable;$count] { [ - $(&mut self.$focusable as &mut dyn Focusable,)* + $(&mut self.$focusable as &mut dyn Focusable,)* ] } } diff --git a/crates/tek_core/src/handle_keymap.rs b/crates/tek_core/src/handle_keymap.rs index dd5caad4..db5fd05c 100644 --- a/crates/tek_core/src/handle_keymap.rs +++ b/crates/tek_core/src/handle_keymap.rs @@ -32,3 +32,9 @@ pub fn handle_keymap ( ] as &'static [KeyBinding<$T>] } } + +#[macro_export] macro_rules! key { + ($k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr) => { + (KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as &dyn Fn()->Usually) + } +} diff --git a/crates/tek_core/src/jack_core.rs b/crates/tek_core/src/jack_core.rs index 7db017ac..b085fbbd 100644 --- a/crates/tek_core/src/jack_core.rs +++ b/crates/tek_core/src/jack_core.rs @@ -1,18 +1,18 @@ use crate::{*, jack::*}; /// A UI component that may be associated with a JACK client by the `Jack` factory. -pub trait Device: Render + Handle + Process + Send + Sync { +pub trait Device<'a, T, U>: Render<'a, T, U> + Handle + Process + Send + Sync { /// Perform type erasure for collecting heterogeneous devices. - fn boxed (self) -> Box> where Self: Sized + 'static { + fn boxed (self) -> Box> where Self: Sized + 'static { Box::new(self) } } /// All things that implement the required traits can be treated as `Device`. -impl Device for D where D: Render + Handle + Process {} +impl<'a, D, T, U> Device<'a, T, U> for D where D: Render<'a, T, U> + Handle + Process {} -impl Render for Box> { - fn render (&self, to: &mut T) -> Result, Box> { +impl<'a, T, U> Render<'a, T, U> for Box> { + fn render (&self, to: &'a mut T) -> Result, Box> { (**self).render(to) } } @@ -143,8 +143,10 @@ impl Jack { )?.0, }) } - pub fn run (self, state: impl FnOnce(JackPorts)->Box) -> Usually> - where D: Device + Process + Sized + 'static, + pub fn run <'a: 'static, D, T, U> ( + self, state: impl FnOnce(JackPorts)->Box + ) -> Usually> + where D: Device<'a, T, U> + Process + Sized + 'static, T: 'static, U: 'static { diff --git a/crates/tek_core/src/jack_device.rs b/crates/tek_core/src/jack_device.rs index b2bce35f..4eb5873c 100644 --- a/crates/tek_core/src/jack_device.rs +++ b/crates/tek_core/src/jack_device.rs @@ -1,31 +1,31 @@ use crate::{*, jack::*}; /// A [Device] bound to a JACK client and a set of ports. -pub struct JackDevice { +pub struct JackDevice<'a, T, U> { /// The active JACK client of this device. pub client: DynamicAsyncClient, /// The device state, encapsulated for sharing between threads. - pub state: Arc>>>, + pub state: Arc>>>, /// Unowned copies of the device's JACK ports, for connecting to the device. /// The "real" readable/writable `Port`s are owned by the `state`. pub ports: UnownedJackPorts, } -impl std::fmt::Debug for JackDevice { +impl<'a, T, U> std::fmt::Debug for JackDevice<'a, T, U> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("JackDevice").field("ports", &self.ports).finish() } } -impl<'a, T, U> Render for JackDevice { - fn render (&self, to: &mut T) -> Perhaps { +impl<'a, T, U> Render<'a, T, U> for JackDevice<'a, T, U> { + fn render (&self, to: &'a mut T) -> Perhaps { self.state.read().unwrap().render(to) } } -impl Handle for JackDevice { +impl<'a, T, U> Handle for JackDevice<'a, T, U> { fn handle (&mut self, event: &AppEvent) -> Usually { self.state.write().unwrap().handle(event) } } -impl Ports for JackDevice { +impl<'a, T, U> Ports for JackDevice<'a, T, U> { fn audio_ins (&self) -> Usually>> { Ok(self.ports.audio_ins.values().collect()) } @@ -39,13 +39,13 @@ impl Ports for JackDevice { Ok(self.ports.midi_outs.values().collect()) } } -impl JackDevice { +impl<'a, T, U> JackDevice<'a, T, U> { /// Returns a locked mutex of the state's contents. - pub fn state (&self) -> LockResult>>> { + pub fn state (&self) -> LockResult>>> { self.state.read() } /// Returns a locked mutex of the state's contents. - pub fn state_mut (&self) -> LockResult>>> { + pub fn state_mut (&self) -> LockResult>>> { self.state.write() } pub fn connect_midi_in (&self, index: usize, port: &Port) -> Usually<()> { diff --git a/crates/tek_core/src/lib.rs b/crates/tek_core/src/lib.rs index c886320c..edd00501 100644 --- a/crates/tek_core/src/lib.rs +++ b/crates/tek_core/src/lib.rs @@ -54,14 +54,13 @@ submod! { jack_ports render render_axis - render_border render_buffer render_collect render_layered render_split render_tui - render_tui_fill render_tui_theme + render_tui_border time_base time_note time_tick @@ -86,28 +85,28 @@ pub type Usually = Result>; pub type Perhaps = Result, Box>; /// A UI component. -pub trait Component: Render + Handle + Sync { +pub trait Component<'a, T, U>: Render<'a, T, U> + Handle + Sync { /// Perform type erasure for collecting heterogeneous components. - fn boxed (self) -> Box> where Self: Sized + 'static { + fn boxed (self) -> Box> where Self: Sized + 'static { Box::new(self) } } -impl Component for C where C: Render + Handle + Sync {} +impl<'a, C, T, U> Component<'a, T, U> for C where C: Render<'a, T, U> + Handle + Sync {} /// Marker trait for [Component]s that can [Exit] -pub trait ExitableComponent: Exit + Component { +pub trait ExitableComponent<'a, T, U>: Exit + Component<'a, T, U> { /// Perform type erasure for collecting heterogeneous components. - fn boxed (self) -> Box> where Self: Sized + 'static { + fn boxed (self) -> Box> where Self: Sized + 'static { Box::new(self) } } -impl, T, U> ExitableComponent for E {} +impl<'a, E: Exit + Component<'a, T, U>, T, U> ExitableComponent<'a, T, U> for E {} /// Run the main loop. pub fn run (state: Arc>) -> Usually>> - where R: for <'a> Render, Rect> + Handle + Sized + 'static + where R: for <'a> Render<'a, TuiOutput<'a>, Rect> + Handle + Sized + 'static { let exited = Arc::new(AtomicBool::new(false)); let _input_thread = input_thread(&exited, &state); diff --git a/crates/tek_core/src/render.rs b/crates/tek_core/src/render.rs index 7d5488c6..f7935709 100644 --- a/crates/tek_core/src/render.rs +++ b/crates/tek_core/src/render.rs @@ -4,13 +4,13 @@ use crate::*; pub(crate) use ratatui::prelude::CrosstermBackend; /// Trait for things that are displayed to the user. -pub trait Render: Send + Sync { - fn render (&self, to: &mut T) -> Perhaps; +pub trait Render<'a, T, U>: Send + Sync { + fn render (&self, to: &'a mut T) -> Perhaps; } /// Options can be rendered optionally. -impl Render for Option where R: Render { - fn render (&self, to: &mut T) -> Perhaps { +impl<'a, R, T, U> Render<'a, T, U> for Option where R: Render<'a, T, U> { + fn render (&self, to: &'a mut T) -> Perhaps { match self { Some(component) => component.render(to), None => Ok(None) @@ -19,43 +19,43 @@ impl Render for Option where R: Render { } /// Boxed references can be rendered. -impl<'a, T, U> Render for Box + 'a> { - fn render (&self, to: &mut T) -> Perhaps { +impl<'a, T, U> Render<'a, T, U> for Box + 'a> { + fn render (&self, to: &'a mut T) -> Perhaps { (**self).render(to) } } /// Immutable references can be rendered. -impl Render for &R where R: Render { - fn render (&self, to: &mut T) -> Perhaps { +impl<'a, R, T, U> Render<'a, T, U> for &R where R: Render<'a, T, U> { + fn render (&self, to: &'a mut T) -> Perhaps { (*self).render(to) } } /// Mutable references can be rendered. -impl Render for &mut R where R: Render { - fn render (&self, to: &mut T) -> Perhaps { +impl<'a, R, T, U> Render<'a, T, U> for &mut R where R: Render<'a, T, U> { + fn render (&self, to: &'a mut T) -> Perhaps { (**self).render(to) } } /// Counted references can be rendered. -impl Render for Arc where R: Render { - fn render (&self, to: &mut T) -> Perhaps { +impl<'a, R, T, U> Render<'a, T, U> for Arc where R: Render<'a, T, U> { + fn render (&self, to: &'a mut T) -> Perhaps { self.as_ref().render(to) } } /// References behind a [Mutex] can be rendered. -impl Render for Mutex where R: Render { - fn render (&self, to: &mut T) -> Perhaps { +impl<'a, R, T, U> Render<'a, T, U> for Mutex where R: Render<'a, T, U> { + fn render (&self, to: &'a mut T) -> Perhaps { self.lock().unwrap().render(to) } } /// References behind a [RwLock] can be rendered. -impl Render for RwLock where R: Render { - fn render (&self, to: &mut T) -> Perhaps { +impl<'a, R, T, U> Render<'a, T, U> for RwLock where R: Render<'a, T, U> { + fn render (&self, to: &'a mut T) -> Perhaps { self.read().unwrap().render(to) } } @@ -65,7 +65,7 @@ impl Render for RwLock where R: Render { /// Rendering unboxed closures should also be possible; /// but in practice implementing the trait for an unboxed /// `Fn` closure causes an impl conflict. -impl<'a, T, U> Render for Box Perhaps + Send + Sync + 'a> { +impl<'a, T, U> Render<'a, T, U> for Box Perhaps + Send + Sync + 'a> { fn render (&self, to: &mut T) -> Perhaps { (*self)(to) } diff --git a/crates/tek_core/src/render_collect.rs b/crates/tek_core/src/render_collect.rs index e89e5b6f..af37753f 100644 --- a/crates/tek_core/src/render_collect.rs +++ b/crates/tek_core/src/render_collect.rs @@ -1,11 +1,11 @@ use crate::*; pub enum Collected<'a, T, U> { - Box(Box + 'a>), - Ref(&'a (dyn Render + 'a)), + Box(Box + 'a>), + Ref(&'a (dyn Render<'a, T, U> + 'a)), } -impl<'a, T, U> Render for Collected<'a, T, U> { - fn render (&self, to: &mut T) -> Perhaps { +impl<'a, T, U> Render<'a, T, U> for Collected<'a, T, U> { + fn render (&self, to: &'a mut T) -> Perhaps { match self { Self::Box(item) => (*item).render(to), Self::Ref(item) => (*item).render(to), @@ -21,20 +21,20 @@ impl<'a, T, U> Collection<'a, T, U> { } } pub trait Collect<'a, T, U> { - fn add_box (self, item: Box + 'a>) -> Self; - fn add_ref (self, item: &'a dyn Render) -> Self; - fn add + Sized + 'a> (self, item: R) -> Self + fn add_box (self, item: Box + 'a>) -> Self; + fn add_ref (self, item: &'a dyn Render<'a, T, U>) -> Self; + fn add + Sized + 'a> (self, item: R) -> Self where Self: Sized { self.add_box(Box::new(item)) } } impl<'a, T, U> Collect<'a, T, U> for Collection<'a, T, U> { - fn add_box (mut self, item: Box + 'a>) -> Self { + fn add_box (mut self, item: Box + 'a>) -> Self { self.0.push(Collected::Box(item)); self } - fn add_ref (mut self, item: &'a dyn Render) -> Self { + fn add_ref (mut self, item: &'a dyn Render<'a, T, U>) -> Self { self.0.push(Collected::Ref(item)); self } diff --git a/crates/tek_core/src/render_layered.rs b/crates/tek_core/src/render_layered.rs index f159d693..95f1199d 100644 --- a/crates/tek_core/src/render_layered.rs +++ b/crates/tek_core/src/render_layered.rs @@ -9,18 +9,18 @@ impl<'a, T, U> Layered<'a, T, U> { } impl<'a, T, U> Collect<'a, T, U> for Layered<'a, T, U> { - fn add_box (mut self, item: Box + 'a>) -> Self { + fn add_box (mut self, item: Box + 'a>) -> Self { self.0 = self.0.add_box(item); self } - fn add_ref (mut self, item: &'a dyn Render) -> Self { + fn add_ref (mut self, item: &'a dyn Render<'a, T, U>) -> Self { self.0 = self.0.add_ref(item); self } } -impl<'a, 'b> Render, Rect> for Layered<'a, TuiOutput<'b>, Rect> { - fn render (&self, to: &mut TuiOutput<'b>) -> Perhaps { +impl<'a, 'b> Render<'a, TuiOutput<'b>, Rect> for Layered<'a, TuiOutput<'b>, Rect> { + fn render (&self, to: &'a mut TuiOutput<'b>) -> Perhaps { let area = to.area; for layer in self.0.0.iter() { layer.render(to)?; diff --git a/crates/tek_core/src/render_split.rs b/crates/tek_core/src/render_split.rs index c5cd3268..dc107c8c 100644 --- a/crates/tek_core/src/render_split.rs +++ b/crates/tek_core/src/render_split.rs @@ -44,17 +44,17 @@ impl<'a, T, U> Split<'a, T, U> { } impl<'a, T, U> Collect<'a, T, U> for Split<'a, T, U> { - fn add_box (mut self, item: Box + 'a>) -> Self { + fn add_box (mut self, item: Box + 'a>) -> Self { self.items = self.items.add_box(item); self } - fn add_ref (mut self, item: &'a dyn Render) -> Self { + fn add_ref (mut self, item: &'a dyn Render<'a, T, U>) -> Self { self.items = self.items.add_ref(item); self } } -impl<'a> Render, Rect> for Split<'a, TuiOutput<'a>, Rect> { +impl<'a> Render<'a, TuiOutput<'a>, Rect> for Split<'a, TuiOutput<'a>, Rect> { fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { Ok(None)//Rect::default())//Some(self.render_areas(to)?.0)) } diff --git a/crates/tek_core/src/render_tui.rs b/crates/tek_core/src/render_tui.rs index cff261ae..3be11af8 100644 --- a/crates/tek_core/src/render_tui.rs +++ b/crates/tek_core/src/render_tui.rs @@ -7,12 +7,17 @@ pub struct TuiOutput<'a> { pub buffer: &'a mut Buffer, pub area: Rect } +impl<'a> TuiOutput<'a> { + pub fn area (&'a mut self, area: Rect) -> Self { + Self { buffer: self.buffer, area } + } +} /// Main thread render loop pub fn tui_render_thread (exited: &Arc, device: &Arc>) -> Usually> where - T: for <'a> Render, Rect> + 'static + T: for <'a> Render<'a, TuiOutput<'a>, Rect> + 'static { let exited = exited.clone(); let device = device.clone(); @@ -29,7 +34,7 @@ where let previous_buffer = &buffers[1 - index]; let current_buffer = &buffers[index]; let updates = previous_buffer.diff(current_buffer); - backend.draw(updates.into_iter()); + backend.draw(updates.into_iter()).expect("failed to render"); buffers[1 - index].reset(); index = 1 - index; } @@ -82,7 +87,7 @@ impl> Blit for T { } /// Rendering unit struct to Ratatui returns zero-sized [Rect] at render coordinates. -impl<'a> Render, Rect> for () { +impl<'a> Render<'a, TuiOutput<'a>, Rect> for () { fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { Ok(Some(Rect { x: to.area.x, y: to.area.y, width: 0, height: 0 })) } @@ -104,3 +109,53 @@ pub fn half_block (lower: bool, upper: bool) -> Option { _ => None } } + +pub struct FillBg(pub Color); + +impl<'a> Render<'a, TuiOutput<'a>, Rect> for FillBg { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + fill_bg(to.buffer, to.area, self.0); + Ok(Some(to.area)) + } +} + +pub fn make_dim (buf: &mut Buffer) { + for cell in buf.content.iter_mut() { + cell.bg = ratatui::style::Color::Rgb(30,30,30); + cell.fg = ratatui::style::Color::Rgb(100,100,100); + cell.modifier = ratatui::style::Modifier::DIM; + } +} + +pub fn buffer_update ( + buf: &mut Buffer, area: Rect, callback: &impl Fn(&mut Cell, u16, u16) +) { + for row in 0..area.height { + let y = area.y + row; + for col in 0..area.width { + let x = area.x + col; + if x < buf.area.width && y < buf.area.height { + callback(buf.get_mut(x, y), col, row); + } + } + } +} + +pub fn fill_fg (buf: &mut Buffer, area: Rect, color: Color) { + buffer_update(buf, area, &|cell,_,_|{cell.set_fg(color);}) +} + +pub fn fill_bg (buf: &mut Buffer, area: Rect, color: Color) { + buffer_update(buf, area, &|cell,_,_|{cell.set_bg(color);}) +} + +pub fn fill_ul (buf: &mut Buffer, area: Rect, color: Color) { + buffer_update(buf, area, &|cell,_,_|{ + cell.modifier = ratatui::prelude::Modifier::UNDERLINED; + cell.underline_color = color; + }) +} + +pub fn fill_char (buf: &mut Buffer, area: Rect, c: char) { + buffer_update(buf, area, &|cell,_,_|{cell.set_char(c);}) +} diff --git a/crates/tek_core/src/render_border.rs b/crates/tek_core/src/render_tui_border.rs similarity index 94% rename from crates/tek_core/src/render_border.rs rename to crates/tek_core/src/render_tui_border.rs index 3b512a8c..7b704b03 100644 --- a/crates/tek_core/src/render_border.rs +++ b/crates/tek_core/src/render_tui_border.rs @@ -11,11 +11,11 @@ pub trait BorderStyle { const W: &'static str = ""; #[inline] - fn draw (&self, buf: &mut Buffer, area: Rect) -> Usually { - self.draw_horizontal(buf, area, None)?; - self.draw_vertical(buf, area, None)?; - self.draw_corners(buf, area, None)?; - Ok(area) + fn draw <'a> (&self, to: &mut TuiOutput<'a>) -> Perhaps { + self.draw_horizontal(to.buffer, to.area, None)?; + self.draw_vertical(to.buffer, to.area, None)?; + self.draw_corners(to.buffer, to.area, None)?; + Ok(Some(to.area)) } #[inline] diff --git a/crates/tek_core/src/render_tui_fill.rs b/crates/tek_core/src/render_tui_fill.rs deleted file mode 100644 index 38da7d40..00000000 --- a/crates/tek_core/src/render_tui_fill.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::*; - -pub struct FillBg(pub Color); - -impl<'a> Render, Rect> for FillBg { - fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { - fill_bg(to.buffer, to.area, self.0); - Ok(Some(to.area)) - } -} - -pub fn make_dim (buf: &mut Buffer) { - for cell in buf.content.iter_mut() { - cell.bg = ratatui::style::Color::Rgb(30,30,30); - cell.fg = ratatui::style::Color::Rgb(100,100,100); - cell.modifier = ratatui::style::Modifier::DIM; - } -} - -pub fn buffer_update ( - buf: &mut Buffer, area: Rect, callback: &impl Fn(&mut Cell, u16, u16) -) { - for row in 0..area.height { - let y = area.y + row; - for col in 0..area.width { - let x = area.x + col; - if x < buf.area.width && y < buf.area.height { - callback(buf.get_mut(x, y), col, row); - } - } - } -} - -pub fn fill_fg (buf: &mut Buffer, area: Rect, color: Color) { - buffer_update(buf, area, &|cell,_,_|{cell.set_fg(color);}) -} - -pub fn fill_bg (buf: &mut Buffer, area: Rect, color: Color) { - buffer_update(buf, area, &|cell,_,_|{cell.set_bg(color);}) -} - -pub fn fill_ul (buf: &mut Buffer, area: Rect, color: Color) { - buffer_update(buf, area, &|cell,_,_|{ - cell.modifier = ratatui::prelude::Modifier::UNDERLINED; - cell.underline_color = color; - }) -} - -pub fn fill_char (buf: &mut Buffer, area: Rect, c: char) { - buffer_update(buf, area, &|cell,_,_|{cell.set_char(c);}) -} diff --git a/crates/tek_mixer/src/lib.rs b/crates/tek_mixer/src/lib.rs index 9d929731..72d47000 100644 --- a/crates/tek_mixer/src/lib.rs +++ b/crates/tek_mixer/src/lib.rs @@ -1,5 +1,4 @@ pub(crate) use tek_core::*; -pub(crate) use tek_core::ratatui::prelude::*; pub(crate) use tek_core::crossterm::event::{KeyCode, KeyModifiers}; pub(crate) use tek_core::midly::{num::u7, live::LiveEvent, MidiMessage}; pub(crate) use tek_core::jack::*; @@ -14,7 +13,6 @@ submod! { mixer mixer_cli mixer_handle - mixer_render track track_view track_handle diff --git a/crates/tek_mixer/src/mixer.rs b/crates/tek_mixer/src/mixer.rs index ebf2a209..e035d0a1 100644 --- a/crates/tek_mixer/src/mixer.rs +++ b/crates/tek_mixer/src/mixer.rs @@ -1,13 +1,12 @@ use crate::*; -pub struct Mixer { +pub struct Mixer { pub name: String, - pub tracks: Vec, + pub tracks: Vec>, pub selected_track: usize, pub selected_column: usize, } - -impl Mixer { +impl Mixer { pub fn new (name: &str) -> Usually { let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?; Ok(Self { @@ -22,17 +21,21 @@ impl Mixer { self.tracks.push(track); Ok(self) } - pub fn track (&self) -> Option<&Track> { + pub fn track (&self) -> Option<&Track> { self.tracks.get(self.selected_track) } } - -process!(Mixer = process); - -fn process ( - _: &mut Mixer, - _: &Client, - _: &ProcessScope -) -> Control { - Control::Continue +impl<'a> Render, Rect> for Mixer, Rect> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + let mut tracks = Split::right(); + for channel in self.tracks.iter() { + tracks = tracks.add_ref(channel) + } + tracks.render(to) + } +} +impl Process for Mixer { + fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { + Control::Continue + } } diff --git a/crates/tek_mixer/src/mixer_cli.rs b/crates/tek_mixer/src/mixer_cli.rs index f08ea10a..e927b091 100644 --- a/crates/tek_mixer/src/mixer_cli.rs +++ b/crates/tek_mixer/src/mixer_cli.rs @@ -1,6 +1,5 @@ use tek_core::clap::{self, Parser}; use crate::*; - #[derive(Debug, Parser)] #[command(version, about, long_about = None)] pub struct MixerCli { @@ -9,8 +8,7 @@ pub struct MixerCli { /// Number of tracks #[arg(short, long)] channels: Option, } - -impl Mixer { +impl Mixer { pub fn from_args () -> Usually { let args = MixerCli::parse(); let mut mix = Self::new("")?; diff --git a/crates/tek_mixer/src/mixer_handle.rs b/crates/tek_mixer/src/mixer_handle.rs index cccda861..7bdbf785 100644 --- a/crates/tek_mixer/src/mixer_handle.rs +++ b/crates/tek_mixer/src/mixer_handle.rs @@ -1,56 +1,57 @@ use crate::*; -handle!(Mixer = handle_mixer); +impl Handle for Mixer { + fn handle (&mut self, event: &AppEvent) -> Usually { + if let AppEvent::Input(crossterm::event::Event::Key(event)) = event { + + match event.code { + //KeyCode::Char('c') => { + //if event.modifiers == KeyModifiers::CONTROL { + //self.exit(); + //} + //}, + KeyCode::Down => { + self.selected_track = (self.selected_track + 1) % self.tracks.len(); + println!("{}", self.selected_track); + return Ok(true) + }, + KeyCode::Up => { + if self.selected_track == 0 { + self.selected_track = self.tracks.len() - 1; + } else { + self.selected_track -= 1; + } + println!("{}", self.selected_track); + return Ok(true) + }, + KeyCode::Left => { + if self.selected_column == 0 { + self.selected_column = 6 + } else { + self.selected_column -= 1; + } + return Ok(true) + }, + KeyCode::Right => { + if self.selected_column == 6 { + self.selected_column = 0 + } else { + self.selected_column += 1; + } + return Ok(true) + }, + _ => { + println!("\n{event:?}"); + } + } + + } + Ok(false) + } +} //pub const ACTIONS: [(&'static str, &'static str);2] = [ //("+/-", "Adjust"), //("Ins/Del", "Add/remove track"), //]; -pub fn handle_mixer (state: &mut Mixer, event: &AppEvent) -> Usually { - if let AppEvent::Input(crossterm::event::Event::Key(event)) = event { - - match event.code { - //KeyCode::Char('c') => { - //if event.modifiers == KeyModifiers::CONTROL { - //state.exit(); - //} - //}, - KeyCode::Down => { - state.selected_track = (state.selected_track + 1) % state.tracks.len(); - println!("{}", state.selected_track); - return Ok(true) - }, - KeyCode::Up => { - if state.selected_track == 0 { - state.selected_track = state.tracks.len() - 1; - } else { - state.selected_track -= 1; - } - println!("{}", state.selected_track); - return Ok(true) - }, - KeyCode::Left => { - if state.selected_column == 0 { - state.selected_column = 6 - } else { - state.selected_column -= 1; - } - return Ok(true) - }, - KeyCode::Right => { - if state.selected_column == 6 { - state.selected_column = 0 - } else { - state.selected_column += 1; - } - return Ok(true) - }, - _ => { - println!("\n{event:?}"); - } - } - - } - Ok(false) -} diff --git a/crates/tek_mixer/src/mixer_render.rs b/crates/tek_mixer/src/mixer_render.rs deleted file mode 100644 index aa1b15f8..00000000 --- a/crates/tek_mixer/src/mixer_render.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::*; - -impl<'a> Render, Rect> for Mixer { - fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { - let mut x = 0; - for channel in self.tracks.iter() { - x = x + channel.render(buf, Rect { - x: area.x + x, - y: area.y, - width: area.width, - height: area.height - })?.width; - if x >= area.width { - break - } - } - Ok(area) - } -} diff --git a/crates/tek_mixer/src/plugin.rs b/crates/tek_mixer/src/plugin.rs index d205b703..5889c145 100644 --- a/crates/tek_mixer/src/plugin.rs +++ b/crates/tek_mixer/src/plugin.rs @@ -12,12 +12,12 @@ pub struct Plugin { handle!(Plugin |self, e| handle_keymap(self, e, KEYMAP_PLUGIN)); process!(Plugin = Plugin::process); impl<'a> Render, Rect> for Plugin { - fn render (&self, to: &mut TuiOutput<'a>) -> Usually { - let Rect { x, y, height, .. } = area; + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + let Rect { x, y, height, .. } = to.area; let mut width = 20u16; - match &state.plugin { + match &self.plugin { Some(PluginKind::LV2(LV2Plugin { port_list, instance, .. })) => { - let start = state.selected.saturating_sub((height as usize / 2).saturating_sub(1)); + let start = self.selected.saturating_sub((height as usize / 2).saturating_sub(1)); let end = start + height as usize - 2; //draw_box(buf, Rect { x, y, width, height }); for i in start..end { @@ -30,11 +30,12 @@ impl<'a> Render, Rect> for Plugin { //let label = &format!("C·· M·· {:25} = {value:.03}", port.name); let label = &format!("{:25} = {value:.03}", port.name); width = width.max(label.len() as u16 + 4); - label.blit(buf, x + 2, y + 1 + i as u16 - start as u16, if i == state.selected { + let style = if i == self.selected { Some(Style::default().green()) } else { None - })?; + } ; + label.blit(to.buffer, x + 2, y + 1 + i as u16 - start as u16, style)?; } else { break } @@ -42,8 +43,8 @@ impl<'a> Render, Rect> for Plugin { }, _ => {} }; - draw_header(state, buf, area.x, area.y, width)?; - Ok(Some(Rect { width, ..area })) + draw_header(self, to.buffer, to.area.x, to.area.y, width)?; + Ok(Some(Rect { width, ..to.area })) } } @@ -56,7 +57,7 @@ pub enum PluginKind { VST3, } impl Plugin { - pub fn new_lv2 (name: &str, path: &str) -> Usually { + pub fn new_lv2 (name: &str, path: &str) -> Usually> { let plugin = LV2Plugin::new(path)?; jack_from_lv2(name, &plugin.plugin)? .run(|ports|Box::new(Self { diff --git a/crates/tek_mixer/src/plugin_lv2.rs b/crates/tek_mixer/src/plugin_lv2.rs index 5c4c860b..febccc95 100644 --- a/crates/tek_mixer/src/plugin_lv2.rs +++ b/crates/tek_mixer/src/plugin_lv2.rs @@ -22,7 +22,7 @@ pub struct LV2Plugin { } impl LV2Plugin { - pub fn from_edn <'e> (args: &[Edn<'e>]) -> Usually { + pub fn from_edn <'e, T, U> (args: &[Edn<'e>]) -> Usually> { let mut name = String::new(); let mut path = String::new(); edn!(edn in args { diff --git a/crates/tek_mixer/src/sample_add.rs b/crates/tek_mixer/src/sample_add.rs index 229f0ede..d2a27409 100644 --- a/crates/tek_mixer/src/sample_add.rs +++ b/crates/tek_mixer/src/sample_add.rs @@ -23,20 +23,20 @@ pub struct AddSampleModal { exit!(AddSampleModal); impl<'a> Render, Rect> for AddSampleModal { - fn render (&self, to: &mut TuiOutput<'a>) -> Usually { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { make_dim(to.buffer); let area = center_box( to.area, 64.max(to.area.width.saturating_sub(8)), 20.max(to.area.width.saturating_sub(8)), ); - fill_fg(buf, area, Color::Reset); - fill_bg(buf, area, Nord::bg_lo(true, true)); - fill_char(buf, area, ' '); + fill_fg(to.buffer, area, Color::Reset); + fill_bg(to.buffer, area, Nord::bg_lo(true, true)); + fill_char(to.buffer, area, ' '); format!("{}", &self.dir.to_string_lossy()) - .blit(buf, area.x+2, area.y+1, Some(Style::default().bold()))?; + .blit(to.buffer, area.x+2, area.y+1, Some(Style::default().bold()))?; "Select sample:" - .blit(buf, area.x+2, area.y+2, Some(Style::default().bold()))?; + .blit(to.buffer, area.x+2, area.y+2, Some(Style::default().bold()))?; for (i, (is_dir, name)) in self.subdirs.iter() .map(|path|(true, path)) .chain(self.files.iter().map(|path|(false, path))) @@ -49,13 +49,13 @@ impl<'a> Render, Rect> for AddSampleModal { let t = if is_dir { "" } else { "" }; let line = format!("{t} {}", name.to_string_lossy()); let line = &line[..line.len().min(area.width as usize - 4)]; - line.blit(buf, area.x + 2, area.y + 3 + i as u16, Some(if i == self.cursor { + line.blit(to.buffer, area.x + 2, area.y + 3 + i as u16, Some(if i == self.cursor { Style::default().green() } else { Style::default().white() }))?; } - Lozenge(Style::default()).draw(buf, area) + Lozenge(Style::default()).draw(to) } } diff --git a/crates/tek_mixer/src/sampler.rs b/crates/tek_mixer/src/sampler.rs index 38e95a8f..7c9adef1 100644 --- a/crates/tek_mixer/src/sampler.rs +++ b/crates/tek_mixer/src/sampler.rs @@ -19,11 +19,11 @@ process!(Sampler = Sampler::process); handle!(Sampler |self, event| handle_keymap(self, event, KEYMAP_SAMPLER)); impl<'a> Render, Rect> for Sampler { - fn render (&self, to: &mut TuiOutput<'a>) -> Usually { - let Rect { x, y, height, .. } = area; + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + let Rect { x, y, height, .. } = to.area; let style = Style::default().gray(); let title = format!(" {} ({})", self.name, self.voices.read().unwrap().len()); - title.blit(buf, x+1, y, Some(style.white().bold().not_dim()))?; + title.blit(to.buffer, x+1, y, Some(style.white().bold().not_dim()))?; let mut width = title.len() + 2; let mut y1 = 1; let mut j = 0; @@ -35,12 +35,14 @@ impl<'a> Render, Rect> for Sampler { break } let active = j == self.cursor.0; - width = width.max(draw_sample(buf, x, y + y1, note, &*sample.read().unwrap(), active)?); + width = width.max( + draw_sample(to.buffer, x, y + y1, note, &*sample.read().unwrap(), active)? + ); y1 = y1 + 1; j = j + 1; } let height = ((2 + y1) as u16).min(height); - Ok(Rect { x, y, width: (width as u16).min(area.width), height }) + Ok(Some(Rect { x, y, width: (width as u16).min(to.area.width), height })) } } @@ -103,7 +105,7 @@ pub const KEYMAP_SAMPLER: &'static [KeyBinding] = keymap!(Sampler { }); impl Sampler { - pub fn from_edn <'e> (args: &[Edn<'e>]) -> Usually { + pub fn from_edn <'e, T, U> (args: &[Edn<'e>]) -> Usually> { let mut name = String::new(); let mut dir = String::new(); let mut samples = BTreeMap::new(); @@ -132,7 +134,9 @@ impl Sampler { Self::new(&name, Some(samples)) } - pub fn new (name: &str, mapped: Option>>>) -> Usually { + pub fn new ( + name: &str, mapped: Option>>> + ) -> Usually> { Jack::new(name)? .midi_in("midi") .audio_in("recL") diff --git a/crates/tek_mixer/src/track.rs b/crates/tek_mixer/src/track.rs index fdbf4408..417a380f 100644 --- a/crates/tek_mixer/src/track.rs +++ b/crates/tek_mixer/src/track.rs @@ -3,18 +3,16 @@ use tek_core::edn; /// A sequencer track. #[derive(Debug)] -pub struct Track { +pub struct Track { pub name: String, /// Inputs and outputs of 1st and last device pub ports: JackPorts, /// Device chain - pub devices: Vec, + pub devices: Vec>, /// Device selector pub device: usize, } - -impl Track { - +impl Track { pub fn new (name: &str) -> Usually { Ok(Self { name: name.to_string(), @@ -23,15 +21,14 @@ impl Track { device: 0, }) } - - fn get_device_mut (&self, i: usize) -> Option>> { + fn get_device_mut (&self, i: usize) -> Option>>> { self.devices.get(i).map(|d|d.state.write().unwrap()) } - pub fn device_mut (&self) -> Option>> { + pub fn device_mut (&self) -> Option>>> { self.get_device_mut(self.device) } /// Add a device to the end of the chain. - pub fn append_device (&mut self, device: JackDevice) -> Usually<&mut JackDevice> { + pub fn append_device (&mut self, device: JackDevice) -> Usually<&mut JackDevice> { self.devices.push(device); let index = self.devices.len() - 1; Ok(&mut self.devices[index]) @@ -57,7 +54,7 @@ impl Track { let mut _gain = 0.0f64; let mut track = Self::new("")?; #[allow(unused_mut)] - let mut devices: Vec = vec![]; + let mut devices: Vec> = vec![]; edn!(edn in args { Edn::Map(map) => { if let Some(Edn::Str(n)) = map.get(&Edn::Key(SYM_NAME)) { @@ -98,8 +95,7 @@ impl Track { } Ok(track) } - - pub fn add_device (&mut self, device: JackDevice) { + pub fn add_device (&mut self, device: JackDevice) { self.devices.push(device); } } diff --git a/crates/tek_mixer/src/track_handle.rs b/crates/tek_mixer/src/track_handle.rs index 0fe87698..0d30772b 100644 --- a/crates/tek_mixer/src/track_handle.rs +++ b/crates/tek_mixer/src/track_handle.rs @@ -1,31 +1,42 @@ use crate::*; -handle!(Track |self, event| handle_keymap(self, event, KEYMAP_TRACK)); - -/// Key bindings for chain section. -pub const KEYMAP_TRACK: &'static [KeyBinding] = keymap!(Track { - [Up, NONE, "chain_cursor_up", "move cursor up", |_: &mut Track| { - Ok(true) - }], - [Down, NONE, "chain_cursor_down", "move cursor down", |_: &mut Track| { - Ok(true) - }], - [Left, NONE, "chain_cursor_left", "move cursor left", |app: &mut Track| { - //if let Some(track) = app.arranger.track_mut() { - //track.device = track.device.saturating_sub(1); - //return Ok(true) - //} - Ok(false) - }], - [Right, NONE, "chain_cursor_right", "move cursor right", |app: &mut Track| { - //if let Some(track) = app.arranger.track_mut() { - //track.device = (track.device + 1).min(track.devices.len().saturating_sub(1)); - //return Ok(true) - //} - Ok(false) - }], - [Char('`'), NONE, "chain_mode_switch", "switch the display mode", |app: &mut Track| { - //app.chain_mode = !app.chain_mode; - Ok(true) - }], -}); +impl Handle for Track { + fn handle (&mut self, event: &AppEvent) -> Usually { + match event { + AppEvent::Input(crossterm::event::Event::Key(event)) => { + for (code, modifiers, _, _, command) in [ + key!(Up, NONE, "chain_cursor_up", "move cursor up", || { + Ok(true) + }), + key!(Down, NONE, "chain_cursor_down", "move cursor down", || { + Ok(true) + }), + key!(Left, NONE, "chain_cursor_left", "move cursor left", || { + //if let Some(track) = app.arranger.track_mut() { + //track.device = track.device.saturating_sub(1); + //return Ok(true) + //} + Ok(false) + }), + key!(Right, NONE, "chain_cursor_right", "move cursor right", || { + //if let Some(track) = app.arranger.track_mut() { + //track.device = (track.device + 1).min(track.devices.len().saturating_sub(1)); + //return Ok(true) + //} + Ok(false) + }), + key!(Char('`'), NONE, "chain_mode_switch", "switch the display mode", || { + //app.chain_mode = !app.chain_mode; + Ok(true) + }), + ].iter() { + if *code == event.code && modifiers.bits() == event.modifiers.bits() { + return command() + } + } + return Ok(false) + }, + _ => Ok(false) + } + } +} diff --git a/crates/tek_mixer/src/track_view.rs b/crates/tek_mixer/src/track_view.rs index 0098a12a..7ed8fcec 100644 --- a/crates/tek_mixer/src/track_view.rs +++ b/crates/tek_mixer/src/track_view.rs @@ -1,7 +1,7 @@ use crate::*; use tek_core::Direction; -impl<'a> Render, Rect> for Track { +impl<'a> Render, Rect> for Track, Rect> { fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { TrackView { chain: Some(&self), @@ -24,37 +24,37 @@ impl<'a> Render, Rect> for Track { }.render(to) } } -pub struct TrackView<'a> { - pub chain: Option<&'a Track>, +pub struct TrackView<'a, T, U> { + pub chain: Option<&'a Track>, pub direction: Direction, pub focused: bool, pub entered: bool, } -impl<'a> Render, Rect> for TrackView<'a> { +impl<'a> Render, Rect> for TrackView<'a, TuiOutput<'a>, Rect> { fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { if let Some(chain) = self.chain { match self.direction { - Direction::Down => area.width = area.width.min(40), - Direction::Right => area.width = area.width.min(10), + Direction::Down => to.area.width = to.area.width.min(40), + Direction::Right => to.area.width = to.area.width.min(10), _ => { unimplemented!() }, } - fill_bg(buf, area, Nord::bg_lo(self.focused, self.entered)); + fill_bg(to.buffer, to.area, Nord::bg_lo(self.focused, self.entered)); let mut split = Split::new(self.direction); for device in chain.devices.as_slice().iter() { split = split.add_ref(device); } - let (area, areas) = split.render_areas(buf, area)?; + let (area, areas) = split.render_areas(to)?; if self.focused && self.entered && areas.len() > 0 { - Corners(Style::default().green().not_dim()).draw(buf, areas[0])?; + Corners(Style::default().green().not_dim()).draw(to.buffer, areas[0])?; } - Ok(area) + Ok(Some(area)) } else { - let Rect { x, y, width, height } = area; + let Rect { x, y, width, height } = to.area; let label = "No chain selected"; let x = x + (width - label.len() as u16) / 2; let y = y + height / 2; - label.blit(buf, x, y, Some(Style::default().dim().bold()))?; - Ok(area) + label.blit(to.buffer, x, y, Some(Style::default().dim().bold()))?; + Ok(Some(to.area)) } } } diff --git a/crates/tek_sequencer/src/arranger.rs b/crates/tek_sequencer/src/arranger.rs index 378b3061..6736ffff 100644 --- a/crates/tek_sequencer/src/arranger.rs +++ b/crates/tek_sequencer/src/arranger.rs @@ -2,7 +2,7 @@ use crate::*; /// Represents the tracks and scenes of the composition. -pub struct Arranger { +pub struct Arranger { /// Name of arranger pub name: Arc>, /// Collection of tracks. @@ -14,14 +14,11 @@ pub struct Arranger { /// Display mode of arranger pub mode: ArrangerViewMode, /// Slot for modal dialog displayed on top of app. - pub modal: Option>, + pub modal: Option>>, /// Whether the arranger is currently focused pub focused: bool } - -focusable!(Arranger (focused)); - -impl Arranger { +impl Arranger { pub fn new (name: &str) -> Self { Self { name: Arc::new(RwLock::new(name.into())), @@ -83,3 +80,56 @@ impl Arranger { } } } +/// Display mode of arranger +pub enum ArrangerViewMode { + VerticalExpanded, + VerticalCompact1, + VerticalCompact2, + Horizontal, +} +/// Arranger display mode can be cycled +impl ArrangerViewMode { + /// Cycle arranger display mode + pub fn to_next (&mut self) { + *self = match self { + Self::VerticalExpanded => Self::VerticalCompact1, + Self::VerticalCompact1 => Self::VerticalCompact2, + Self::VerticalCompact2 => Self::Horizontal, + Self::Horizontal => Self::VerticalExpanded, + } + } +} +/// Render arranger to terminal +impl<'a> Render, Rect> for Arranger, Rect> { + fn render (&'a self, to: &'a mut TuiOutput<'a>) -> Perhaps { + let area = (|to|match self.mode { + ArrangerViewMode::Horizontal => + super::arranger_view_h::draw(self, to), + ArrangerViewMode::VerticalCompact1 => + super::arranger_view_v::draw_compact_1(self, to), + ArrangerViewMode::VerticalCompact2 => + super::arranger_view_v::draw_compact_2(self, to), + ArrangerViewMode::VerticalExpanded => + super::arranger_view_v::draw_expanded(self, to), + })(&mut to.area(Rect { + x: to.area.x + 1, + width: to.area.width - 2, + y: to.area.y + 1, + height: to.area.height - 2 + }))?.unwrap(); + Lozenge(Style::default().fg(Nord::BG2)).draw(&mut to.area(Rect { + x: area.x.saturating_sub(1), + width: area.width + 2, + y: area.y.saturating_sub(1), + height: area.height + 2, + })) + } +} +impl<'a> Focusable, Rect> for Arranger, Rect> { + fn is_focused (&self) -> bool { + self.focused + } + fn set_focused (&mut self, focused: bool) { + self.focused = focused + } +} diff --git a/crates/tek_sequencer/src/arranger_handle.rs b/crates/tek_sequencer/src/arranger_handle.rs index 5c0fad00..d8e48866 100644 --- a/crates/tek_sequencer/src/arranger_handle.rs +++ b/crates/tek_sequencer/src/arranger_handle.rs @@ -1,86 +1,71 @@ use crate::*; -handle!(Arranger |self, e| { - match self.modal.as_mut() { - Some(modal) => { - let result = modal.handle(e)?; - if modal.exited() { - self.modal = None; +impl Handle for Arranger { + fn handle (&mut self, event: &AppEvent) -> Usually { + match self.modal.as_mut() { + Some(modal) => { + let result = modal.handle(event)?; + if modal.exited() { + self.modal = None; + } + Ok(result) + }, + None => match event { + AppEvent::Input(crossterm::event::Event::Key(event)) => { + for (code, modifiers, _, _, command) in [ + key!(Char('`'), NONE, "mode_switch", "switch the display mode", || { + self.mode.to_next(); + Ok(true) + }), + key!(Up, NONE, "cursor_up", "move cursor up", || { + match self.mode { + ArrangerViewMode::Horizontal => self.track_prev(), + _ => self.scene_prev(), + }; + self.show_phrase()?; + Ok(true) + }), + key!(Down, NONE, "cursor_down", "move cursor down", || { + match self.mode { + ArrangerViewMode::Horizontal => self.track_next(), + _ => self.scene_next(), + }; + self.show_phrase()?; + Ok(true) + }), + key!(Left, NONE, "cursor_left", "move cursor left", || { + match self.mode { + ArrangerViewMode::Horizontal => self.scene_prev(), + _ => self.track_prev(), + }; + self.show_phrase()?; + Ok(true) + }), + key!(Right, NONE, "cursor_right", "move cursor right", || { + match self.mode { + ArrangerViewMode::Horizontal => self.scene_next(), + _ => self.track_next(), + }; + self.show_phrase()?; + Ok(true) + }), + key!(Char('.'), NONE, "increment", "set next clip at cursor", || { self.phrase_next(); Ok(true) }), + key!(Char(','), NONE, "decrement", "set previous clip at cursor", || { self.phrase_prev(); Ok(true) }), + key!(Enter, NONE, "activate", "activate item at cursor", || { self.activate(); Ok(true) }), + key!(Char('a'), CONTROL, "scene_add", "add a new scene", || { self.scene_add(None)?; Ok(true) }), + key!(Char('t'), CONTROL, "track_add", "add a new track", || { self.track_add(None)?; Ok(true) }), + key!(Char('n'), NONE, "rename", "rename item at cursor", || { self.rename_selected(); Ok(true) }), + key!(Char('l'), NONE, "length", "set length of item at cursor", || { todo!(); Ok(true) }), + key!(Char('c'), NONE, "color", "set color of item at cursor", || { todo!(); Ok(true) }) + ].iter() { + if *code == event.code && modifiers.bits() == event.modifiers.bits() { + return command() + } + } + return Ok(false) + }, + _ => Ok(false) } - Ok(result) - }, - None => handle_keymap(self, e, KEYMAP_ARRANGER) + } } -}); - -/// Key bindings for arranger section. -pub const KEYMAP_ARRANGER: &'static [KeyBinding] = keymap!(Arranger { - [Char('`'), NONE, "mode_switch", "switch the display mode", |arranger: &mut Arranger| { - arranger.mode.to_next(); - Ok(true) - }], - [Up, NONE, "cursor_up", "move cursor up", |arranger: &mut Arranger| { - match arranger.mode { - ArrangerViewMode::Horizontal => arranger.track_prev(), - _ => arranger.scene_prev(), - }; - arranger.show_phrase()?; - Ok(true) - }], - [Down, NONE, "cursor_down", "move cursor down", |arranger: &mut Arranger| { - match arranger.mode { - ArrangerViewMode::Horizontal => arranger.track_next(), - _ => arranger.scene_next(), - }; - arranger.show_phrase()?; - Ok(true) - }], - [Left, NONE, "cursor_left", "move cursor left", |arranger: &mut Arranger| { - match arranger.mode { - ArrangerViewMode::Horizontal => arranger.scene_prev(), - _ => arranger.track_prev(), - }; - arranger.show_phrase()?; - Ok(true) - }], - [Right, NONE, "cursor_right", "move cursor right", |arranger: &mut Arranger| { - match arranger.mode { - ArrangerViewMode::Horizontal => arranger.scene_next(), - _ => arranger.track_next(), - }; - arranger.show_phrase()?; - Ok(true) - }], - [Char('.'), NONE, "increment", "set next clip at cursor", |arranger: &mut Arranger| { - arranger.phrase_next(); - Ok(true) - }], - [Char(','), NONE, "decrement", "set previous clip at cursor", |arranger: &mut Arranger| { - arranger.phrase_prev(); - Ok(true) - }], - [Enter, NONE, "activate", "activate item at cursor", |arranger: &mut Arranger| { - arranger.activate(); - Ok(true) - }], - [Char('a'), CONTROL, "scene_add", "add a new scene", |arranger: &mut Arranger| { - arranger.scene_add(None)?; - Ok(true) - }], - [Char('t'), CONTROL, "track_add", "add a new track", |arranger: &mut Arranger| { - arranger.track_add(None)?; - Ok(true) - }], - [Char('n'), NONE, "rename", "rename item at cursor", |arranger: &mut Arranger| { - arranger.rename_selected(); - Ok(true) - }], - [Char('l'), NONE, "length", "set length of item at cursor", |arranger: &mut Arranger| { - todo!(); - Ok(true) - }], - [Char('c'), NONE, "color", "set color of item at cursor", |arranger: &mut Arranger| { - todo!(); - Ok(true) - }] -}); +} diff --git a/crates/tek_sequencer/src/arranger_rename.rs b/crates/tek_sequencer/src/arranger_rename.rs index 104298ff..24a348d3 100644 --- a/crates/tek_sequencer/src/arranger_rename.rs +++ b/crates/tek_sequencer/src/arranger_rename.rs @@ -1,6 +1,5 @@ use crate::*; - -impl Arranger { +impl<'a> Arranger, Rect> { pub fn rename_selected (&mut self) { self.modal = Some(Box::new(ArrangerRenameModal::new( self.selected, @@ -13,7 +12,6 @@ impl Arranger { ))); } } - /// Appears on first run (i.e. if state dir is missing). pub struct ArrangerRenameModal { done: bool, @@ -22,7 +20,6 @@ pub struct ArrangerRenameModal { result: Arc>, cursor: usize } - impl ArrangerRenameModal { pub fn new (target: ArrangerFocus, value: &Arc>) -> Self { Self { @@ -34,18 +31,17 @@ impl ArrangerRenameModal { } } } - impl<'a> Render, Rect> for ArrangerRenameModal { - fn render (&self, to: &mut TuiOutput<'a>) -> Usually { - let y = area.y + area.height / 2; + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + let y = to.area.y + to.area.height / 2; let bg_area = Rect { x: 1, y: y - 1, - width: area.width - 2, + width: to.area.width - 2, height: 3 }; - fill_bg(buf, bg_area, Nord::BG0); - Lozenge(Style::default().bold().white().dim()).draw(buf, bg_area)?; + fill_bg(to.buffer, bg_area, Nord::BG0); + Lozenge(Style::default().bold().white().dim()).draw(to.buffer, bg_area)?; let label = match self.target { ArrangerFocus::Mix => "Rename project:", ArrangerFocus::Track(_) => "Rename track:", @@ -53,15 +49,14 @@ impl<'a> Render, Rect> for ArrangerRenameModal { ArrangerFocus::Clip(_, _) => "Rename clip:", }; let style = Some(Style::default().not_bold().white().not_dim()); - label.blit(buf, area.x + 3, y, style)?; + label.blit(to.buffer, to.area.x + 3, y, style)?; let style = Some(Style::default().bold().white().not_dim()); - self.value.blit(buf, area.x + 3 + label.len() as u16 + 1, y, style)?; + self.value.blit(to.buffer, to.area.x + 3 + label.len() as u16 + 1, y, style)?; let style = Some(Style::default().bold().white().not_dim().reversed()); - "▂".blit(buf, area.x + 3 + label.len() as u16 + 1 + self.cursor as u16, y, style)?; - Ok(area) + "▂".blit(to.buffer, to.area.x + 3 + label.len() as u16 + 1 + self.cursor as u16, y, style)?; + Ok(Some(to.area)) } } - handle!(ArrangerRenameModal |self, e| { match e { AppEvent::Input(Event::Key(k)) => { @@ -98,7 +93,6 @@ handle!(ArrangerRenameModal |self, e| { _ => Ok(false), } }); - impl Exit for ArrangerRenameModal { fn exited (&self) -> bool { self.done diff --git a/crates/tek_sequencer/src/arranger_track.rs b/crates/tek_sequencer/src/arranger_track.rs index 53b30aa4..fd899605 100644 --- a/crates/tek_sequencer/src/arranger_track.rs +++ b/crates/tek_sequencer/src/arranger_track.rs @@ -3,7 +3,7 @@ use crate::*; use super::Arranger; /// Track management methods -impl Arranger { +impl Arranger { pub fn track (&self) -> Option<&Sequencer> { self.selected.track().map(|t|self.tracks.get(t)).flatten() } diff --git a/crates/tek_sequencer/src/arranger_view.rs b/crates/tek_sequencer/src/arranger_view.rs deleted file mode 100644 index 97b0a34e..00000000 --- a/crates/tek_sequencer/src/arranger_view.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::*; - -/// Display mode of arranger -pub enum ArrangerViewMode { - VerticalExpanded, - VerticalCompact1, - VerticalCompact2, - Horizontal, -} - -impl ArrangerViewMode { - pub fn to_next (&mut self) { - *self = match self { - Self::VerticalExpanded => Self::VerticalCompact1, - Self::VerticalCompact1 => Self::VerticalCompact2, - Self::VerticalCompact2 => Self::Horizontal, - Self::Horizontal => Self::VerticalExpanded, - } - } -} - -impl<'a> Render, Rect> for Arranger { - fn render (&self, to: &mut TuiOutput<'a>) -> Usually { - let area = Rect { - x: to.area.x + 1, - width: to.area.width - 2, - y: to.area.y + 1, - height: to.area.height - 2 - }; - let area = match self.mode { - ArrangerViewMode::Horizontal => - super::arranger_view_h::draw(self, to.buffer, area), - ArrangerViewMode::VerticalCompact1 => - super::arranger_view_v::draw_compact_1(self, to.buffer, area), - ArrangerViewMode::VerticalCompact2 => - super::arranger_view_v::draw_compact_2(self, to.buffer, area), - ArrangerViewMode::VerticalExpanded => - super::arranger_view_v::draw_expanded(self, to.buffer, area), - }?; - let area = Rect { - x: area.x - 1, - width: area.width + 2, - y: area.y - 1, - height: area.height + 2, - }; - Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, area) - } -} diff --git a/crates/tek_sequencer/src/arranger_view_h.rs b/crates/tek_sequencer/src/arranger_view_h.rs index 9609521a..6d7d8271 100644 --- a/crates/tek_sequencer/src/arranger_view_h.rs +++ b/crates/tek_sequencer/src/arranger_view_h.rs @@ -1,6 +1,9 @@ use crate::*; -pub fn draw (state: &Arranger, buf: &mut Buffer, mut area: Rect) -> Usually { +pub fn draw <'a> ( + state: &Arranger, Rect>, to: &mut TuiOutput<'a> +) -> Perhaps { + let mut area = to.area; area.height = area.height.min((2 + state.tracks.len() * 2) as u16); let tracks = state.tracks.as_slice(); Layered::new() @@ -13,51 +16,53 @@ pub fn draw (state: &Arranger, buf: &mut Buffer, mut area: Rect) -> Usually(&'a [Sequencer], ArrangerFocus); -impl<'a> Render for TrackNameColumn<'a> { - fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually { +impl<'a> Render, Rect> for TrackNameColumn<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { let Self(tracks, selected) = self; + let mut area = to.area; let yellow = Some(Style::default().yellow().bold().not_dim()); let white = Some(Style::default().white().bold().not_dim()); area.width = 3 + 5.max(track_name_max_len(tracks)) as u16; let offset = 0; // track scroll offset for y in 0..area.height { if y == 0 { - "Mixer".blit(buf, area.x + 1, area.y + y, Some(DIM))?; + "Mixer".blit(to.buffer, area.x + 1, area.y + y, Some(DIM))?; } else if y % 2 == 0 { let index = (y as usize - 2) / 2 + offset; if let Some(track) = tracks.get(index) { let selected = selected.track() == Some(index); let style = if selected { yellow } else { white }; - format!(" {index:>02} ").blit(buf, area.x, area.y + y, style)?; - track.name.read().unwrap().blit(buf, area.x + 4, area.y + y, style)?; + format!(" {index:>02} ").blit(to.buffer, area.x, area.y + y, style)?; + track.name.read().unwrap().blit(to.buffer, area.x + 4, area.y + y, style)?; } } } - Ok(area) + Ok(Some(area)) } } struct TrackMonitorColumn<'a>(&'a [Sequencer]); -impl<'a> Render for TrackMonitorColumn<'a> { - fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually { +impl<'a> Render, Rect> for TrackMonitorColumn<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { let Self(tracks) = self; + let mut area = to.area; let on = Some(Style::default().not_dim().green().bold()); let off = Some(DIM); area.x += 1; for y in 0..area.height { if y == 0 { - //" MON ".blit(buf, area.x, area.y + y, style2)?; + //" MON ".blit(to.buffer, area.x, area.y + y, style2)?; } else if y % 2 == 0 { let index = (y as usize - 2) / 2; if let Some(track) = tracks.get(index) { let style = if track.monitoring { on } else { off }; - " MON ".blit(buf, area.x, area.y + y, style)?; + " MON ".blit(to.buffer, area.x, area.y + y, style)?; } else { area.height = y; break @@ -65,26 +70,27 @@ impl<'a> Render for TrackMonitorColumn<'a> { } } area.width = 4; - Ok(area) + Ok(Some(area)) } } struct TrackRecordColumn<'a>(&'a [Sequencer]); -impl<'a> Render for TrackRecordColumn<'a> { - fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually { +impl<'a> Render, Rect> for TrackRecordColumn<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { let Self(tracks) = self; + let mut area = to.area; let on = Some(Style::default().not_dim().red().bold()); let off = Some(Style::default().dim()); area.x += 1; for y in 0..area.height { if y == 0 { - //" REC ".blit(buf, area.x, area.y + y, style2)?; + //" REC ".blit(to.buffer, area.x, area.y + y, style2)?; } else if y % 2 == 0 { let index = (y as usize - 2) / 2; if let Some(track) = tracks.get(index) { let style = if track.recording { on } else { off }; - " REC ".blit(buf, area.x, area.y + y, style)?; + " REC ".blit(to.buffer, area.x, area.y + y, style)?; } else { area.height = y; break @@ -92,25 +98,26 @@ impl<'a> Render for TrackRecordColumn<'a> { } } area.width = 4; - Ok(area) + Ok(Some(area)) } } struct TrackOverdubColumn<'a>(&'a [Sequencer]); -impl<'a> Render for TrackOverdubColumn<'a> { - fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually { +impl<'a> Render, Rect> for TrackOverdubColumn<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { let Self(tracks) = self; + let mut area = to.area; let on = Some(Style::default().not_dim().yellow().bold()); let off = Some(Style::default().dim()); area.x = area.x + 1; for y in 0..area.height { if y == 0 { - //" OVR ".blit(buf, area.x, area.y + y, style2)?; + //" OVR ".blit(to.buffer, area.x, area.y + y, style2)?; } else if y % 2 == 0 { let index = (y as usize - 2) / 2; if let Some(track) = tracks.get(index) { - " OVR ".blit(buf, area.x, area.y + y, if track.overdub { + " OVR ".blit(to.buffer, area.x, area.y + y, if track.overdub { on } else { off @@ -122,24 +129,25 @@ impl<'a> Render for TrackOverdubColumn<'a> { } } area.width = 4; - Ok(area) + Ok(Some(area)) } } struct TrackEraseColumn<'a>(&'a [Sequencer]); -impl<'a> Render for TrackEraseColumn<'a> { - fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually { +impl<'a> Render, Rect> for TrackEraseColumn<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { let Self(tracks) = self; + let mut area = to.area; let off = Some(Style::default().dim()); area.x = area.x + 1; for y in 0..area.height { if y == 0 { - //" DEL ".blit(buf, area.x, area.y + y, style2)?; + //" DEL ".blit(to.buffer, area.x, area.y + y, style2)?; } else if y % 2 == 0 { let index = (y as usize - 2) / 2; if let Some(_) = tracks.get(index) { - " DEL ".blit(buf, area.x, area.y + y, off)?; + " DEL ".blit(to.buffer, area.x, area.y + y, off)?; } else { area.height = y; break @@ -147,24 +155,25 @@ impl<'a> Render for TrackEraseColumn<'a> { } } area.width = 4; - Ok(area) + Ok(Some(area)) } } struct TrackGainColumn<'a>(&'a [Sequencer]); -impl<'a> Render for TrackGainColumn<'a> { - fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually { +impl<'a> Render, Rect> for TrackGainColumn<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { let Self(tracks) = self; + let mut area = to.area; let off = Some(Style::default().dim()); area.x = area.x + 1; for y in 0..area.height { if y == 0 { - //" GAIN ".blit(buf, area.x, area.y + y, style2)?; + //" GAIN ".blit(to.buffer, area.x, area.y + y, style2)?; } else if y % 2 == 0 { let index = (y as usize - 2) / 2; if let Some(_) = tracks.get(index) { - " +0.0 ".blit(buf, area.x, area.y + y, off)?; + " +0.0 ".blit(to.buffer, area.x, area.y + y, off)?; } else { area.height = y; break @@ -172,15 +181,16 @@ impl<'a> Render for TrackGainColumn<'a> { } } area.width = 7; - Ok(area) + Ok(Some(area)) } } struct TrackScenesColumn<'a>(&'a [Sequencer], &'a [Scene], ArrangerFocus); -impl<'a> Render for TrackScenesColumn<'a> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { +impl<'a> Render, Rect> for TrackScenesColumn<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { let Self(tracks, scenes, selected) = self; + let area = to.area; let mut x2 = 0; let Rect { x, y, height, .. } = area; for (scene_index, scene) in scenes.iter().enumerate() { @@ -191,11 +201,11 @@ impl<'a> Render for TrackScenesColumn<'a> { Style::default().dim() }); for y in y+1..y+height { - "│".blit(buf, x + x2, y, sep)?; + "│".blit(to.buffer, x + x2, y, sep)?; } let name = scene.name.read().unwrap(); let mut x3 = name.len() as u16; - name.blit(buf, x + x2, y, sep)?; + name.blit(to.buffer, x + x2, y, sep)?; for (i, clip) in scene.clips.iter().enumerate() { let active_track = selected.track() == Some(i); if let Some(clip) = clip { @@ -204,7 +214,7 @@ impl<'a> Render for TrackScenesColumn<'a> { Some(phrase) => &format!("{}", phrase.read().unwrap().name.read().unwrap()), None => "...." }; - label.blit(buf, x + x2, y2, Some(if active_track && active_scene { + label.blit(to.buffer, x + x2, y2, Some(if active_track && active_scene { Style::default().not_dim().yellow().bold() } else { Style::default().not_dim() @@ -214,6 +224,6 @@ impl<'a> Render for TrackScenesColumn<'a> { } x2 = x2 + x3 + 1; } - Ok(Rect { x, y, height, width: x2 }) + Ok(Some(Rect { x, y, height, width: x2 })) } } diff --git a/crates/tek_sequencer/src/arranger_view_v.rs b/crates/tek_sequencer/src/arranger_view_v.rs index 1087b52f..b5dbe49e 100644 --- a/crates/tek_sequencer/src/arranger_view_v.rs +++ b/crates/tek_sequencer/src/arranger_view_v.rs @@ -1,33 +1,39 @@ use crate::*; /// Draw arranger with 1 row per scene. -pub fn draw_compact_1 (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually { +pub fn draw_compact_1 <'a> ( + state: &Arranger, Rect>, to: &mut TuiOutput<'a> +) -> Perhaps { let track_cols = track_clip_name_lengths(state.tracks.as_slice()); let scene_rows = (0..=state.scenes.len()).map(|i|(96, 96*i)).collect::>(); - draw(state, buf, area, track_cols.as_slice(), scene_rows.as_slice()) + draw(state, to, track_cols.as_slice(), scene_rows.as_slice()) } /// Draw arranger with 2 rows per scene. -pub fn draw_compact_2 (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually { +pub fn draw_compact_2 <'a> ( + state: &Arranger, Rect>, to: &mut TuiOutput<'a> +) -> Perhaps { let track_cols = track_clip_name_lengths(state.tracks.as_slice()); let scene_rows = (0..=state.scenes.len()).map(|i|(192, 192*i)).collect::>(); - draw(state, buf, area, track_cols.as_slice(), scene_rows.as_slice()) + draw(state, to, track_cols.as_slice(), scene_rows.as_slice()) } /// Draw arranger with number of rows per scene proportional to duration of scene. -pub fn draw_expanded (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually { +pub fn draw_expanded <'a> ( + state: &Arranger, Rect>, to: &mut TuiOutput<'a> +) -> Perhaps { let track_cols = track_clip_name_lengths(state.tracks.as_slice()); let scene_rows = scene_ppqs(state.tracks.as_slice(), state.scenes.as_slice()); - draw(state, buf, area, track_cols.as_slice(), scene_rows.as_slice()) + draw(state, to, track_cols.as_slice(), scene_rows.as_slice()) } -pub fn draw ( - state: &Arranger, - buf: &mut Buffer, - mut area: Rect, - cols: &[(usize, usize)], - rows: &[(usize, usize)], -) -> Usually { +pub fn draw <'a, 'b> ( + state: &Arranger, Rect>, + to: &mut TuiOutput<'a>, + cols: &'b [(usize, usize)], + rows: &'b [(usize, usize)], +) -> Perhaps { + let mut area = to.area; area.height = 2 + (rows[rows.len() - 1].1 / 96) as u16; let offset = 3 + scene_name_max_len(state.scenes.as_ref()) as u16; let tracks = state.tracks.as_ref(); @@ -40,42 +46,44 @@ pub fn draw ( .add_ref(&Split::down() .add_ref(&TracksHeader(offset, cols, tracks)) .add_ref(&SceneRows(offset, cols, rows, tracks, scenes))) - .render(buf, area) + .render(to) } struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]); -impl<'a> Render for ColumnSeparators<'a> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { +impl<'a> Render, Rect> for ColumnSeparators<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + let area = to.area; let Self(offset, cols) = self; let style = Some(Style::default().fg(Nord::SEPARATOR)); for (_, x) in cols.iter() { let x = offset + area.x + *x as u16 - 1; for y in area.y..area.height+area.y { - "▎".blit(buf, x, y, style)?; + "▎".blit(to.buffer, x, y, style)?; } } - Ok(area) + Ok(Some(area)) } } struct RowSeparators<'a>(&'a [(usize, usize)]); -impl<'a> Render for RowSeparators<'a> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { +impl<'a> Render, Rect> for RowSeparators<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + let mut area = to.area; let Self(rows) = self; for (_, y) in rows.iter() { let y = area.y + (*y / 96) as u16 + 1; - if y >= buf.area.height { + if y >= to.buffer.area.height { break } for x in area.x..area.width+area.y-2 { - let cell = buf.get_mut(x, y); + let cell = to.buffer.get_mut(x, y); cell.modifier = Modifier::UNDERLINED; cell.underline_color = Nord::SEPARATOR; } } - Ok(area) + Ok(Some(area)) } } @@ -83,8 +91,9 @@ struct CursorFocus<'a>( ArrangerFocus, u16, &'a [(usize, usize)], &'a [(usize, usize)] ); -impl<'a> Render for CursorFocus<'a> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { +impl<'a> Render, Rect> for CursorFocus<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + let mut area = to.area; let Self(selected, offset, cols, rows) = *self; let get_track_area = |t: usize| Rect { x: offset + area.x + cols[t].1 as u16 - 1, @@ -109,7 +118,7 @@ impl<'a> Render for CursorFocus<'a> { let mut clip_area: Option = None; let area = match selected { ArrangerFocus::Mix => { - fill_bg(buf, area, COLOR_BG0); + fill_bg(to.buffer, area, COLOR_BG0); area }, ArrangerFocus::Track(t) => { @@ -128,19 +137,19 @@ impl<'a> Render for CursorFocus<'a> { }, }; if let Some(Rect { x, y, width, height }) = track_area { - fill_fg(buf, Rect { x, y, width: 1, height }, COLOR_BG5); - fill_fg(buf, Rect { x: x + width, y, width: 1, height }, COLOR_BG5); + fill_fg(to.buffer, Rect { x, y, width: 1, height }, COLOR_BG5); + fill_fg(to.buffer, Rect { x: x + width, y, width: 1, height }, COLOR_BG5); } if let Some(Rect { y, height, .. }) = scene_area { - fill_ul(buf, Rect { x: area.x, y: y - 1, width: area.width, height: 1 }, COLOR_BG5); - fill_ul(buf, Rect { x: area.x, y: y + height - 1, width: area.width, height: 1 }, COLOR_BG5); + fill_ul(to.buffer, Rect { x: area.x, y: y - 1, width: area.width, height: 1 }, COLOR_BG5); + fill_ul(to.buffer, Rect { x: area.x, y: y + height - 1, width: area.width, height: 1 }, COLOR_BG5); } if let Some(clip_area) = clip_area { - fill_bg(buf, clip_area, COLOR_BG0); + fill_bg(to.buffer, clip_area, COLOR_BG0); } else if let Some(track_area) = track_area { - fill_bg(buf, track_area, COLOR_BG0); + fill_bg(to.buffer, track_area, COLOR_BG0); } else if let Some(scene_area) = scene_area { - fill_bg(buf, scene_area, COLOR_BG0); + fill_bg(to.buffer, scene_area, COLOR_BG0); } Ok(area) } @@ -148,8 +157,9 @@ impl<'a> Render for CursorFocus<'a> { struct TracksHeader<'a>(u16, &'a[(usize, usize)], &'a [Sequencer]); -impl<'a> Render for TracksHeader<'a> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { +impl<'a> Render, Rect> for TracksHeader<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + let mut area = to.area; let Self(offset, track_cols, tracks) = *self; let Rect { y, width, .. } = area; for (track, (w, x)) in tracks.iter().zip(track_cols) { @@ -158,17 +168,18 @@ impl<'a> Render for TracksHeader<'a> { break } let name = track.name.read().unwrap(); - fill_bg(buf, Rect { x: offset + x, y, width: *w as u16, height: 2 }, COLOR_BG1); - name.blit(buf, offset + x + 1, y, Some(Style::default().white()))?; + fill_bg(to.buffer, Rect { x: offset + x, y, width: *w as u16, height: 2 }, COLOR_BG1); + name.blit(to.buffer, offset + x + 1, y, Some(Style::default().white()))?; } - Ok(Rect { x: area.x, y, width, height: 2 }) + Ok(Some(Rect { x: area.x, y, width, height: 2 })) } } struct SceneRows<'a>(u16, &'a[(usize, usize)], &'a[(usize, usize)], &'a[Sequencer], &'a[Scene]); -impl<'a> Render for SceneRows<'a> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { +impl<'a> Render, Rect> for SceneRows<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + let area = to.area; let Self(offset, track_cols, scene_rows, tracks, scenes) = *self; let black = Some(Style::default().fg(Nord::SEPARATOR)); let Rect { mut y, height, .. } = area; @@ -176,7 +187,7 @@ impl<'a> Render for SceneRows<'a> { let x = *x as u16; if x > 0 { for y in area.y-2..y-2 { - "▎".blit(buf, x - 1, y, black)?; + "▎".blit(to.buffer, x - 1, y, black)?; } } } @@ -185,28 +196,27 @@ impl<'a> Render for SceneRows<'a> { //break //} let h = 1.max((pulses / 96) as u16); - SceneRow(tracks, scene, track_cols, offset).render(buf, Rect { - x: area.x, - y, - width: area.width, - height: h,//.min(area.height - y) + SceneRow(tracks, scene, track_cols, offset).render(&mut TuiOutput { + buffer: to.buffer, + area: Rect { x: area.x, y, width: area.width, height: h, } })?; y = y + h } - Ok(area) + Ok(Some(area)) } } struct SceneRow<'a>(&'a[Sequencer], &'a Scene, &'a[(usize, usize)], u16); -impl<'a> Render for SceneRow<'a> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { +impl<'a> Render, Rect> for SceneRow<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + let mut area = to.area; let Self(tracks, scene, track_cols, offset) = self; let Rect { x, y, width, .. } = area; let playing = scene.is_playing(tracks); - (if playing { "▶" } else { " " }).blit(buf, x, y, None)?; - scene.name.read().unwrap().blit(buf, x + 1, y, Some(Style::default().white()))?; - fill_bg(buf, Rect { x: x, y, width: offset.saturating_sub(1), height: area.height }, COLOR_BG1); + (if playing { "▶" } else { " " }).blit(to.buffer, x, y, None)?; + scene.name.read().unwrap().blit(to.buffer, x + 1, y, Some(Style::default().white()))?; + fill_bg(to.buffer, Rect { x: x, y, width: offset.saturating_sub(1), height: area.height }, COLOR_BG1); for (track, (w, x)) in track_cols.iter().enumerate() { let x = *x as u16 + offset; @@ -217,33 +227,36 @@ impl<'a> Render for SceneRow<'a> { if let (Some(track), Some(Some(clip))) = ( tracks.get(track), scene.clips.get(track) ) { - let area = Rect { x, y, width: *w as u16, height: area.height, }; - SceneClip(track, *clip).render(buf, area)?; + SceneClip(track, *clip).render(&mut TuiOutput { + buffer: to.buffer, + area: Rect { x, y, width: *w as u16, height: area.height, } + })?; } } - Ok(area) + Ok(Some(area)) } } struct SceneClip<'a>(&'a Sequencer, usize); -impl<'a> Render for SceneClip<'a> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { +impl<'a> Render, Rect> for SceneClip<'a> { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + let area = to.area; let Self(track, clip) = self; let style = Some(Style::default().white()); if let Some(phrase) = track.phrases.get(*clip) { let phrase = phrase.read().unwrap(); let name = phrase.name.read().unwrap(); - format!("{clip:02} {name}").blit(buf, area.x + 1, area.y, style)?; - fill_bg(buf, area, if track.sequence == Some(*clip) { + format!("{clip:02} {name}").blit(to.buffer, area.x + 1, area.y, style)?; + fill_bg(to.buffer, area, if track.sequence == Some(*clip) { Nord::PLAYING } else { COLOR_BG1 }); } else { - fill_bg(buf, area, COLOR_BG0) + fill_bg(to.buffer, area, COLOR_BG0) } - Ok(area) + Ok(Some(area)) } } diff --git a/crates/tek_sequencer/src/lib.rs b/crates/tek_sequencer/src/lib.rs index ebd22e68..713829ce 100644 --- a/crates/tek_sequencer/src/lib.rs +++ b/crates/tek_sequencer/src/lib.rs @@ -12,7 +12,6 @@ submod! { arranger_focus arranger_handle arranger_track - arranger_view arranger_rename midi phrase diff --git a/crates/tek_sequencer/src/phrase.rs b/crates/tek_sequencer/src/phrase.rs index 8c70f393..4382756a 100644 --- a/crates/tek_sequencer/src/phrase.rs +++ b/crates/tek_sequencer/src/phrase.rs @@ -146,7 +146,7 @@ impl Phrase { }} } -impl Arranger { +impl Arranger { pub fn phrase (&self) -> Option<&Arc>> { let track_id = self.selected.track()?; self.tracks.get(track_id)?.phrases.get((*self.scene()?.clips.get(track_id)?)?) diff --git a/crates/tek_sequencer/src/scene.rs b/crates/tek_sequencer/src/scene.rs index 556369bf..1edf8266 100644 --- a/crates/tek_sequencer/src/scene.rs +++ b/crates/tek_sequencer/src/scene.rs @@ -79,7 +79,7 @@ pub fn scene_ppqs (tracks: &[Sequencer], scenes: &[Scene]) -> Vec<(usize, usize) scenes } -impl Arranger { +impl Arranger { pub fn scene (&self) -> Option<&Scene> { self.selected.scene().map(|s|self.scenes.get(s)).flatten() } diff --git a/crates/tek_sequencer/src/sequencer_view.rs b/crates/tek_sequencer/src/sequencer_view.rs index aa60e3fb..881cd2f3 100644 --- a/crates/tek_sequencer/src/sequencer_view.rs +++ b/crates/tek_sequencer/src/sequencer_view.rs @@ -1,12 +1,12 @@ use crate::*; impl<'a> Render, Rect> for Sequencer { - fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps { - self.horizontal_draw(target)?; + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + self.horizontal_draw(to)?; if self.focused && self.entered { - Corners(Style::default().green().not_dim()).draw(buf, area)?; + Corners(Style::default().green().not_dim()).draw(to.buffer, to.area)?; } - Ok(area) + Ok(Some(to.area)) } } diff --git a/crates/tek_sequencer/src/sequencer_view_h.rs b/crates/tek_sequencer/src/sequencer_view_h.rs index 3d44671b..d338fd97 100644 --- a/crates/tek_sequencer/src/sequencer_view_h.rs +++ b/crates/tek_sequencer/src/sequencer_view_h.rs @@ -4,17 +4,26 @@ impl Sequencer { const H_KEYS_OFFSET: usize = 5; - pub(crate) fn horizontal_draw (&self, buf: &mut Buffer, mut area: Rect) -> Usually<()> { + pub(crate) fn horizontal_draw <'a> (&self, to: &mut TuiOutput<'a>) -> Usually<()> { + let mut area = to.area; Split::down() .add_ref(&SequenceName(&self)) .add_ref(&SequenceRange) .add_ref(&SequenceLoopRange) .add_ref(&SequenceNoteRange) - .render(buf, Rect { x: area.x, y: area.y, width: 10, height: area.height })?; + .render(&mut TuiOutput { + buffer: to.buffer, + area: Rect { + x: area.x, + y: area.y, + height: area.height, + width: 10, + } + })?; area.x = area.x + 10; area.width = area.width.saturating_sub(10); area.height = area.height.min(66); - Lozenge(Style::default().fg(Nord::BG2)).draw(buf, area)?; + Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, area)?; area.x = area.x + 1; area.width = area.width.saturating_sub(1); Layered::new() @@ -23,7 +32,7 @@ impl Sequencer { .add_ref(&SequenceNotes(&self)) .add_ref(&SequenceCursor(&self)) .add_ref(&SequenceZoom(&self)) - .render(buf, area)?; + .render(&mut TuiOutput { buffer: to.buffer, area: area })?; Ok(()) } @@ -48,102 +57,102 @@ const STYLE_VALUE: Option