diff --git a/src/device.rs b/src/device.rs index af5b48c9..26582d9a 100644 --- a/src/device.rs +++ b/src/device.rs @@ -20,6 +20,44 @@ pub use self::launcher::Launcher; use crossterm::event; +pub trait Handle { + // Returns Ok(true) if the device handled the event. + // This is the mechanism which allows nesting of components;. + fn handle (&mut self, _e: &AppEvent) -> Usually { + Ok(false) + } +} + +pub trait Render { + // Returns space used by component. + // This is insufficient but for the most basic dynamic layout algorithms. + fn render (&self, _b: &mut Buffer, _a: Rect) -> Usually { + Ok(Rect { x: 0, y: 0, width: 0, height: 0 }) + } +} + +impl Render for Box { + fn render (&self, b: &mut Buffer, a: Rect) -> Usually { + (**self).render(b, a) + } +} + +pub trait Device: Render + Handle + Send + Sync {} + +impl Device for T {} + +impl WidgetRef for &dyn Render { + fn render_ref (&self, area: Rect, buf: &mut Buffer) { + Render::render(*self, buf, area).expect("Failed to render device."); + } +} + +impl WidgetRef for dyn Render { + fn render_ref (&self, area: Rect, buf: &mut Buffer) { + Render::render(self, buf, area).expect("Failed to render device."); + } +} + pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box> { let device = Arc::new(Mutex::new(device)); let exited = Arc::new(AtomicBool::new(false)); @@ -89,19 +127,6 @@ pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box Usually { - Ok(false) - } - // Returns space used by component. - // This is insufficient but for the most basic dynamic layout algorithms. - fn render (&self, _buffer: &mut Buffer, _area: Rect) -> Usually { - Ok(Rect { x: 0, y: 0, width: 0, height: 0 }) - } -} - pub struct DynamicDevice { pub state: Arc>, pub render: MutexUsually + Send>>, @@ -110,10 +135,13 @@ pub struct DynamicDevice { client: Option } -impl Device for DynamicDevice { +impl Handle for DynamicDevice { fn handle (&mut self, event: &AppEvent) -> Usually { self.handle.lock().unwrap()(&mut *self.state.lock().unwrap(), event) } +} + +impl Render for DynamicDevice { fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { self.render.lock().unwrap()(&*self.state.lock().unwrap(), buf, area) } @@ -162,18 +190,6 @@ impl DynamicDevice { } } -impl WidgetRef for &dyn Device { - fn render_ref (&self, area: Rect, buf: &mut Buffer) { - Device::render(*self, buf, area).expect("Failed to render device."); - } -} - -impl WidgetRef for dyn Device { - fn render_ref (&self, area: Rect, buf: &mut Buffer) { - Device::render(self, buf, area).expect("Failed to render device."); - } -} - #[derive(Debug)] pub enum AppEvent { /// Terminal input diff --git a/src/device/chain.rs b/src/device/chain.rs index 22eca5ac..5c28d125 100644 --- a/src/device/chain.rs +++ b/src/device/chain.rs @@ -35,29 +35,25 @@ pub fn render (state: &Chain, buf: &mut Buffer, area: Rect) -> Usually { let Rect { x, y, .. } = area; + let selected = Some(if state.focused { + Style::default().green().not_dim() + } else { + Style::default().green().dim() + }); Ok(match state.view { ChainView::Hidden => Rect { x, y, width: 0, height: 0 }, ChainView::Compact => { let area = Rect { x, y, width: (state.name.len() + 4) as u16, height: 3 }; buf.set_string(area.x + 2, area.y + 1, &state.name, Style::default()); - draw_box_styled(buf, area, Some(Style::default().dim())) + draw_box_styled(buf, area, selected) }, ChainView::Row => { - let (area, areas) = draw_row(&state.items, buf, area, 0)?; - draw_box_styled(buf, area, Some(Style::default().dim())) + let (area, areas) = Row::draw(buf, area, &state.items, 0)?; + draw_box_styled(buf, area, selected) }, ChainView::Column => { - let (area, areas) = draw_column(&state.items, buf, area, 0)?; - //draw_box_styled(buf, area, Some(if state.focused { - //Style::default().dim() - //} else { - //Style::default().not_dim() - //})); - draw_box_styled(buf, areas[state.focus], Some(if state.focused { - Style::default().green().not_dim() - } else { - Style::default().green().dim() - })); + let (area, areas) = Column::draw(buf, area, &state.items, 0)?; + draw_box_styled(buf, areas[state.focus], selected); area }, }) diff --git a/src/device/sequencer.rs b/src/device/sequencer.rs index 32a90f93..c1e956ab 100644 --- a/src/device/sequencer.rs +++ b/src/device/sequencer.rs @@ -106,6 +106,22 @@ impl Sequencer { } } +impl Ports for Sequencer { + fn audio_ins (&self) -> Usually> { + Ok(vec![]) + } + fn audio_outs (&self) -> Usually> { + Ok(vec![]) + } + fn midi_ins (&self) -> Usually> { + Ok(vec![self.input_port.short_name()?]) + } + fn midi_outs (&self) -> Usually> { + Ok(vec![self.output_port.short_name()?]) + } + fn connect (&mut self, connect: bool, source: &str, target: &str) {} +} + fn process_in (s: &mut Sequencer, scope: &ProcessScope, transport: &::jack::TransportStatePosition) { if !s.recording { return @@ -227,11 +243,11 @@ fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usu let style = Style::default().gray(); let timer = format!("{rep}.{step:02} / {reps}.{steps}"); buf.set_string(x + width - 2 - timer.len() as u16, y + 1, &timer, style.bold().not_dim()); - buf.set_string(x + 2, y + 1, &format!("⏹ STOP"), if s.playing { - style.dim().bold() + if s.playing { + buf.set_string(x + 2, y + 1, &format!("▶ PLAYING"), style.not_dim().white().bold()); } else { - style.not_dim().white().bold() - }); + buf.set_string(x + 2, y + 1, &format!("⏹ STOPPED"), style.dim().bold()); + } buf.set_string(x, y + 2, format!("├{}┤", "-".repeat((area.width - 2).into())), style.dim()); //buf.set_string(x + 2, y + 2, //&format!("▶ PLAY"), if s.playing { @@ -239,19 +255,19 @@ fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usu //} else { //Style::default().dim() //}); - buf.set_string(x + 10, y + 1, + buf.set_string(x + 13, y + 1, &format!("⏺ REC"), if s.recording { Style::default().bold().red() } else { Style::default().bold().dim() }); - buf.set_string(x + 17, y + 1, + buf.set_string(x + 20, y + 1, &format!("⏺ DUB"), if s.overdub { Style::default().bold().yellow() } else { Style::default().bold().dim() }); - buf.set_string(x + 24, y + 1, + buf.set_string(x + 27, y + 1, &format!("⏺ MON"), if s.monitoring { Style::default().bold().green() } else { diff --git a/src/device/sequencer/horizontal.rs b/src/device/sequencer/horizontal.rs index e1f6fe1b..9b1671e8 100644 --- a/src/device/sequencer/horizontal.rs +++ b/src/device/sequencer/horizontal.rs @@ -7,17 +7,26 @@ pub fn draw_horizontal ( mut area: Rect, beat: usize ) -> Usually { - let ppq = s.timebase.ppq() as u32; area.x = area.x + 13; - - draw_keys_horizontal(s, buf, area)?; - let Rect { x, y, width, .. } = area; - let (time0, time1) = s.time_axis; - let (note0, note1) = s.note_axis; + keys(s, buf, area)?; + timer(s, buf, x, y, beat); + let height = 32.max(s.note_axis.1 - s.note_axis.0) / 2; + lanes(s, buf, x, y, width); + cursor(s, buf, x, y); + footer(s, buf, x, y, width, height); + Ok(Rect { + x: x - 13, + y: y, + width: s.time_axis.1 - s.time_axis.0 + 19, + height: height + 3 + }) +} + +fn timer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, beat: usize) { let bw = Style::default().dim(); let bg = Style::default().on_black(); - + let (time0, time1) = s.time_axis; for step in time0..time1 { buf.set_string(x + 6 + step, y - 1, &"-", if beat % s.steps == step as usize { Style::default().yellow().bold().not_dim() @@ -25,6 +34,13 @@ pub fn draw_horizontal ( Style::default() }); } +} + +fn lanes (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, width: u16) { + let bw = Style::default().dim(); + let bg = Style::default().on_black(); + let ppq = s.timebase.ppq() as u32; + let (time0, time1) = s.time_axis; for step in time0..time1 { let time_start = step as u32 * ppq; let time_end = (step + 1) as u32 * ppq; @@ -33,12 +49,16 @@ pub fn draw_horizontal ( } for (_, (_, events)) in s.sequences[s.sequence].range(time_start..time_end).enumerate() { if events.len() > 0 { - buf.set_string(x + 5 + step as u16, y, "█", bw); + buf.set_string(x + 6 + step as u16, y, "█", bw); } } } +} - let height = 32.max(note1 - note0) / 2; +fn footer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, width: u16, height: u16) { + let bw = Style::default().dim(); + let bg = Style::default().on_black(); + let (note0, note1) = s.note_axis; buf.set_string(x - 13, y + height, format!("├{}┤", "-".repeat((width - 2).into())), Style::default().dim()); { @@ -65,26 +85,25 @@ pub fn draw_horizontal ( x = x + 2; } } +} + +fn cursor (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) { + let bw = Style::default().dim(); + let bg = Style::default().on_black(); buf.set_string( x + 6 + s.time_cursor, y + s.note_cursor / 2, if s.note_cursor % 2 == 0 { "▀" } else { "▄" }, Style::default() ); - Ok(Rect { - x: x - 13, - y, - width: time1 - time0 + 19, - height: height + 3 - }) } -pub fn draw_keys_horizontal (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually { +fn keys (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually { + let bw = Style::default().dim(); + let bg = Style::default().on_black(); let Rect { x, y, .. } = area; let (note0, note1) = s.note_axis; let (time0, time1) = s.time_axis; - let bw = Style::default().dim(); - let bg = Style::default().on_black(); for i in 0..32.max(note1-note0)/2 { let y = y + i; buf.set_string(x + 2, y, KEYS_VERTICAL[(i % 6) as usize], bw); diff --git a/src/device/sequencer/vertical.rs b/src/device/sequencer/vertical.rs index acd50460..86c4df9c 100644 --- a/src/device/sequencer/vertical.rs +++ b/src/device/sequencer/vertical.rs @@ -7,17 +7,23 @@ pub fn draw_vertical ( mut area: Rect, beat: usize ) -> Usually { - let ppq = s.timebase.ppq() as u32; area.x = area.x + 13; + let Rect { x, y, .. } = area; + keys(s, buf, area, beat); + steps(s, buf, area, beat); + let height = (s.time_axis.1-s.time_axis.0)/2; + footer(s, buf, x, y, height); + playhead(s, buf, x, y); + Ok(Rect { x, y, width: area.width, height: height + 1 }) +} - draw_keys_vertical(s, buf, area, beat); - +fn steps (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) { + let ppq = s.timebase.ppq() as u32; + let bw = Style::default().dim().on_black(); + let bg = Style::default().on_black(); let Rect { x, y, .. } = area; let (time0, time1) = s.time_axis; let (note0, note1) = s.note_axis; - let bw = Style::default().dim().on_black(); - let bg = Style::default().on_black(); - for step in time0..time1 { let y = y - time0 + step / 2; let step = step as usize; @@ -56,8 +62,9 @@ pub fn draw_vertical ( } } } +} - let height = (time1-time0)/2; +fn footer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, height: u16) { buf.set_string(x + 2, y + height + 1, format!( "Q 1/{} | N {} ({}-{}) | T {} ({}-{})", 4 * s.resolution, @@ -68,16 +75,18 @@ pub fn draw_vertical ( s.time_axis.0 + 1, s.time_axis.1, ), Style::default().dim()); +} + +fn playhead (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) { buf.set_string( x + 5 + s.note_cursor, y + s.time_cursor / 2, if s.time_cursor % 2 == 0 { "▀" } else { "▄" }, Style::default() ); - Ok(Rect { x, y, width: area.width, height: height + 1 }) } -fn draw_keys_vertical (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize) { +fn keys (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize) { let ppq = s.timebase.ppq() as u32; let Rect { x, y, .. } = area; let (note0, note1) = s.note_axis; diff --git a/src/device/transport.rs b/src/device/transport.rs index 017ba08d..42d6afaa 100644 --- a/src/device/transport.rs +++ b/src/device/transport.rs @@ -15,15 +15,9 @@ impl Transport { DynamicDevice::new(render, handle, process, Self { name: name.into(), timebase: Arc::new(Timebase { - rate: AtomicUsize::new( - transport.query()?.pos.frame_rate().map(|x|x as usize).unwrap_or(0) - ), - tempo: AtomicUsize::new( - 113000 - ), - ppq: AtomicUsize::new( - 96 - ), + rate: AtomicUsize::new(client.sample_rate()), + tempo: AtomicUsize::new(113000), + ppq: AtomicUsize::new(96), }), transport }).activate(client) diff --git a/src/layout/container.rs b/src/layout/container.rs index a4a49026..2e9e727f 100644 --- a/src/layout/container.rs +++ b/src/layout/container.rs @@ -1,111 +1,62 @@ use crate::prelude::*; - -pub fn draw_box (buffer: &mut Buffer, area: Rect) -> Rect { - if area.width < 1 || area.height < 1 { - return area - } - let border = Style::default().gray().dim(); - let top = format!("╭{}╮", "─".repeat((area.width - 2).into())); - let bottom = format!("╰{}╯", "─".repeat((area.width - 2).into())); - buffer.set_string(area.x, area.y, top, border); - for y in (area.y + 1)..(area.y + area.height - 1) { - buffer.set_string(area.x, y, format!("│"), border); - buffer.set_string(area.x + area.width - 1, y, format!("│"), border); - } - buffer.set_string(area.x, area.y + area.height - 1, bottom, border); - area -} - -pub fn draw_box_styled (buffer: &mut Buffer, area: Rect, style: Option