From 8d55c737314ec2a5dac40a21d55bb2ed12181135 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 18 Oct 2024 01:54:17 +0300 Subject: [PATCH] further simplify sequencer rendering --- crates/tek_sequencer/src/sequencer_tui.rs | 200 +++++++++------------- 1 file changed, 81 insertions(+), 119 deletions(-) diff --git a/crates/tek_sequencer/src/sequencer_tui.rs b/crates/tek_sequencer/src/sequencer_tui.rs index 588ffba8..f77a13aa 100644 --- a/crates/tek_sequencer/src/sequencer_tui.rs +++ b/crates/tek_sequencer/src/sequencer_tui.rs @@ -14,119 +14,75 @@ impl Content for Sequencer { impl Content for PhrasePool { type Engine = Tui; fn content (&self) -> impl Widget { + let Self { focused, phrases, mode, .. } = self; let content = col!( - (i, phrase) in self.phrases.iter().enumerate() => Layers::new(|add|{ - + (i, phrase) in phrases.iter().enumerate() => Layers::new(|add|{ let Phrase { ref name, color, length, .. } = *phrase.read().unwrap(); - - let row1 = lay!(format!(" {i}").align_w().fill_x(), - if let Some(PhrasePoolMode::Length(phrase, new_length, focus)) = self.mode { - if self.focused && i == phrase { - PhraseLength::new(new_length, Some(focus)) - } else { - PhraseLength::new(length, None) - } - } else { - PhraseLength::new(length, None) - }.align_e().fill_x() - ).fill_x(); - - let row2 = if let Some(PhrasePoolMode::Rename(phrase, _)) = self.mode { - if self.focused && i == phrase { - format!(" {}▄", name) - } else { - format!(" {}", name) + let mut length = PhraseLength::new(length, None); + if let Some(PhrasePoolMode::Length(phrase, new_length, focus)) = mode { + if *focused && i == *phrase { + length.pulses = *new_length; + length.focus = Some(*focus); } - } else { - format!(" {}", name) + } + let length = length.align_e().fill_x(); + let row1 = lay!(format!(" {i}").align_w().fill_x(), length).fill_x(); + let mut row2 = format!(" {name}"); + if let Some(PhrasePoolMode::Rename(phrase, _)) = mode { + if *focused && i == *phrase { row2 = format!("{row2}▄"); } }; - - add(&col!(row1, row2).fill_x().bg(if i == self.phrase { - color //Color::Rgb(40, 50, 30) - } else { - color //Color::Rgb(28, 35, 25) - }))?; - - if self.focused && i == self.phrase { add(&CORNERS)?; } - + let bg = if i == self.phrase { color } else { color }; + add(&col!(row1, row2).fill_x().bg(bg))?; + if *focused && i == self.phrase { add(&CORNERS)?; } Ok(()) }) - ) - .fill_xy() - .bg(Color::Rgb(28, 35, 25)) - .border(Lozenge(Style::default() - .bg(Color::Rgb(40, 50, 30)) - .fg(if self.focused { - Color::Rgb(100, 110, 40) - } else { - Color::Rgb(70, 80, 50) - }))); - lay!(content, TuiStyle::fg("Phrases", if self.focused { - Color::Rgb(150, 160, 90) - } else { - Color::Rgb(120, 130, 100) - }).push_x(1)) + ); + let border_color = if *focused {Color::Rgb(100, 110, 40)} else {Color::Rgb(70, 80, 50)}; + let border = Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color)); + let content = content.fill_xy().bg(Color::Rgb(28, 35, 25)).border(border); + let title_color = if *focused {Color::Rgb(150, 160, 90)} else {Color::Rgb(120, 130, 100)}; + let title = TuiStyle::fg("Phrases", title_color).push_x(1); + lay!(content, title) } } impl Content for PhraseEditor { type Engine = Tui; fn content (&self) -> impl Widget { let Self { focused, entered, time_axis, note_axis, keys, phrase, buffer, .. } = self; - //let field_bg = Color::Rgb(28, 35, 25); - //let toolbar = Stack::down(move|add|{ - ////let name = format!("{:>9}", self.name.read().unwrap().as_str()); - ////add(&col!("Track:", TuiStyle::bg(name.as_str(), field_bg)))?; - //if let Some(phrase) = &self.phrase { - //let phrase = phrase.read().unwrap(); - //let length = format!("{}q{}p", phrase.length / PPQ, phrase.length % PPQ); - //let length = format!("{:>9}", &length); - //let loop_on = format!("{:>9}", if phrase.loop_on { "on" } else { "off" }); - //let loop_start = format!("{:>9}", phrase.loop_start); - //let loop_end = format!("{:>9}", phrase.loop_length); - //add(&"")?; - //add(&col!("Length:", TuiStyle::bg(length.as_str(), field_bg)))?; - //add(&col!("Loop:", TuiStyle::bg(loop_on.as_str(), field_bg)))?; - //add(&col!("L. start:", TuiStyle::bg(loop_start.as_str(), field_bg)))?; - //add(&col!("L. length:", TuiStyle::bg(loop_end.as_str(), field_bg)))?; - //} - //Ok(()) - //}).min_x(10); - let piano_roll = lay!( - // keys - CustomWidget::new(|_|Ok(Some([32u16,4u16])), |to: &mut TuiOutput|{ - if to.area().h() < 2 { return Ok(()) } - Ok(to.buffer_update(to.area().set_w(5), &|cell, x, y|{ + let offset = Self::H_KEYS_OFFSET as u16; + let keys = CustomWidget::new(|_|Ok(Some([32u16,4u16])), move|to: &mut TuiOutput|{ + if to.area().h() >= 2 { + to.buffer_update(to.area().set_w(5), &|cell, x, y|{ let y = y + note_axis.start as u16; if x < keys.area.width && y < keys.area.height { *cell = keys.get(x, y).clone() } - })) - }).fill_y(), - // playhead - CustomWidget::new(|_|Ok(Some([32u16,2u16])), |to: &mut TuiOutput|{ - if let Some(phrase) = phrase { - let time_0 = time_axis.start; - let time_z = time_axis.scale; - let now = 0; // TODO FIXME: self.now % phrase.read().unwrap().length; - let [x, y, width, _] = to.area(); - let x2 = x as usize + Self::H_KEYS_OFFSET; - let x3 = x as usize + width as usize; - for x in x2..x3 { - let step = (time_0 + x2) * time_z; - let next_step = (time_0 + x2 + 1) * time_z; - to.blit(&"-", x as u16, y, Some(PhraseEditor::::style_timer_step( - now, step as usize, next_step as usize - ))); - } + }); + } + Ok(()) + }).fill_y(); + let playhead = CustomWidget::new(|_|Ok(Some([32u16,2u16])), move|to: &mut TuiOutput|{ + if let Some(phrase) = phrase { + let time_0 = time_axis.start; + let time_z = time_axis.scale; + let now = 0; // TODO FIXME: self.now % phrase.read().unwrap().length; + let [x, y, width, _] = to.area(); + let x2 = x as usize + Self::H_KEYS_OFFSET; + let x3 = x as usize + width as usize; + for x in x2..x3 { + let step = (time_0 + x2) * time_z; + let next_step = (time_0 + x2 + 1) * time_z; + to.blit(&"-", x as u16, y, Some(PhraseEditor::::style_timer_step( + now, step as usize, next_step as usize + ))); } - Ok(()) - }).fill_x(), - // notes - CustomWidget::new(|_|Ok(Some([32u16,4u16])), |to: &mut TuiOutput|{ - let offset = Self::H_KEYS_OFFSET as u16; - if to.area().h() < 2 || to.area().w() < offset { return Ok(()) } + } + Ok(()) + }).fill_x(); + let notes = CustomWidget::new(|_|Ok(Some([32u16,4u16])), move|to: &mut TuiOutput|{ + if to.area().h() >= 2 && to.area().w() >= offset { let area = to.area().push_x(offset).shrink_x(offset); - Ok(to.buffer_update(area, &move |cell, x, y|{ + to.buffer_update(area, &move |cell, x, y|{ cell.set_bg(Color::Rgb(20, 20, 20)); let src_x = ((x as usize + time_axis.start) * time_axis.scale) as usize; let src_y = (y as usize + note_axis.start) as usize; @@ -134,40 +90,46 @@ impl Content for PhraseEditor { let src = buffer.get(src_x, buffer.height - src_y); src.map(|src|{ cell.set_symbol(src.symbol()); cell.set_fg(src.fg); }); } - })) - }).fill_x(), - // note cursor - CustomWidget::new(|_|Ok(Some([1u16,1u16])), |to: &mut TuiOutput|{ - if *entered { - let area = to.area(); - if let (Some(time), Some(note)) = (time_axis.point, note_axis.point) { - let x = area.x() + Self::H_KEYS_OFFSET as u16 + time as u16; - let y = area.y() + 1 + note as u16 / 2; - let c = if note % 2 == 0 { "▀" } else { "▄" }; - to.blit(&c, x, y, self.style_focus()); - } + }); + } + Ok(()) + }).fill_x(); + let cursor = CustomWidget::new(|_|Ok(Some([1u16,1u16])), move|to: &mut TuiOutput|{ + if *entered { + let area = to.area(); + if let (Some(time), Some(note)) = (time_axis.point, note_axis.point) { + let x = area.x() + Self::H_KEYS_OFFSET as u16 + time as u16; + let y = area.y() + 1 + note as u16 / 2; + let c = if note % 2 == 0 { "▀" } else { "▄" }; + to.blit(&c, x, y, self.style_focus()); } - Ok(()) - }), - ); - let border_color = if *focused{Color::Rgb(100, 110, 40)}else{Color::Rgb(70, 80, 50)}; - let title_color = if *focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)}; - let border = Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color)); - let content = piano_roll.fill_x().bg(Color::Rgb(40, 50, 30)).border(border); + } + Ok(()) + }); + let border_color = if *focused{Color::Rgb(100, 110, 40)}else{Color::Rgb(70, 80, 50)}; + let title_color = if *focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)}; + let border = Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color)); + let piano_roll = lay!(keys, playhead, notes, cursor).fill_x(); + let content_bg = Color::Rgb(40, 50, 30); + let content = piano_roll.bg(content_bg).border(border); + let mut upper_left = String::from("Sequencer"); let mut lower_left = String::new(); let mut lower_right = format!("Zoom: {}", ppq_to_name(time_axis.scale)); + if let Some(phrase) = phrase { + upper_left = format!("{upper_left}: {}", phrase.read().unwrap().name); + } if *focused { if *entered { - lower_left = "[Esc] Exit edit mode [A]ppend [I]nsert".to_string(); - lower_right = format!("[,.] Length: ?? {}", lower_right); + lower_left = "[Esc] Exit edit mode [A]ppend [I]nsert".to_string(); + lower_right = format!("[,.] Length: ?? {lower_right}"); } else { - lower_left = "[Enter] Edit notes".to_string(); - lower_right = format!("[,.] {}", lower_right); + lower_left = "[Enter] Edit notes".to_string(); + lower_right = format!("[,.] {lower_right}"); } } lay!( content, - TuiStyle::fg("Sequencer", title_color).push_x(1).align_nw().fill_xy(), + TuiStyle::fg(upper_left.to_string(), title_color).push_x(1).align_nw().fill_xy(), TuiStyle::fg(lower_left.to_string(), title_color).push_x(1).align_sw().fill_xy(), TuiStyle::fg(lower_right.to_string(), title_color).pull_x(1).align_se().fill_xy(), )