diff --git a/crates/tek_tui/src/tui_view_phrase_editor.rs b/crates/tek_tui/src/tui_view_phrase_editor.rs index 187ffff7..b248a930 100644 --- a/crates/tek_tui/src/tui_view_phrase_editor.rs +++ b/crates/tek_tui/src/tui_view_phrase_editor.rs @@ -34,7 +34,7 @@ impl<'a, T: HasEditor> From<&'a T> for PhraseView<'a> { editor.note_lo.store(note_lo, Ordering::Relaxed); } - let mut note_hi = 127.min(note_lo + height * 2 + 1); + let mut note_hi = 127.min(note_lo + height + 1); if note_point > note_hi { note_lo += note_point - note_hi; note_hi = note_point; @@ -84,34 +84,43 @@ impl<'a> Content for PhraseView<'a> { now, .. } = self; - 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 playhead_inactive = Style::default().fg(Color::Rgb(255,255,255)).bg(Color::Rgb(40,50,30)); - let playhead_active = playhead_inactive.clone().yellow().bold().not_dim(); - let mut name = "".to_string(); - if let Some(phrase) = phrase { - name = phrase.read().unwrap().name.clone(); - } - let upper_left = format!("{note_hi} {note_hi_name} {name}"); - let lower_left = format!("{note_lo} {note_lo_name}"); - let mut lower_right = format!("┤{}├", size.format()); + + let upper_left = format!( + "{note_hi} {note_hi_name} {}", + phrase.as_ref().map(|p|p.read().unwrap().name.clone()).unwrap_or(String::new()) + ); + + let lower_left = format!( + "{note_lo} {note_lo_name}" + + ); + + let mut lower_right = format!( + "┤{}├", size.format() + ); if *focused && *entered { lower_right = format!("┤Note: {} ({}) {}├─{lower_right}", note_point, to_note_name(*note_point), pulses_to_name(*note_len) ); } - let mut upper_right = format!("[{}]", if *entered {"■"} else {" "}); + + let mut upper_right = format!( + "[{}]", + if *entered {"■"} else {" "} + ); if let Some(phrase) = phrase { upper_right = format!("┤Time: {}/{} {}├{upper_right}", time_point, phrase.read().unwrap().length, pulses_to_name(*time_scale), ) }; + + let title_color = if *focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)}; + lay!( row!( CustomWidget::new(|to:[u16;2]|Ok(Some(to.clip_w(2))), move|to: &mut TuiOutput|{ Ok(if to.area().h() >= 2 { - view_mode.blit_keys(to, *note_hi) + view_mode.blit_keys(to, *note_hi, *note_lo) }) }).fill_y(), lay!( @@ -128,14 +137,20 @@ impl<'a> Content for PhraseView<'a> { Ok(if *focused && *entered { view_mode.blit_cursor( to, - *time_point, *time_scale, - *note_len, *note_hi, *note_lo, *note_point + *time_point, *time_start, *time_scale, + *note_point, *note_len, *note_hi, *note_lo, ) }) }) ).fill_x() - ).fill_x().bg(Color::Rgb(40, 50, 30)).border(border), + ).fill_x().bg(Color::Rgb(40, 50, 30)).border(Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(if *focused{ + Color::Rgb(100, 110, 40) + } else { + Color::Rgb(70, 80, 50) + }))), CustomWidget::new(|to:[u16;2]|Ok(Some(to.clip_h(1))), move|to: &mut TuiOutput|{ + let playhead_inactive = Style::default().fg(Color::Rgb(255,255,255)).bg(Color::Rgb(40,50,30)); + let playhead_active = playhead_inactive.clone().yellow().bold().not_dim(); if let Some(_) = phrase { let now = now.get() as usize; // TODO FIXME: self.now % phrase.read().unwrap().length; let time_clamp = time_clamp; @@ -226,59 +241,61 @@ impl PhraseViewMode { _ => unimplemented!() } } - fn blit_keys (&self, to: &mut TuiOutput, note_hi: usize) { - for y in to.area.y()..to.area.y2() { - let n = y - to.area.y(); - let m = note_hi.saturating_sub(n as usize*2); - //let c = format!("{m:>03} {n}"); - //to.blit(&c, to.area.x(), y, None); - let x = to.area.x(); - let s = Some(Style::default().fg(Color::Rgb(255,255,255)).bg(Color::Rgb(0,0,0))); - match m % 2 { - 1 => match (m / 2) % 6 { - 5 => to.blit(&"▀", x, y, s), - 4 => to.blit(&"▀", x, y, s), - 3 => to.blit(&"▀", x, y, s), - 2 => to.blit(&"█", x, y, s), - 1 => to.blit(&"▄", x, y, s), - 0 => to.blit(&"▄", x, y, s), - _ => unreachable!(), - }, - 0 => match (m / 2) % 6 { - 5 => to.blit(&"▄", x, y, s), - 4 => to.blit(&"▄", x, y, s), - 3 => to.blit(&"▄", x, y, s), - 2 => to.blit(&"▀", x, y, s), - 1 => to.blit(&"▀", x, y, s), - 0 => to.blit(&"█", x, y, s), - _ => unreachable!(), - }, - _ => unreachable!() - } + fn blit_keys (&self, to: &mut TuiOutput, note_hi: usize, note_lo: usize) { + let style = Some(Style::default().fg(Color::White).bg(Color::Rgb(0, 0, 0))); + match self { + Self::PianoHorizontal { .. } => { + let [x0, y0, _, h] = to.area().xywh(); + for (y, note) in (note_lo..note_hi).rev().enumerate() { + to.blit(&match note % 12 { + 11 => "██", + 10 => " ", + 9 => "██", + 8 => " ", + 7 => "██", + 6 => " ", + 5 => "██", + 4 => "██", + 3 => " ", + 2 => "██", + 1 => " ", + 0 => "██", + _ => unreachable!(), + }, x0, y0 + y as u16, style) + } + }, + _ => unimplemented!() } } fn blit_cursor ( &self, to: &mut TuiOutput, time_point: usize, + time_start: usize, time_scale: usize, + note_point: usize, note_len: usize, note_hi: usize, note_lo: usize, - note_point: usize, ) { - let area = to.area(); - let x1 = area.x() + (time_point / time_scale) as u16; - let x2 = x1 + (note_len / time_scale) as u16; - let y = area.y() + (note_hi - note_point) as u16 / 2; - let c = if note_lo % 2 == 0 { - if note_point % 2 == 0 { "▄" } else { "▀" } - } else { - if note_point % 2 == 0 { "▀" } else { "▄" } - }; - let style = Some(Style::default().fg(Color::Rgb(0,255,0))); - for x in x1..x2 { - to.blit(&c, x, y, style); + match self { + Self::PianoHorizontal { .. } => { + let [x0, y0, w, h] = to.area().xywh(); + for (y, note) in (note_lo..note_hi).rev().enumerate() { + if note == note_point { + for x in 0..w { + let time_1 = time_start + x as usize * time_scale; + let time_2 = time_1 + time_scale; + if time_1 <= time_point && time_point < time_2 { + to.blit(&"█", x0 + x as u16, y0 + y as u16, None); + break + } + } + break + } + } + }, + _ => unimplemented!() } } /// Determine the required width to render the phrase.