mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-10-08 18:56:42 +02:00
piano keys reappear
This commit is contained in:
parent
9db60c3429
commit
9a50ce6613
15 changed files with 856 additions and 782 deletions
559
src/tek_view.rs
559
src/tek_view.rs
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue