mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
refactor piano roll layout
This commit is contained in:
parent
f2fdf3dd12
commit
d2a9e0b722
1 changed files with 107 additions and 110 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue