From 1d4db3c62990232a8c367ff14d671bb7a123cd93 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Wed, 4 Sep 2024 03:20:58 +0300 Subject: [PATCH] wip: render: remove render! macro --- crates/tek_core/src/lib.rs | 4 +- crates/tek_core/src/render_split.rs | 2 +- crates/tek_core/src/render_tui.rs | 36 ++-- crates/tek_mixer/src/mixer_render.rs | 28 +-- crates/tek_mixer/src/plugin.rs | 74 ++++--- crates/tek_mixer/src/sample_add.rs | 66 +++--- crates/tek_mixer/src/sampler.rs | 46 +++-- crates/tek_mixer/src/track_view.rs | 48 ++--- crates/tek_sequencer/src/arranger_rename.rs | 50 ++--- crates/tek_sequencer/src/arranger_view.rs | 49 +++-- crates/tek_sequencer/src/sequencer_view.rs | 14 +- crates/tek_sequencer/src/sequencer_view_h.rs | 20 +- crates/tek_sequencer/src/transport_render.rs | 204 ++++++++++--------- 13 files changed, 337 insertions(+), 304 deletions(-) diff --git a/crates/tek_core/src/lib.rs b/crates/tek_core/src/lib.rs index a02d24e5..c886320c 100644 --- a/crates/tek_core/src/lib.rs +++ b/crates/tek_core/src/lib.rs @@ -106,8 +106,8 @@ pub trait ExitableComponent: Exit + Component { impl, T, U> ExitableComponent for E {} /// Run the main loop. -pub fn run <'a, R> (state: Arc>) -> Usually>> - where R: Render, Rect> + Handle + Sized + 'static +pub fn run (state: Arc>) -> Usually>> + where R: for <'a> Render, 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_split.rs b/crates/tek_core/src/render_split.rs index d1ad21c9..c5cd3268 100644 --- a/crates/tek_core/src/render_split.rs +++ b/crates/tek_core/src/render_split.rs @@ -56,7 +56,7 @@ impl<'a, T, U> Collect<'a, T, U> for Split<'a, T, U> { impl<'a> Render, Rect> for Split<'a, TuiOutput<'a>, Rect> { fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { - Ok(Some(self.render_areas(to)?.0)) + 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 45bfe780..cff261ae 100644 --- a/crates/tek_core/src/render_tui.rs +++ b/crates/tek_core/src/render_tui.rs @@ -1,6 +1,7 @@ use crate::*; pub(crate) use ratatui::buffer::Cell; +use ratatui::backend::Backend; pub struct TuiOutput<'a> { pub buffer: &'a mut Buffer, @@ -8,24 +9,29 @@ pub struct TuiOutput<'a> { } /// Main thread render loop -pub fn tui_render_thread <'a: 'static, T> ( - exited: &Arc, device: &Arc>, -) -> Usually> where - T: Render, Rect> + 'static +pub fn tui_render_thread (exited: &Arc, device: &Arc>) + -> Usually> +where + T: for <'a> Render, Rect> + 'static { - let exited = exited.clone(); - let device = device.clone(); - let mut terminal = ratatui::Terminal::new(CrosstermBackend::new(stdout()))?; - let sleep = Duration::from_millis(20); + let exited = exited.clone(); + let device = device.clone(); + let mut backend = CrosstermBackend::new(stdout()); + let area = backend.size()?; + let mut buffers = [Buffer::empty(area), Buffer::empty(area)]; + let mut index = 0; + let sleep = Duration::from_millis(20); Ok(spawn(move || { - let draw = |frame: &'a mut ratatui::Frame|{ - let area = frame.size(); - let buffer = frame.buffer_mut(); - device.render(&mut TuiOutput { buffer, area }).expect("Failed to render content"); - }; loop { - if let Ok(_) = device.try_read() { - terminal.draw(draw).expect("Failed to render frame"); + if let Ok(device) = device.try_read() { + let mut target = TuiOutput { buffer: &mut buffers[index], area }; + device.render(&mut target).expect("render failed"); + let previous_buffer = &buffers[1 - index]; + let current_buffer = &buffers[index]; + let updates = previous_buffer.diff(current_buffer); + backend.draw(updates.into_iter()); + buffers[1 - index].reset(); + index = 1 - index; } if exited.fetch_and(true, Ordering::Relaxed) { break diff --git a/crates/tek_mixer/src/mixer_render.rs b/crates/tek_mixer/src/mixer_render.rs index d75a9990..aa1b15f8 100644 --- a/crates/tek_mixer/src/mixer_render.rs +++ b/crates/tek_mixer/src/mixer_render.rs @@ -1,17 +1,19 @@ use crate::*; -render!(Mixer |self, buf, area| { - 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 +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) } - Ok(area) -}); +} diff --git a/crates/tek_mixer/src/plugin.rs b/crates/tek_mixer/src/plugin.rs index bc3803a2..d205b703 100644 --- a/crates/tek_mixer/src/plugin.rs +++ b/crates/tek_mixer/src/plugin.rs @@ -9,9 +9,43 @@ pub struct Plugin { pub mapping: bool, pub ports: JackPorts, } -render!(Plugin = render_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; + let mut width = 20u16; + match &state.plugin { + Some(PluginKind::LV2(LV2Plugin { port_list, instance, .. })) => { + let start = state.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 { + if let Some(port) = port_list.get(i) { + let value = if let Some(value) = instance.control_input(port.index) { + value + } else { + port.default_value + }; + //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 { + Some(Style::default().green()) + } else { + None + })?; + } else { + break + } + } + }, + _ => {} + }; + draw_header(state, buf, area.x, area.y, width)?; + Ok(Some(Rect { width, ..area })) + } +} /// Supported plugin formats. pub enum PluginKind { @@ -21,7 +55,6 @@ pub enum PluginKind { }, VST3, } - impl Plugin { pub fn new_lv2 (name: &str, path: &str) -> Usually { let plugin = LV2Plugin::new(path)?; @@ -102,43 +135,6 @@ impl Plugin { Control::Continue } } - -pub fn render_plugin (state: &Plugin, buf: &mut Buffer, area: Rect) - -> Usually -{ - let Rect { x, y, height, .. } = area; - let mut width = 20u16; - match &state.plugin { - Some(PluginKind::LV2(LV2Plugin { port_list, instance, .. })) => { - let start = state.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 { - if let Some(port) = port_list.get(i) { - let value = if let Some(value) = instance.control_input(port.index) { - value - } else { - port.default_value - }; - //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 { - Some(Style::default().green()) - } else { - None - })?; - } else { - break - } - } - }, - _ => {} - }; - draw_header(state, buf, area.x, area.y, width)?; - Ok(Rect { width, ..area }) -} - fn draw_header (state: &Plugin, buf: &mut Buffer, x: u16, y: u16, w: u16) -> Usually { let style = Style::default().gray(); let label1 = format!(" {}", state.name); diff --git a/crates/tek_mixer/src/sample_add.rs b/crates/tek_mixer/src/sample_add.rs index 4574394c..229f0ede 100644 --- a/crates/tek_mixer/src/sample_add.rs +++ b/crates/tek_mixer/src/sample_add.rs @@ -22,40 +22,42 @@ pub struct AddSampleModal { exit!(AddSampleModal); -render!(AddSampleModal |self,buf,area|{ - make_dim(buf); - let area = center_box( - area, - 64.max(area.width.saturating_sub(8)), - 20.max(area.width.saturating_sub(8)), - ); - fill_fg(buf, area, Color::Reset); - fill_bg(buf, area, Nord::bg_lo(true, true)); - fill_char(buf, area, ' '); - format!("{}", &self.dir.to_string_lossy()) - .blit(buf, area.x+2, area.y+1, Some(Style::default().bold()))?; - "Select sample:" - .blit(buf, 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))) - .enumerate() - .skip(self.offset) - { - if i >= area.height as usize - 4 { - break +impl<'a> Render, Rect> for AddSampleModal { + fn render (&self, to: &mut TuiOutput<'a>) -> Usually { + 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, ' '); + format!("{}", &self.dir.to_string_lossy()) + .blit(buf, area.x+2, area.y+1, Some(Style::default().bold()))?; + "Select sample:" + .blit(buf, 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))) + .enumerate() + .skip(self.offset) + { + if i >= area.height as usize - 4 { + break + } + 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 { + Style::default().green() + } else { + Style::default().white() + }))?; } - 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 { - Style::default().green() - } else { - Style::default().white() - }))?; + Lozenge(Style::default()).draw(buf, area) } - Lozenge(Style::default()).draw(buf, area) -}); +} handle!(AddSampleModal |self,e|{ if handle_keymap(self, e, KEYMAP_ADD_SAMPLE)? { diff --git a/crates/tek_mixer/src/sampler.rs b/crates/tek_mixer/src/sampler.rs index 73a7dd64..38e95a8f 100644 --- a/crates/tek_mixer/src/sampler.rs +++ b/crates/tek_mixer/src/sampler.rs @@ -15,30 +15,34 @@ pub struct Sampler { } process!(Sampler = Sampler::process); + handle!(Sampler |self, event| handle_keymap(self, event, KEYMAP_SAMPLER)); -render!(Sampler |self, buf, area| { - let Rect { x, y, height, .. } = 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()))?; - let mut width = title.len() + 2; - let mut y1 = 1; - let mut j = 0; - for (note, sample) in self.mapped.iter() - .map(|(note, sample)|(Some(note), sample)) - .chain(self.unmapped.iter().map(|sample|(None, sample))) - { - if y1 >= height { - break + +impl<'a> Render, Rect> for Sampler { + fn render (&self, to: &mut TuiOutput<'a>) -> Usually { + let Rect { x, y, height, .. } = 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()))?; + let mut width = title.len() + 2; + let mut y1 = 1; + let mut j = 0; + for (note, sample) in self.mapped.iter() + .map(|(note, sample)|(Some(note), sample)) + .chain(self.unmapped.iter().map(|sample|(None, sample))) + { + if y1 >= height { + break + } + let active = j == self.cursor.0; + width = width.max(draw_sample(buf, x, y + y1, note, &*sample.read().unwrap(), active)?); + y1 = y1 + 1; + j = j + 1; } - let active = j == self.cursor.0; - width = width.max(draw_sample(buf, 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 }) } - let height = ((2 + y1) as u16).min(height); - Ok(Rect { x, y, width: (width as u16).min(area.width), height }) -}); +} fn draw_sample ( buf: &mut Buffer, x: u16, y: u16, note: Option<&u7>, sample: &Sample, focus: bool diff --git a/crates/tek_mixer/src/track_view.rs b/crates/tek_mixer/src/track_view.rs index dc944dbe..0098a12a 100644 --- a/crates/tek_mixer/src/track_view.rs +++ b/crates/tek_mixer/src/track_view.rs @@ -1,35 +1,37 @@ use crate::*; use tek_core::Direction; -render!(Track |self, buf, area| TrackView { - chain: Some(&self), - direction: tek_core::Direction::Right, - focused: true, - entered: true, - //pub channels: u8, - //pub input_ports: Vec>, - //pub pre_gain_meter: f64, - //pub gain: f64, - //pub insert_ports: Vec>, - //pub return_ports: Vec>, - //pub post_gain_meter: f64, - //pub post_insert_meter: f64, - //pub level: f64, - //pub pan: f64, - //pub output_ports: Vec>, - //pub post_fader_meter: f64, - //pub route: String, -}.render(buf, area)); - +impl<'a> Render, Rect> for Track { + fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps { + TrackView { + chain: Some(&self), + direction: tek_core::Direction::Right, + focused: true, + entered: true, + //pub channels: u8, + //pub input_ports: Vec>, + //pub pre_gain_meter: f64, + //pub gain: f64, + //pub insert_ports: Vec>, + //pub return_ports: Vec>, + //pub post_gain_meter: f64, + //pub post_insert_meter: f64, + //pub level: f64, + //pub pan: f64, + //pub output_ports: Vec>, + //pub post_fader_meter: f64, + //pub route: String, + }.render(to) + } +} pub struct TrackView<'a> { pub chain: Option<&'a Track>, pub direction: Direction, pub focused: bool, pub entered: bool, } - -impl<'a> Render for TrackView<'a> { - fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually { +impl<'a> Render, Rect> for TrackView<'a> { + 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), diff --git a/crates/tek_sequencer/src/arranger_rename.rs b/crates/tek_sequencer/src/arranger_rename.rs index 3075407f..104298ff 100644 --- a/crates/tek_sequencer/src/arranger_rename.rs +++ b/crates/tek_sequencer/src/arranger_rename.rs @@ -35,30 +35,32 @@ impl ArrangerRenameModal { } } -render!(ArrangerRenameModal |self, buf, area| { - let y = area.y + area.height / 2; - let bg_area = Rect { - x: 1, - y: y - 1, - width: area.width - 2, - height: 3 - }; - fill_bg(buf, bg_area, Nord::BG0); - Lozenge(Style::default().bold().white().dim()).draw(buf, bg_area)?; - let label = match self.target { - ArrangerFocus::Mix => "Rename project:", - ArrangerFocus::Track(_) => "Rename track:", - ArrangerFocus::Scene(_) => "Rename scene:", - ArrangerFocus::Clip(_, _) => "Rename clip:", - }; - let style = Some(Style::default().not_bold().white().not_dim()); - label.blit(buf, 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)?; - 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) -}); +impl<'a> Render, Rect> for ArrangerRenameModal { + fn render (&self, to: &mut TuiOutput<'a>) -> Usually { + let y = area.y + area.height / 2; + let bg_area = Rect { + x: 1, + y: y - 1, + width: area.width - 2, + height: 3 + }; + fill_bg(buf, bg_area, Nord::BG0); + Lozenge(Style::default().bold().white().dim()).draw(buf, bg_area)?; + let label = match self.target { + ArrangerFocus::Mix => "Rename project:", + ArrangerFocus::Track(_) => "Rename track:", + ArrangerFocus::Scene(_) => "Rename scene:", + ArrangerFocus::Clip(_, _) => "Rename clip:", + }; + let style = Some(Style::default().not_bold().white().not_dim()); + label.blit(buf, 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)?; + 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) + } +} handle!(ArrangerRenameModal |self, e| { match e { diff --git a/crates/tek_sequencer/src/arranger_view.rs b/crates/tek_sequencer/src/arranger_view.rs index ef09f99f..97b0a34e 100644 --- a/crates/tek_sequencer/src/arranger_view.rs +++ b/crates/tek_sequencer/src/arranger_view.rs @@ -19,25 +19,30 @@ impl ArrangerViewMode { } } -render!(Arranger |self, buf, area| { - let area = Rect { - x: area.x + 1, width: area.width - 2, y: area.y + 1, height: area.height - 2 - }; - let area = match self.mode { - ArrangerViewMode::Horizontal => - super::arranger_view_h::draw(self, buf, area), - ArrangerViewMode::VerticalCompact1 => - super::arranger_view_v::draw_compact_1(self, buf, area), - ArrangerViewMode::VerticalCompact2 => - super::arranger_view_v::draw_compact_2(self, buf, area), - ArrangerViewMode::VerticalExpanded => - super::arranger_view_v::draw_expanded(self, buf, 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(buf, area) -}); +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/sequencer_view.rs b/crates/tek_sequencer/src/sequencer_view.rs index 28c77725..aa60e3fb 100644 --- a/crates/tek_sequencer/src/sequencer_view.rs +++ b/crates/tek_sequencer/src/sequencer_view.rs @@ -1,12 +1,14 @@ use crate::*; -render!(Sequencer |self, buf, area| { - self.horizontal_draw(buf, area)?; - if self.focused && self.entered { - Corners(Style::default().green().not_dim()).draw(buf, area)?; +impl<'a> Render, Rect> for Sequencer { + fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps { + self.horizontal_draw(target)?; + if self.focused && self.entered { + Corners(Style::default().green().not_dim()).draw(buf, area)?; + } + Ok(area) } - Ok(area) -}); +} impl Sequencer { /// Select which pattern to display. This pre-renders it to the buffer at full resolution. diff --git a/crates/tek_sequencer/src/sequencer_view_h.rs b/crates/tek_sequencer/src/sequencer_view_h.rs index dcf16707..3d44671b 100644 --- a/crates/tek_sequencer/src/sequencer_view_h.rs +++ b/crates/tek_sequencer/src/sequencer_view_h.rs @@ -47,7 +47,7 @@ const STYLE_VALUE: Option