refactor piano roll layout

This commit is contained in:
🪞👃🪞 2024-12-04 13:23:46 +01:00
parent f2fdf3dd12
commit d2a9e0b722

View file

@ -84,108 +84,11 @@ impl<'a> Content for PhraseView<'a> {
now,
..
} = self;
let keys = move|to: &mut TuiOutput|Ok(if to.area().h() >= 2 {
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!()
}
}
});
let notes_bg_null = Color::Rgb(28, 35, 25);
let notes = move|to: &mut TuiOutput|{
let area = to.area();
let h = area.h() as usize;
size.set_wh(area.w(), h - 1);
Ok(if to.area().h() >= 2 {
let area = to.area();
view_mode.blit(buffer, &mut to.buffer, area, *time_start, *note_hi);
//to.buffer_update(area, &move |cell, x, y|{
//cell.set_bg(notes_bg_null);
//let src_x = (x as usize + time_start) * time_scale;
//let src_y = y as usize + note_lo / 2;
//if src_x < buffer.width && src_y < buffer.height - 1 {
//buffer.get(src_x, (note_hi - y as usize) / 2).map(|src|{
//cell.set_symbol(src.symbol());
//cell.set_fg(src.fg);
//cell.set_bg(src.bg);
//});
//}
//});
})
};
let cursor = move|to: &mut TuiOutput|Ok(if *focused && *entered {
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);
}
});
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 playhead = move|to: &mut TuiOutput|{
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(())
};
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 note_area = lay!(
CustomWidget::new(|to|Ok(Some(to)), notes).fill_x(),
CustomWidget::new(|to|Ok(Some(to)), cursor)
).fill_x();
let piano_roll = row!(
CustomWidget::new(|to:[u16;2]|Ok(Some(to.clip_w(2))), keys).fill_y(),
note_area
).fill_x().bg(Color::Rgb(40, 50, 30)).border(border);
let content = lay!(
piano_roll,
CustomWidget::new(|to:[u16;2]|Ok(Some(to.clip_h(1))), playhead).push_x(6).align_sw()
);
let mut name = "".to_string();
if let Some(phrase) = phrase {
name = phrase.read().unwrap().name.clone();
@ -195,21 +98,59 @@ impl<'a> Content for PhraseView<'a> {
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)
note_point, to_note_name(*note_point), pulses_to_name(*note_len)
);
}
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),
time_point, phrase.read().unwrap().length, pulses_to_name(*time_scale),
)
};
lay!(
content,
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)
})
}).fill_y(),
lay!(
CustomWidget::new(|to|Ok(Some(to)), |to: &mut TuiOutput|{
size.set_wh(to.area.w(), to.area.h() as usize - 1);
let draw = to.area().h() >= 2;
Ok(if draw {
view_mode.blit_notes(
to, buffer, *time_start, *note_hi
)
})
}).fill_x(),
CustomWidget::new(|to|Ok(Some(to)), move|to: &mut TuiOutput|{
Ok(if *focused && *entered {
view_mode.blit_cursor(
to,
*time_point, *time_scale,
*note_len, *note_hi, *note_lo, *note_point
)
})
})
).fill_x()
).fill_x().bg(Color::Rgb(40, 50, 30)).border(border),
CustomWidget::new(|to:[u16;2]|Ok(Some(to.clip_h(1))), move|to: &mut TuiOutput|{
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(),
TuiStyle::fg(lower_left.to_string(), title_color).push_x(1).align_sw(),
TuiStyle::fg(upper_right.to_string(), title_color).pull_x(1).align_ne().fill_xy(),
@ -253,14 +194,15 @@ impl PhraseViewMode {
buffer
}
/// Draw a subsection of the [BigBuffer] onto a regular ratatui [Buffer].
fn blit (
fn blit_notes (
&self,
target: &mut TuiOutput,
source: &BigBuffer,
target: &mut Buffer,
area: impl Area<u16>,
time_start: usize,
note_hi: usize,
) {
let area = target.area();
let target = &mut target.buffer;
match self {
Self::PianoHorizontal { .. } => {
let [x0, y0, w, h] = area.xywh();
@ -284,6 +226,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_cursor (
&self,
to: &mut TuiOutput,
time_point: usize,
time_scale: 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);
}
}
/// Determine the required width to render the phrase.
fn buffer_width (&self, phrase: &Phrase) -> usize {
match self {