remove H_KEYS_OFFSET, just use row macro

This commit is contained in:
🪞👃🪞 2024-10-22 21:30:47 +03:00
parent ee94df78f6
commit c56758b616
4 changed files with 50 additions and 52 deletions

View file

@ -47,6 +47,8 @@ pub trait Size<N: Number> {
#[inline] fn w (&self) -> N { self.x() } #[inline] fn w (&self) -> N { self.x() }
#[inline] fn h (&self) -> N { self.y() } #[inline] fn h (&self) -> N { self.y() }
#[inline] fn wh (&self) -> [N;2] { [self.x(), self.y()] } #[inline] fn wh (&self) -> [N;2] { [self.x(), self.y()] }
#[inline] fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w.into()), self.h()] }
#[inline] fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h.into())] }
#[inline] fn expect_min (&self, w: N, h: N) -> Usually<&Self> { #[inline] fn expect_min (&self, w: N, h: N) -> Usually<&Self> {
if self.w() < w || self.h() < h { if self.w() < w || self.h() < h {
Err(format!("min {w}x{h}").into()) Err(format!("min {w}x{h}").into())
@ -67,20 +69,29 @@ impl<N: Number> Size<N> for [N;2] {
} }
pub trait Area<N: Number>: Copy { pub trait Area<N: Number>: Copy {
fn x (&self) -> N; fn x (&self) -> N;
fn y (&self) -> N; fn y (&self) -> N;
fn w (&self) -> N; fn w (&self) -> N;
fn h (&self) -> N; fn h (&self) -> N;
fn x2 (&self) -> N { self.x() + self.w() } fn x2 (&self) -> N { self.x() + self.w() }
fn y2 (&self) -> N { self.y() + self.h() } fn y2 (&self) -> N { self.y() + self.h() }
#[inline] fn wh (&self) -> [N;2] { #[inline] fn wh (&self) -> [N;2] { [self.w(), self.h()] }
[self.w(), self.h()] #[inline] fn xywh (&self) -> [N;4] { [self.x(), self.y(), self.w(), self.h()] }
#[inline] fn lrtb (&self) -> [N;4] { [self.x(), self.x2(), self.y(), self.y2()] }
#[inline] fn push_x (&self, x: N) -> [N;4] { [self.x() + x, self.y(), self.w(), self.h()] }
#[inline] fn push_y (&self, y: N) -> [N;4] { [self.x(), self.y() + y, self.w(), self.h()] }
#[inline] fn shrink_x (&self, x: N) -> [N;4] { [self.x(), self.y(), self.w() - x, self.h()] }
#[inline] fn shrink_y (&self, y: N) -> [N;4] { [self.x(), self.y(), self.w(), self.h() - y] }
#[inline] fn set_w (&self, w: N) -> [N;4] { [self.x(), self.y(), w, self.h()] }
#[inline] fn set_h (&self, h: N) -> [N;4] { [self.x(), self.y(), self.w(), h] }
#[inline] fn clip_h (&self, h: N) -> [N;4] {
[self.x(), self.y(), self.w(), self.h().min(h.into())]
} }
#[inline] fn xywh (&self) -> [N;4] { #[inline] fn clip_w (&self, w: N) -> [N;4] {
[self.x(), self.y(), self.w(), self.h()] [self.x(), self.y(), self.w().min(w.into()), self.h()]
} }
#[inline] fn lrtb (&self) -> [N;4] { #[inline] fn clip (&self, wh: impl Size<N>) -> [N;4] {
[self.x(), self.x2(), self.y(), self.y2()] [self.x(), self.y(), wh.w(), wh.h()]
} }
#[inline] fn expect_min (&self, w: N, h: N) -> Usually<&Self> { #[inline] fn expect_min (&self, w: N, h: N) -> Usually<&Self> {
if self.w() < w || self.h() < h { if self.w() < w || self.h() < h {
@ -89,9 +100,6 @@ pub trait Area<N: Number>: Copy {
Ok(self) Ok(self)
} }
} }
#[inline] fn clip (&self, wh: impl Size<N>) -> [N;4] {
[self.x(), self.y(), wh.w(), wh.h()]
}
#[inline] fn split_fixed (&self, direction: Direction, a: N) -> ([N;4],[N;4]) { #[inline] fn split_fixed (&self, direction: Direction, a: N) -> ([N;4],[N;4]) {
match direction { match direction {
Direction::Up => ( Direction::Up => (
@ -109,14 +117,6 @@ pub trait Area<N: Number>: Copy {
_ => todo!(), _ => todo!(),
} }
} }
#[inline] fn push_x (&self, x: N) -> [N;4] { [self.x() + x, self.y(), self.w(), self.h()] }
#[inline] fn push_y (&self, y: N) -> [N;4] { [self.x(), self.y() + y, self.w(), self.h()] }
#[inline] fn shrink_x (&self, x: N) -> [N;4] { [self.x(), self.y(), self.w() - x, self.h()] }
#[inline] fn shrink_y (&self, y: N) -> [N;4] { [self.x(), self.y(), self.w(), self.h() - y] }
#[inline] fn set_w (&self, w: N) -> [N;4] { [self.x(), self.y(), w, self.h()] }
#[inline] fn set_h (&self, h: N) -> [N;4] { [self.x(), self.y(), self.w(), h] }
#[inline] fn clip_h (&self, h: N) -> [N;4] { [self.x(), self.y(), self.w(), self.h().min(h)] }
#[inline] fn clip_w (&self, w: N) -> [N;4] { [self.x(), self.y(), self.w().min(w), self.h()] }
} }
impl<N: Number> Area<N> for (N, N, N, N) { impl<N: Number> Area<N> for (N, N, N, N) {

View file

@ -123,7 +123,7 @@ impl<E: Engine> Arranger<E> {
transport, transport,
arrangement, arrangement,
phrases, phrases,
phrases_split: 20, phrases_split: 20,
arrangement_split: 20, arrangement_split: 20,
}; };
app.update_focus(); app.update_focus();

View file

@ -32,8 +32,8 @@ impl Content for ArrangerStatusBar {
Self::ArrangementScene => "SCENE", Self::ArrangementScene => "SCENE",
Self::ArrangementClip => "CLIP", Self::ArrangementClip => "CLIP",
Self::PhrasePool => "SEQ LIST", Self::PhrasePool => "SEQ LIST",
Self::PhraseView => "SEQ VIEW", Self::PhraseView => "VIEW SEQ",
Self::PhraseEdit => "SEQ EDIT", Self::PhraseEdit => "EDIT SEQ",
}; };
let mode = TuiStyle::bg(format!(" {label} "), Color::Rgb(150, 160, 90)) let mode = TuiStyle::bg(format!(" {label} "), Color::Rgb(150, 160, 90))
.fg(Color::Rgb(0, 0, 0)) .fg(Color::Rgb(0, 0, 0))
@ -259,13 +259,13 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
to.render_in(clip_area, &CORNERS)?; to.render_in(clip_area, &CORNERS)?;
//to.fill_bg(clip_area, Color::Rgb(40, 50, 30)); //to.fill_bg(clip_area, Color::Rgb(40, 50, 30));
} else if let Some(track_area) = track_area { } else if let Some(track_area) = track_area {
to.render_in(track_area.clip_h(2), &CORNERS)?; to.render_in(track_area.clip_h(2u16), &CORNERS)?;
//to.fill_bg(track_area, Color::Rgb(40, 50, 30)); //to.fill_bg(track_area, Color::Rgb(40, 50, 30));
} else if let Some(scene_area) = scene_area { } else if let Some(scene_area) = scene_area {
to.render_in(scene_area.clip_w(offset-1), &CORNERS)?; to.render_in(scene_area.clip_w(offset-1), &CORNERS)?;
//to.fill_bg(scene_area, Color::Rgb(40, 50, 30)); //to.fill_bg(scene_area, Color::Rgb(40, 50, 30));
} else { } else {
to.render_in(area.clip_w(offset-1).clip_h(2), &CORNERS)?; to.render_in(area.clip_w(offset-1).clip_h(2u16), &CORNERS)?;
} }
} }
Ok(()) Ok(())

