piano keys reappear

This commit is contained in:
i do not exist 2026-09-23 16:08:54 +03:00
parent 9db60c3429
commit 9a50ce6613
15 changed files with 856 additions and 782 deletions

View file

@ -8,75 +8,73 @@ use crate::*;
impl Draw<Tui> for App {
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
#[cfg(feature = "prof2")] profiling::scope!("App::draw");
//self.perf.cycle(&mut |_|{
self.draw_error(to)?;
self.draw_modes(to)?;
self.draw_debug(to)?;
self.perf.cycle(&mut |_|{
draw_error(to, self.error.as_ref())?;
draw_mode_stack(to, &self)?;
view_debug(&self.perf, &self.size).draw(to)?;
#[cfg(feature = "prof2")] profiling::finish_frame!();
Ok(Some(to.area().into()))
//})
})
}
}
impl App {
#[allow(unused)] fn draw_debug (&self, to: &mut Tui) -> Drawn<u16> {
Split::Above.bar(0,
Split::West.bar(16,
format!("{}/{} {} ",
self.perf.used.load(Relaxed),
self.perf.window.load(Relaxed),
self.perf.clock.raw() / 10000000).align_e(),
format!("{}x{} ",
self.size.0.load(Relaxed),
self.size.1.load(Relaxed)).align_w(),
).align_se(),
()
//Split::East.bar(16,
//format!("{}", self.modes.read()[0]),
//format!("{}", self.modes.read()[0])
//).align_sw()
).draw(to)
pub fn draw_error (to: &mut Tui, error: &RwLock<Option<Arc<str>>>) -> Usually<()> {
if let Some(e) = error.read().as_ref() {
e.as_ref().align_c().draw(to)?;
}
Ok(())
}
fn draw_error (&self, to: &mut Tui) -> Usually<()> {
if let Some(e) = self.error.try_read().unwrap().as_ref() {
e.as_ref().align_c().draw(to)?;
}
Ok(())
}
pub fn view_debug (perf: &PerfModel, size: &Sizer) -> impl Draw<Tui> {
Split::Above.bar(0,
Split::West.bar(16,
format!("{}/{} {} ",
perf.used.load(Relaxed),
perf.window.load(Relaxed),
perf.clock.raw() / 10000000).align_e(),
format!("{}x{} ",
size.0.load(Relaxed),
size.1.load(Relaxed)).align_w(),
).align_se(),
()
//Split::East.bar(16,
//format!("{}", self.modes.read()[0]),
//format!("{}", self.modes.read()[0])
//).align_sw()
)
}
fn draw_modes (&self, to: &mut Tui) -> Usually<()> {
for name in self.modes.read().iter() {
let mut error = false;
if let Some(mode) = self.config.get_mode(name).as_ref().map(Arc::clone) {
for (index, view) in mode.view.iter().enumerate() {
match (view.render)(self, to) {
Ok(None) => {},
Ok(Some(XYWH(.., w, h))) => {
self.size.0.store(w as usize, Relaxed);
self.size.1.store(h as usize, Relaxed);
},
Err(e) => {
let message = format!(
"Mode: {:?}\n\nLayer: #{index}\n\nError: {e}\n\nSource:\n{}",
&mode.name,
&view.source
);
*self.error.try_write().unwrap() = Some(message.into());
error = true;
break;
}
pub fn draw_mode_stack (to: &mut Tui, state: &App) -> Usually<()> {
for name in state.modes.read().iter() {
let mut error = false;
if let Some(mode) = state.config.get_mode(name).as_ref().map(Arc::clone) {
for (index, view) in mode.view.iter().enumerate() {
match (view.render)(state, to) {
Ok(None) => {},
Ok(Some(XYWH(.., w, h))) => {
state.size.0.store(w as usize, Relaxed);
state.size.1.store(h as usize, Relaxed);
},
Err(e) => {
let message = format!(
"Mode: {:?}\n\nLayer: #{index}\n\nError: {e}\n\nSource:\n{}",
&mode.name,
&view.source
);
*state.error.try_write().unwrap() = Some(message.into());
error = true;
break;
}
}
if error {
break
} else {
*self.error.try_write().unwrap() = None;
}
}
if error {
break
} else {
*state.error.try_write().unwrap() = None;
}
}
Ok(())
}
Ok(())
}
/// Collection of custom view definitions.
@ -365,58 +363,48 @@ impl View<Tui, App> {
fn compile_word (word: Arc<str>)
-> Usually<Arc<Box<dyn Fn(&App, &mut Tui)->Drawn<u16> + Send + Sync>>>
{
macro_rules! draw {
(|$state:ident|$body:expr)=>{Self::boxed(move|$state, to|$body.draw(to))};
($const:expr)=>{Self::boxed(move|_, to|$const.draw(to))};
()=>{Self::boxed(move|_, __|Ok(None))};
macro_rules! view {
(|$state:ident|$body:expr)=>{Box::new(move|$state, to|$body.draw(to))};
($const:expr)=>{Box::new(move|_, to|$const.draw(to))};
()=>{Box::new(move|_, __|Ok(None))};
}
Ok(Arc::new(match word.split("/").next() {
Some("Beat") => draw!(),
Some("Bpm") => draw!(),
Some("BrowseTitle") => draw!(|app|view_browse_title(app)),
Some("Debug") => Self::boxed(move|_, to|format!("[{:?}]", to.area()).exact_h(1).draw(to)),
Some("Device") => draw!(|app|view_device(app)),
Some("Menu") => draw!(|app|{
if let Dialog::Menu(selected, items) = &app.dialog {
Some(iter_south(move||items.iter().enumerate().map(move|(index, MenuItem(item, _))|{
let f = if *selected == index { Rgb(240,200,180) } else { Rgb(200, 200, 200) };
let b = if *selected == index { Rgb(80, 80, 50) } else { Rgb(30, 30, 30) };
fg_bg(f, b, item).exact_h(2).align_x()
})).exact_w(20).align_x())
} else {
None
}
}),
Some("Editor") => draw!("TODO editor"),
Some("MetersIn") => draw!(|___|bg(Rgb(30, 30, 30), "Input Meters".align_s().full_h())),
Some("MetersOut") => draw!(|___|bg(Rgb(30, 30, 30), "Output Meters".align_s().full_h())),
Some("ModeList") => draw!(|app|view_modes(app)),
Some("HistoryCount") => draw!(|app|app.history.len()),
Some("ScenesClips") => draw!(|app|view_scenes_clips(app)),
Some("ScenesNames") => draw!(|app|app.view_scenes_names()),
Some("Sessions") => draw!(|___|view_sessions()),
Some("Selected") => draw!(|app|view_selection(app)),
Some("Templates") => draw!(|app|view_templates(app)),
Some("Time") => draw!(),
Some("Tracks") => draw!("TODO tracks"),
Some("TracksDevices") => draw!(|app|app.project.view_track_devices(app.color.clone())),
Some("TracksDevicesAdd") => draw!(|app|app.project.view_track_device_add()),
Some("TracksDevicesCells") => draw!(|app|app.project.view_track_device_cells(app.tracks_devices_height())),
Some("TracksDevicesCount") => draw!(|app|app.project.view_track_device_count()),
Some("TracksInputs") => draw!(|app|app.project.view_track_inputs(app.color.clone())),
Some("TracksInputsAdd") => draw!(|app|app.project.view_track_input_add()),
Some("TracksInputsCells") => draw!(|app|app.project.view_track_input_cells(app.tracks_inputs_height())),
Some("TracksInputsCount") => draw!(|app|app.project.view_track_input_count()),
Some("TracksNames") => draw!(|app|app.project.view_track_names(app.color.clone())),
Some("TracksNamesAdd") => draw!(|app|app.project.view_track_names_add()),
Some("TracksNamesCells") => draw!(|app|app.project.view_track_names_cells()),
Some("TracksNamesCount") => draw!(|app|app.project.view_track_names_count()),
Some("TracksOutputs") => draw!(|app|app.project.view_track_outputs(app.color.clone(), 0)),
Some("TracksOutputsAdd") => draw!(|app|app.project.view_track_output_add()),
Some("TracksOutputsCells") => draw!(|app|app.project.view_track_output_cells()),
Some("TracksOutputsCount") => draw!(|app|app.project.view_track_output_count()),
Some(_) => Self::boxed(move|state, to|if let Some(view) = state.config.get_view(word.as_ref()) {
Some("Beat") => view!(),
Some("Bpm") => view!(),
Some("BrowseTitle") => view!(|app|view_browse_title(app)),
Some("Debug") => Box::new(move|_, to|format!("[{:?}]", to.area()).exact_h(1).draw(to)),
Some("Device") => view!(|app|view_device(app)),
Some("Menu") => view!(|app|view_menu(app)),
Some("Editor") => view!(|app|app.editor()),
Some("MetersIn") => view!(|___|bg(Rgb(30, 30, 30), "Input Meters".align_s().full_h())),
Some("MetersOut") => view!(|___|bg(Rgb(30, 30, 30), "Output Meters".align_s().full_h())),
Some("ModeList") => view!(|app|view_modes(app)),
Some("HistoryCount") => view!(|app|app.history.len()),
Some("ScenesClips") => view!(|app|view_scenes_clips(app)),
Some("ScenesNames") => view!(|app|app.view_scenes_names()),
Some("Sessions") => view!(|___|view_sessions()),
Some("Selected") => view!(|app|view_selection(app)),
Some("Templates") => view!(|app|view_templates(app)),
Some("Time") => view!(),
Some("Tracks") => view!("TODO tracks"),
Some("TracksDevices") => view!(|app|app.project.view_track_devices(app.color.clone())),
Some("TracksDevicesAdd") => view!(|app|app.project.view_track_device_add()),
Some("TracksDevicesCells") => view!(|app|app.project.view_track_device_cells(app.tracks_devices_height())),
Some("TracksDevicesCount") => view!(|app|app.project.view_track_device_count()),
Some("TracksInputs") => view!(|app|app.project.view_track_inputs(app.color.clone())),
Some("TracksInputsAdd") => view!(|app|app.project.view_track_input_add()),
Some("TracksInputsCells") => view!(|app|app.project.view_track_input_cells(app.tracks_inputs_height())),
Some("TracksInputsCount") => view!(|app|app.project.view_track_input_count()),
Some("TracksNames") => view!(|app|app.project.view_track_names(app.color.clone())),
Some("TracksNamesAdd") => view!(|app|app.project.view_track_names_add()),
Some("TracksNamesCells") => view!(|app|app.project.view_track_names_cells()),
Some("TracksNamesCount") => view!(|app|app.project.view_track_names_count()),
Some("TracksOutputs") => view!(|app|app.project.view_track_outputs(app.color.clone(), 0)),
Some("TracksOutputsAdd") => view!(|app|app.project.view_track_output_add()),
Some("TracksOutputsCells") => view!(|app|app.project.view_track_output_cells()),
Some("TracksOutputsCount") => view!(|app|app.project.view_track_output_count()),
Some(_) => Box::new(move|state, to|if let Some(view) = state.config.get_view(word.as_ref()) {
(view.render)(state, to)
} else {
fg(Color::Rgb(128, 32, 32), format!("undefined: {word:?}")).draw(to)
@ -426,6 +414,18 @@ impl View<Tui, App> {
}
}
pub fn view_menu (app: &App) -> impl Draw<Tui> {
if let Dialog::Menu(selected, items) = &app.dialog {
Some(iter_south(move||items.iter().enumerate().map(move|(index, MenuItem(item, _))|{
let f = if *selected == index { Rgb(240,200,180) } else { Rgb(200, 200, 200) };
let b = if *selected == index { Rgb(80, 80, 50) } else { Rgb(30, 30, 30) };
fg_bg(f, b, item).exact_h(2).align_x()
})).exact_w(20).align_x())
} else {
None
}
}
/// ```
/// let _ = tek::button_play_pause(true, true);
/// let _ = tek::button_play_pause(true, false);
@ -489,7 +489,9 @@ pub fn view_piano_h_notes (state: &PianoHorizontal) -> impl Draw<Tui> {
let is_in_y = source_y < source.height;
if is_in_x && is_in_y {
if let Some(source_cell) = source.get(source_x, source_y) {
if let Some(cell) = to.cell_mut(ratatui::prelude::Position::from((screen_x, screen_y))) {
if let Some(cell) = to.cell_mut(
ratatui::prelude::Position::from((screen_x, screen_y))
) {
*cell = source_cell.clone();
}
}
@ -605,47 +607,43 @@ impl OctaveVertical {
}
}
impl HasClipsSize for App { fn clips_size (&self) -> &Sizer { &self.project.size_inner } }
impl HasClipsSize for Project { fn clips_size (&self) -> &Sizer { &self.size_inner } }
pub fn view_editor_clip_status (editor: &MidiEditor) -> impl Draw<Tui> + '_ {
let (
_color, name, length, looped
) = if let Some(clip) = editor.mode.clip.read().as_ref().map(|p|p.read()) {
(clip.color, clip.name.clone(), clip.length, clip.looped)
} else {
(ItemTheme::G[64], String::new().into(), 0, false)
};
south!(
east(button_2("f2", "name ", false), fg(Rgb(255, 255, 255), format!("{name} "))
.origin_e().full_w()).origin_w().full_w(),
east(button_2("l", "ength ", false), fg(Rgb(255, 255, 255), format!("{length} ")).origin_e().full_w())
.origin_w().full_w(),
east(button_2("r", "epeat ", false), fg(Rgb(255, 255, 255), format!("{looped} ")).origin_e().full_w())
.origin_w().full_w(),
).exact_w(20)
}
impl MidiEditor {
pub fn clip_status (&self) -> impl Draw<Tui> + '_ {
let (
_color, name, length, looped
) = if let Some(clip) = self.mode.clip.read().as_ref().map(|p|p.read()) {
(clip.color, clip.name.clone(), clip.length, clip.looped)
} else {
(ItemTheme::G[64], String::new().into(), 0, false)
};
south!(
east(button_2("f2", "name ", false), fg(Rgb(255, 255, 255), format!("{name} "))
.origin_e().full_w()).origin_w().full_w(),
east(button_2("l", "ength ", false), fg(Rgb(255, 255, 255), format!("{length} ")).origin_e().full_w())
.origin_w().full_w(),
east(button_2("r", "epeat ", false), fg(Rgb(255, 255, 255), format!("{looped} ")).origin_e().full_w())
.origin_w().full_w(),
).exact_w(20)
}
pub fn edit_status (&self) -> impl Draw<Tui> + '_ {
let (_color, length) = if let Some(clip) = self.mode.clip.read().as_ref().map(|p|p.read()) {
(clip.color, clip.length)
} else { (ItemTheme::G[64], 0) };
let time_pos = self.get_time_pos();
let time_zoom = self.get_time_zoom();
let time_lock = if self.get_time_lock() { "[lock]" } else { " " };
let note_pos = self.get_note_pos();
let note_name = format!("{:4}", note_pitch_to_name(note_pos));
let note_pos = format!("{:>3}", note_pos);
let note_len = format!("{:>4}", self.get_note_len());
south!(
east(button_2("t", "ime ", false),
fg(Rgb(255, 255, 255), format!("{length} /{time_zoom} +{time_pos} ")).origin_e().full_w()).origin_w().full_w(),
east(button_2("z", "lock ", false),
fg(Rgb(255, 255, 255), format!("{time_lock}")).origin_e().full_w()).origin_w().full_w(),
east(button_2("x", "note ", false),
fg(Rgb(255, 255, 255), format!("{note_name} {note_pos} {note_len}")).origin_e().full_w()).origin_w().full_w(),
).exact_w(20)
}
pub fn view_editor_edit_status (editor: &MidiEditor) -> impl Draw<Tui> + '_ {
let (_color, length) = if let Some(clip) = editor.mode.clip.read().as_ref().map(|p|p.read()) {
(clip.color, clip.length)
} else { (ItemTheme::G[64], 0) };
let time_pos = editor.get_time_pos();
let time_zoom = editor.get_time_zoom();
let time_lock = if editor.get_time_lock() { "[lock]" } else { " " };
let note_pos = editor.get_note_pos();
let note_name = format!("{:4}", note_pitch_to_name(note_pos));
let note_pos = format!("{:>3}", note_pos);
let note_len = format!("{:>4}", editor.get_note_len());
south!(
east(button_2("t", "ime ", false),
fg(Rgb(255, 255, 255), format!("{length} /{time_zoom} +{time_pos} ")).origin_e().full_w()).origin_w().full_w(),
east(button_2("z", "lock ", false),
fg(Rgb(255, 255, 255), format!("{time_lock}")).origin_e().full_w()).origin_w().full_w(),
east(button_2("x", "note ", false),
fg(Rgb(255, 255, 255), format!("{note_name} {note_pos} {note_len}")).origin_e().full_w()).origin_w().full_w(),
).exact_w(20)
}
impl Draw<Tui> for MidiEditor {
@ -660,28 +658,31 @@ impl<T> TracksView for T where T:
HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTrack
{}
fn iter_tracks_with_sizes <'t, T> (state: &'t T) -> impl TracksSizes<'t> where
T: HasTracks + HasEditor + HasSelection + HasClipsSize + ?Sized
{
let _editor_width = state.editor().as_ref().map(|e|e.size.w());
let _active_track = state.selection().track();
let mut x = 0;
let w = state.clips_size().w() as usize;
state.tracks().iter().enumerate().map_while(move |(index, track)|{
let width = track.width.max(8);
if x + width < w {
let data = (index, track, x, x + width);
x += width;
Some(data)
} else {
None
}
})
}
pub trait TracksView:
HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTrack
{
/// Spacing between tracks.
const TRACK_SPACING: usize = 0;
/// Iterate over tracks with their corresponding sizes.
fn tracks_with_sizes (&self) -> impl TracksSizes<'_> {
let _editor_width = self.editor().as_ref().map(|e|e.size.w());
let _active_track = self.selection().track();
let mut x = 0;
let w = self.clips_size().w() as usize;
self.tracks().iter().enumerate().map_while(move |(index, track)|{
let width = track.width.max(8);
if x + width < w {
let data = (index, track, x, x + width);
x += width + Self::TRACK_SPACING;
Some(data)
} else {
None
}
})
iter_tracks_with_sizes(self)
}
fn view_track_column <'t, S, D, F> (&'t self, f: F) -> impl Draw<S> where
@ -830,7 +831,13 @@ pub trait TracksView:
fn view_outputs (&self, theme: ItemTheme) -> impl Draw<Tui> {
let height = self.outputs_height();
let list = south(
let list = self.view_outputs_list(height);
view_track_row_section(theme, list, self.view_track_output_add(),
bg(theme.darker.term, self.view_outputs_grid().align_w().full_w())).exact_h(height)
}
fn view_outputs_list (&self, height: u16) -> impl Draw<Tui> {
south(
button_3("o", "utput", self.midi_outs().len(), false).align_w().full_w().exact_h(1),
bg(Color::Rgb(0, 0, 0), draw(|to: &mut Tui|{
for (_index, port) in self.midi_outs().iter().enumerate() {
@ -844,26 +851,28 @@ pub trait TracksView:
}
}
todo!();
})).align_nw().full_wh().exact_h(height - 1).min_h(1)
);
view_track_row_section(theme, list, self.view_track_output_add(),
bg(theme.darker.term, draw(|to: &mut Tui|{
for (index, track, _x1, _x2) in self.tracks_with_sizes() {
let _ = draw(|to: &mut Tui|{
east(either(true, fg(Green, "play "), "play "),
either(false, fg(Yellow, "solo "), "solo ")).align_w().exact_h(1).draw(to)?;
for (_index, port) in self.midi_outs().iter().enumerate() {
east(either(true, fg(Green, " ● "), " · "),
either(false, fg(Yellow, " ● "), " · ")).align_w().exact_h(1).draw(to)?;
for (_index, _conn) in port.connections.iter().enumerate() {
"".full_w().exact_h(1).draw(to)?;
}
})).align_nw().full_wh().exact_h(height.minus(1)).min_h(1)
)
}
fn view_outputs_grid (&self) -> impl Draw<Tui> {
draw(|to: &mut Tui|{
for (index, track, _x1, _x2) in self.tracks_with_sizes() {
let _ = draw(|to: &mut Tui|{
east(either(true, fg(Green, "play "), "play "),
either(false, fg(Yellow, "solo "), "solo ")).align_w().exact_h(1).draw(to)?;
for (_index, port) in self.midi_outs().iter().enumerate() {
east(either(true, fg(Green, " ● "), " · "),
either(false, fg(Yellow, " ● "), " · ")).align_w().exact_h(1).draw(to)?;
for (_index, _conn) in port.connections.iter().enumerate() {
"".full_w().exact_h(1).draw(to)?;
}
todo!()
}).exact_w(track_width(index, track)).draw(to)?;
}
todo!()
}).align_w().full_w())).exact_h(height)
}
todo!()
}).exact_w(track_width(index, track)).draw(to)?;
}
todo!()
})
}
}
@ -876,7 +885,9 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize +
fn h_scenes (&self) -> u16;
fn w_side (&self) -> u16;
fn w_mid (&self) -> u16;
fn scenes_with_sizes (&self) -> impl ScenesSizes<'_> {
iter_scenes_with_sizes(self)
}
fn view_scenes_names (&self) -> impl Draw<Tui> where Self: Sized {
let select = self.selection();
let editor = self.editor();
@ -885,50 +896,33 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize +
view_scene_name(select, editor.as_ref(), index, scene, editing)
}))
}
fn scenes_with_sizes (&self) -> impl ScenesSizes<'_> {
let mut y = 0;
self.scenes().iter().enumerate().skip(self.scene_scroll()).map_while(move|(s, scene)|{
let height = if self.selection().scene() == Some(s)
&& let Some(editor) = self.editor()
{
editor.size.h() as usize
} else {
Scene::DEFAULT_HEIGHT
};
if y + height <= self.clips_size().h() as usize {
let data = (s, scene, y, y + height);
y += height;
Some(data)
} else {
None
}
})
}
}
impl ScenesView for App {
fn w_mid (&self) -> u16 {
(self.size.w() as u16).saturating_sub(self.w_side())
}
fn w_side (&self) -> u16 {
20
}
fn h_scenes (&self) -> u16 {
(self.size.h() as u16).saturating_sub(20)
}
}
fn iter_scenes_with_sizes <'s, T> (state: &'s T) -> impl ScenesSizes<'s> where T:
HasScenes + HasSceneScroll + HasEditor + HasSelection + HasClipsSize + ?Sized
{
let mut y = 0;
let skip = state.scene_scroll();
let selected = state.selection();
let editor = state.editor();
let clips_size = state.clips_size();
state.scenes().iter().enumerate().skip(skip).map_while(move|(s, scene)|{
let height = if selected.scene() == Some(s)
&& let Some(editor) = editor
{
editor.size.h() as usize
} else {
Scene::DEFAULT_HEIGHT
};
impl ScenesView for Project {
fn h_scenes (&self) -> u16 {
(self.size.h() as u16).saturating_sub(20)
}
fn w_side (&self) -> u16 {
(self.size.w() as u16 * 2 / 10).max(20)
}
fn w_mid (&self) -> u16 {
(self.size.w() as u16).saturating_sub(2 * self.w_side()).max(40)
}
if y + height <= clips_size.h() as usize {
let data = (s, scene, y, y + height);
y += height;
Some(data)
} else {
None
}
})
}
fn view_modes <'a, S: HasModeStack> (state: &'a S) -> impl Draw<Tui> + 'a {
@ -968,7 +962,7 @@ fn view_scenes_clips <S: TracksView + ScenesView> (state: &S) -> impl Draw<Tui>
let is_sel = view_scene_sel(select, track_index, scene_index, editing);
below(
Outer(true, Style::default().fg(o)).full_wh(),
below(
above(
below(
fg_bg(o, b, "".full_wh()),
fg_bg(f, b, bold(true, name)).align_nw().full_wh(),
@ -1204,9 +1198,11 @@ pub fn view_scene_name <'a> (
Scene::DEFAULT_HEIGHT as u16
};
let a = east!("·s", index, " ", fg(g(255), bold(true, &scene.name))).align_w().full_w();
let b = when(select.scene() == Some(index) && editing, south(
editor.as_ref().map(|e|e.clip_status()),
editor.as_ref().map(|e|e.edit_status())).align_nw().full_wh());
let s = select.scene() == Some(index) && editing;
let b = when(s, south(
editor.map(view_editor_clip_status),
editor.map(view_editor_edit_status)
).full_wh().align_se());
let c = if select.scene() == Some(index) {
scene.color.light.term
} else {
@ -1462,62 +1458,67 @@ pub fn view_wrap <'a> (bg: Color, fg: Color, content: impl Draw<Tui>)
east(left, west(right, fg_bg(fg, bg, content)))
}
impl PianoHorizontal {
/// Draw the piano roll background.
///
/// This mode uses full blocks on note on and half blocks on legato: █▄ █▄ █▄
pub fn draw_bg (
buf: &mut BigBuffer, clip: &MidiClip, zoom: usize, note_len: usize, note_point: usize, time_point: usize
) {
for (y, note) in (0..=127).rev().enumerate() {
for (x, time) in (0..buf.width).map(|x|(x, x*zoom)) {
let cell = buf.get_mut(x, y).unwrap();
if note == (127-note_point) || time == time_point {
cell.set_bg(Rgb(0,0,0));
} else {
cell.set_bg(clip.color.darkest.term);
}
if time % 384 == 0 {
cell.set_fg(clip.color.darker.term);
cell.set_char('│');
} else if time % 96 == 0 {
cell.set_fg(clip.color.dark.term);
cell.set_char('╎');
} else if time % note_len == 0 {
cell.set_fg(clip.color.darker.term);
cell.set_char('┊');
} else if (127 - note) % 12 == 0 {
cell.set_fg(clip.color.darker.term);
cell.set_char('=');
} else if (127 - note) % 6 == 0 {
cell.set_fg(clip.color.darker.term);
cell.set_char('—');
} else {
cell.set_fg(clip.color.darker.term);
cell.set_char('·');
}
/// Draw the piano roll background.
pub fn draw_piano_bg (
buf: &mut BigBuffer,
clip: &MidiClip,
zoom: usize,
note_len: usize,
note_point: usize,
time_point: usize
) {
for (y, note) in (0..=127).rev().enumerate() {
for (x, time) in (0..buf.width).map(|x|(x, x*zoom)) {
let cell = buf.get_mut(x, y).unwrap();
if note == (127-note_point) || time == time_point {
cell.set_bg(Rgb(0,0,0));
} else {
cell.set_bg(clip.color.darkest.term);
}
if time % 384 == 0 {
cell.set_fg(clip.color.darker.term);
cell.set_char('│');
} else if time % 96 == 0 {
cell.set_fg(clip.color.dark.term);
cell.set_char('╎');
} else if time % note_len == 0 {
cell.set_fg(clip.color.darker.term);
cell.set_char('┊');
} else if (127 - note) % 12 == 0 {
cell.set_fg(clip.color.darker.term);
cell.set_char('=');
} else if (127 - note) % 6 == 0 {
cell.set_fg(clip.color.darker.term);
cell.set_char('—');
} else {
cell.set_fg(clip.color.darker.term);
cell.set_char('·');
}
}
}
}
/// Draw the piano roll foreground.
///
/// This mode uses full blocks on note on and half blocks on legato: █▄ █▄ █▄
pub fn draw_fg (buf: &mut BigBuffer, clip: &MidiClip, zoom: usize) {
let style = Style::default().fg(clip.color.base.term);//.bg(Rgb(0, 0, 0));
let mut notes_on = [false;128];
for (x, time_start) in (0..clip.length).step_by(zoom).enumerate() {
for (_y, note) in (0..=127).rev().enumerate() {
if let Some(cell) = buf.get_mut(x, note) {
if notes_on[note] {
cell.set_char('▂');
cell.set_style(style);
}
/// Draw the piano roll foreground.
pub fn draw_piano_fg (
buf: &mut BigBuffer,
clip: &MidiClip,
zoom: usize
) {
let style = Style::default().fg(clip.color.base.term);//.bg(Rgb(0, 0, 0));
let mut notes_on = [false;128];
for (x, time_start) in (0..clip.length).step_by(zoom).enumerate() {
for (_y, note) in (0..=127).rev().enumerate() {
if let Some(cell) = buf.get_mut(x, note) {
if notes_on[note] {
cell.set_char('▂');
cell.set_style(style);
}
}
let time_end = time_start + zoom;
for time in time_start..time_end.min(clip.length) {
for event in clip.notes[time].iter() {
}
let time_end = time_start + zoom;
for time in time_start..time_end.min(clip.length) {
if let Some(notes) = clip.notes.get(time) {
for event in notes.iter() {
match event {
MidiMessage::NoteOn { key, .. } => {
let note = key.as_int() as usize;