diff --git a/crates/tek_tui/src/tui_control_phrase_editor.rs b/crates/tek_tui/src/tui_control_phrase_editor.rs index 3a64a621..447be2f5 100644 --- a/crates/tek_tui/src/tui_control_phrase_editor.rs +++ b/crates/tek_tui/src/tui_control_phrase_editor.rs @@ -24,15 +24,15 @@ impl InputToCommand for PhraseCommand { let note_point = state.note_point.load(Ordering::Relaxed); let time_start = state.time_start.load(Ordering::Relaxed); let time_point = state.time_point.load(Ordering::Relaxed); - let time_scale = state.time_scale.load(Ordering::Relaxed); + let time_zoom = state.view_mode.time_zoom(); let length = state.phrase.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1); Some(match from.event() { key!(Char('`')) => ToggleDirection, key!(Esc) => SetEditMode(PhraseEditMode::Scroll), - key!(Char('-')) => SetTimeZoom(next_note_length(time_scale)), - key!(Char('_')) => SetTimeZoom(next_note_length(time_scale)), - key!(Char('=')) => SetTimeZoom(prev_note_length(time_scale)), - key!(Char('+')) => SetTimeZoom(prev_note_length(time_scale)), + key!(Char('-')) => SetTimeZoom(next_note_length(time_zoom)), + key!(Char('_')) => SetTimeZoom(next_note_length(time_zoom)), + key!(Char('=')) => SetTimeZoom(prev_note_length(time_zoom)), + key!(Char('+')) => SetTimeZoom(prev_note_length(time_zoom)), key!(Char('a')) => AppendNote, key!(Char('s')) => PutNote, key!(Char('[')) => SetNoteLength(prev_note_length(state.note_len)), @@ -55,8 +55,8 @@ impl InputToCommand for PhraseCommand { key!(Down) => SetNoteCursor(note_point.saturating_sub(1)), key!(PageUp) => SetNoteCursor(note_point + 3), key!(PageDown) => SetNoteCursor(note_point.saturating_sub(3)), - key!(Left) => SetTimeCursor(time_point.saturating_sub(time_scale)), - key!(Right) => SetTimeCursor((time_point + time_scale) % length), + key!(Left) => SetTimeCursor(time_point.saturating_sub(time_zoom)), + key!(Right) => SetTimeCursor((time_point + time_zoom) % length), _ => return None }, } @@ -98,7 +98,8 @@ impl Command for PhraseCommand { None }, SetTimeZoom(zoom) => { - state.time_scale.store(zoom, Ordering::Relaxed); + state.view_mode.set_time_zoom(zoom); + state.show_phrase(state.phrase.clone()); None }, SetNoteScroll(note) => { diff --git a/crates/tek_tui/src/tui_model_phrase_editor.rs b/crates/tek_tui/src/tui_model_phrase_editor.rs index 8c1387ad..214ec26a 100644 --- a/crates/tek_tui/src/tui_model_phrase_editor.rs +++ b/crates/tek_tui/src/tui_model_phrase_editor.rs @@ -22,7 +22,6 @@ pub struct PhraseEditorModel { pub(crate) time_start: AtomicUsize, pub(crate) time_point: AtomicUsize, - pub(crate) time_clamp: AtomicUsize, pub(crate) time_scale: AtomicUsize, pub(crate) edit_mode: PhraseEditMode, @@ -36,10 +35,9 @@ impl std::fmt::Debug for PhraseEditorModel { self.note_lo.load(Ordering::Relaxed), self.note_point.load(Ordering::Relaxed), )) - .field("time_axis", &format!("{} {} {} {}", + .field("time_axis", &format!("{} {} {}", self.time_start.load(Ordering::Relaxed), self.time_point.load(Ordering::Relaxed), - self.time_clamp.load(Ordering::Relaxed), self.time_scale.load(Ordering::Relaxed), )) .finish() @@ -61,7 +59,6 @@ impl Default for PhraseEditorModel { note_point: 24.into(), time_start: 0.into(), time_point: 0.into(), - time_clamp: 0.into(), time_scale: 24.into(), view_mode: PhraseViewMode::PianoHorizontal { time_zoom: 24, @@ -96,14 +93,12 @@ impl PhraseEditorModel { } /// Select which pattern to display. This pre-renders it to the buffer at full resolution. pub fn show_phrase (&mut self, phrase: Option>>) { - if let Some(phrase) = phrase { - self.phrase = Some(phrase.clone()); - self.time_clamp.store(phrase.read().unwrap().length, Ordering::Relaxed); - self.buffer = self.view_mode.draw(&*phrase.read().unwrap()); + if phrase.is_some() { + self.buffer = self.view_mode.draw(&*phrase.as_ref().unwrap().read().unwrap()); + self.phrase = phrase; } else { - self.phrase = None; - self.time_clamp.store(0, Ordering::Relaxed); self.buffer = Default::default(); + self.phrase = None; } } } diff --git a/crates/tek_tui/src/tui_view_phrase_editor.rs b/crates/tek_tui/src/tui_view_phrase_editor.rs index b248a930..9840d01b 100644 --- a/crates/tek_tui/src/tui_view_phrase_editor.rs +++ b/crates/tek_tui/src/tui_view_phrase_editor.rs @@ -16,8 +16,6 @@ pub struct PhraseView<'a> { time_start: usize, time_point: usize, - time_clamp: usize, - time_scale: usize, } impl<'a, T: HasEditor> From<&'a T> for PhraseView<'a> { @@ -57,8 +55,6 @@ impl<'a, T: HasEditor> From<&'a T> for PhraseView<'a> { time_start: editor.time_start.load(Ordering::Relaxed), time_point: editor.time_point.load(Ordering::Relaxed), - time_clamp: editor.time_clamp.load(Ordering::Relaxed), - time_scale: editor.time_scale.load(Ordering::Relaxed), } } } @@ -79,8 +75,6 @@ impl<'a> Content for PhraseView<'a> { note_point, time_start, time_point, - time_clamp, - time_scale, now, .. } = self; @@ -110,7 +104,7 @@ impl<'a> Content for PhraseView<'a> { ); if let Some(phrase) = phrase { upper_right = format!("┤Time: {}/{} {}├{upper_right}", - time_point, phrase.read().unwrap().length, pulses_to_name(*time_scale), + time_point, phrase.read().unwrap().length, pulses_to_name(view_mode.time_zoom()), ) }; @@ -137,7 +131,7 @@ impl<'a> Content for PhraseView<'a> { Ok(if *focused && *entered { view_mode.blit_cursor( to, - *time_point, *time_start, *time_scale, + *time_point, *time_start, view_mode.time_zoom(), *note_point, *note_len, *note_hi, *note_lo, ) }) @@ -149,21 +143,21 @@ impl<'a> Content for PhraseView<'a> { 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; - for x in 0..(time_clamp/time_scale).saturating_sub(*time_start) { - let this_step = time_start + (x + 0) * time_scale; - let next_step = time_start + (x + 1) * time_scale; - let x = to.area().x() + x as u16; - let active = this_step <= now && now < next_step; - let character = if active { "|" } else { "·" }; - let style = if active { playhead_active } else { playhead_inactive }; - to.blit(&character, x, to.area.y(), Some(style)); - } - } + //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; + //for x in 0..(time_clamp/time_scale).saturating_sub(*time_start) { + //let this_step = time_start + (x + 0) * time_scale; + //let next_step = time_start + (x + 1) * time_scale; + //let x = to.area().x() + x as u16; + //let active = this_step <= now && now < next_step; + //let character = if active { "|" } else { "·" }; + //let style = if active { playhead_active } else { playhead_inactive }; + //to.blit(&character, x, to.area.y(), Some(style)); + //} + //} Ok(()) }).push_x(6).align_sw(), TuiStyle::fg(upper_left.to_string(), title_color).push_x(1).align_nw(), @@ -194,6 +188,21 @@ pub enum PhraseViewNoteZoom { } impl PhraseViewMode { + pub fn time_zoom (&self) -> usize { + match self { + Self::PianoHorizontal { time_zoom, .. } => *time_zoom, + _ => unimplemented!() + } + } + pub fn set_time_zoom (&mut self, time_zoom: usize) { + *self = match self { + Self::PianoHorizontal { note_zoom, .. } => Self::PianoHorizontal { + note_zoom: *note_zoom, + time_zoom, + }, + _ => unimplemented!() + } + } /// Return a new [BigBuffer] containing a render of the phrase. pub fn draw (&self, phrase: &Phrase) -> BigBuffer { let mut buffer = BigBuffer::new(self.buffer_width(phrase), self.buffer_height(phrase)); @@ -328,15 +337,36 @@ impl PhraseViewMode { fn draw_piano_horizontal ( target: &mut BigBuffer, phrase: &Phrase, time_zoom: usize, _: usize ) { + //cell.set_char(if ppq == 0 { + //'·' + //} else if x % (4 * ppq) == 0 { + //'│' + //} else if x % ppq == 0 { + //'╎' + //} else { + //'·' + //}); + //cell.set_fg(Color::Rgb(48, 64, 56)); let mut notes_on = [false;128]; + for (y, _) in (127..0).enumerate() { + for (x, _) in (0..phrase.length).step_by(time_zoom).enumerate() { + let cell = target.get_mut(x, y).unwrap(); + cell.set_fg(Color::Rgb(28, 35, 25)); + cell.set_char('·'); + } + } for (x, time_start) in (0..phrase.length).step_by(time_zoom).enumerate() { let time_end = time_start + time_zoom; for time in time_start..time_end { for (y, note) in (127..0).enumerate() { let cell = target.get_mut(x, y).unwrap(); - cell.set_fg(Color::Rgb(255, 255, 255)); - cell.set_bg(Color::Rgb(28, 35, 25)); - cell.set_char(if notes_on[note] { '▄' } else { '?' }); + if notes_on[note] { + cell.set_fg(Color::Rgb(255, 255, 255)); + cell.set_bg(Color::Rgb(0, 0, 0)); + cell.set_char('▄'); + } else { + cell.set_char('x'); + } } for event in phrase.notes[time].iter() { match event {