View file

@ -52,10 +52,9 @@ impl Content for PhraseEditor<Tui> {
let Self { let Self {
focused, entered, time_axis, note_axis, keys, phrase, buffer, note_len, .. focused, entered, time_axis, note_axis, keys, phrase, buffer, note_len, ..
} = self; } = self;
let offset = Self::H_KEYS_OFFSET as u16;
let color = Color::Rgb(0,255,0); let color = Color::Rgb(0,255,0);
let color = phrase.as_ref().map(|p|p.read().unwrap().color).unwrap_or(color); let color = phrase.as_ref().map(|p|p.read().unwrap().color).unwrap_or(color);
let keys = CustomWidget::new(|_|Ok(Some([32u16,0u16])), move|to: &mut TuiOutput|{ let keys = CustomWidget::new(|to:[u16;2]|Ok(Some(to.clip_w(5))), move|to: &mut TuiOutput|{
if to.area().h() >= 2 { if to.area().h() >= 2 {
to.buffer_update(to.area().set_w(5), &|cell, x, y|{ to.buffer_update(to.area().set_w(5), &|cell, x, y|{
let y = y + note_axis.start as u16; let y = y + note_axis.start as u16;
@ -66,11 +65,12 @@ impl Content for PhraseEditor<Tui> {
} }
Ok(()) Ok(())
}).fill_y(); }).fill_y();
let notes = CustomWidget::new(|_|Ok(Some([32u16,4u16])), move|to: &mut TuiOutput|{ let notes_bg_null = Color::Rgb(28, 35, 25);
if to.area().h() >= 2 && to.area().w() >= offset { let notes = CustomWidget::new(|to|Ok(Some(to)), move|to: &mut TuiOutput|{
let area = to.area().push_x(offset).shrink_x(offset); if to.area().h() >= 2 {
let area = to.area();
to.buffer_update(area, &move |cell, x, y|{ to.buffer_update(area, &move |cell, x, y|{
cell.set_bg(Color::Rgb(20, 20, 20)); cell.set_bg(notes_bg_null);
let src_x = ((x as usize + time_axis.start) * time_axis.scale) as usize; 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; let src_y = (y as usize + note_axis.start) as usize;
if src_x < buffer.width && src_y < buffer.height - 1 { if src_x < buffer.width && src_y < buffer.height - 1 {
@ -83,11 +83,11 @@ impl Content for PhraseEditor<Tui> {
} }
Ok(()) Ok(())
}).fill_x(); }).fill_x();
let cursor = CustomWidget::new(|_|Ok(Some([1u16,1u16])), move|to: &mut TuiOutput|{ let cursor = CustomWidget::new(|to|Ok(Some(to)), move|to: &mut TuiOutput|{
if *entered { if *entered {
let area = to.area(); let area = to.area();
if let (Some(time), Some(note)) = (time_axis.point, note_axis.point) { 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 x = area.x() + time as u16;
let y = area.y() + 1 + note as u16 / 2; let y = area.y() + 1 + note as u16 / 2;
let c = if note % 2 == 0 { "" } else { "" }; let c = if note % 2 == 0 { "" } else { "" };
to.blit(&c, x, y, Some(Style::default().fg(color))); to.blit(&c, x, y, Some(Style::default().fg(color)));
@ -95,22 +95,21 @@ impl Content for PhraseEditor<Tui> {
} }
Ok(()) Ok(())
}); });
let playhead = CustomWidget::new(|_|Ok(Some([32u16,2u16])), move|to: &mut TuiOutput|{ let playhead_inactive = Style::default().fg(Color::Rgb(255,255,255)).bg(Color::Rgb(0,0,0));
let playhead_active = playhead_inactive.yellow().bold().not_dim();
let playhead = CustomWidget::new(|to|Ok(Some(to)), move|to: &mut TuiOutput|{
if let Some(_) = phrase { if let Some(_) = phrase {
let time_0 = time_axis.start; let now = 0; // TODO FIXME: self.now % phrase.read().unwrap().length;
let time_z = time_axis.scale; let ScaledAxis { start: first_beat, scale: time_zoom, .. } = time_axis;
let now = 0; // TODO FIXME: self.now % phrase.read().unwrap().length; for x in 0..10 {
let [x, y, width, _] = to.area(); let this_step = (first_beat + 0) * time_zoom;
let x2 = x as usize + Self::H_KEYS_OFFSET; let next_step = (first_beat + 1) * time_zoom;
let x3 = x as usize + width as usize; let x = to.area().x() + x;
for x in x2..x3 { to.blit(&"-", x, to.area.y(), Some(if this_step <= now && now < next_step {
let step = (time_0 + x2) * time_z; playhead_active
let next_step = (time_0 + x2 + 1) * time_z; } else {
let mut style = Style::default().fg(Color::Rgb(255,255,255)); playhead_active
if step <= now && now < next_step { }));
style = style.yellow().bold().not_dim()
}
to.blit(&"-", x as u16, y, Some(style));
} }
} }
Ok(()) Ok(())
@ -118,7 +117,8 @@ impl Content for PhraseEditor<Tui> {
let border_color = if *focused{Color::Rgb(100, 110, 40)}else{Color::Rgb(70, 80, 50)}; 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 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 border = Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color));
let piano_roll = lay!(keys, notes, cursor).fill_x(); let note_area = lay!(notes, cursor, playhead).fill_x();
let piano_roll = row!(keys, note_area).fill_x();
let content_bg = Color::Rgb(40, 50, 30); let content_bg = Color::Rgb(40, 50, 30);
let content = piano_roll.bg(content_bg).border(border); let content = piano_roll.bg(content_bg).border(border);
let mut upper_left = String::from("Sequencer"); let mut upper_left = String::from("Sequencer");
@ -131,14 +131,12 @@ impl Content for PhraseEditor<Tui> {
} }
lay!( lay!(
content, content,
playhead,
TuiStyle::fg(upper_left.to_string(), 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(upper_right.to_string(), title_color).pull_x(1).align_ne().fill_xy(), TuiStyle::fg(upper_right.to_string(), title_color).pull_x(1).align_ne().fill_xy(),
) )
} }
} }
impl<E: Engine> PhraseEditor<E> { impl<E: Engine> PhraseEditor<E> {
const H_KEYS_OFFSET: usize = 5;
pub fn put (&mut self) { pub fn put (&mut self) {
if let (Some(phrase), Some(time), Some(note)) = ( if let (Some(phrase), Some(time), Some(note)) = (
&self.phrase, &self.phrase,