modularize grid view

This commit is contained in:
i do not exist 2026-09-17 16:53:28 +03:00
parent 922c2e1f2f
commit 5a34025277
4 changed files with 173 additions and 104 deletions

View file

@ -2,28 +2,28 @@ use crate::*;
pub fn get_dialog <'a> (_: &App, src: impl Language) -> Perhaps<Dialog> { pub fn get_dialog <'a> (_: &App, src: impl Language) -> Perhaps<Dialog> {
src.word()?.map(|word|Ok(match word { src.word()?.map(|word|Ok(match word {
":dialog/none" => Dialog::None, ":none" => Dialog::None,
":dialog/options" => Dialog::Options, ":options" => Dialog::Options,
":dialog/device" => Dialog::Device(0), ":device" => Dialog::Device(0),
":dialog/device/prev" => Dialog::Device(0), ":device/prev" => Dialog::Device(0),
":dialog/device/next" => Dialog::Device(0), ":device/next" => Dialog::Device(0),
":dialog/help" => Dialog::Help(0), ":help" => Dialog::Help(0),
":dialog/save" => Dialog::Browse( ":save" => Dialog::Browse(
BrowseTarget::SaveProject, BrowseTarget::SaveProject,
Browse::new(None).unwrap().into()), Browse::new(None).unwrap().into()),
":dialog/load" => Dialog::Browse( ":load" => Dialog::Browse(
BrowseTarget::LoadProject, BrowseTarget::LoadProject,
Browse::new(None).unwrap().into()), Browse::new(None).unwrap().into()),
":dialog/import/clip" => Dialog::Browse( ":import/clip" => Dialog::Browse(
BrowseTarget::ImportClip(Default::default()), BrowseTarget::ImportClip(Default::default()),
Browse::new(None).unwrap().into()), Browse::new(None).unwrap().into()),
":dialog/export/clip" => Dialog::Browse( ":export/clip" => Dialog::Browse(
BrowseTarget::ExportClip(Default::default()), BrowseTarget::ExportClip(Default::default()),
Browse::new(None).unwrap().into()), Browse::new(None).unwrap().into()),
":dialog/import/sample" => Dialog::Browse( ":import/sample" => Dialog::Browse(
BrowseTarget::ImportSample(Default::default()), BrowseTarget::ImportSample(Default::default()),
Browse::new(None).unwrap().into()), Browse::new(None).unwrap().into()),
":dialog/export/sample" => Dialog::Browse( ":export/sample" => Dialog::Browse(
BrowseTarget::ExportSample(Default::default()), BrowseTarget::ExportSample(Default::default()),
Browse::new(None).unwrap().into()), Browse::new(None).unwrap().into()),
_ => return Err( _ => return Err(
@ -89,6 +89,12 @@ impl Dialog {
modes: vec![":arranger".into()] modes: vec![":arranger".into()]
} }
}, },
AppCommand::Tracks {
command: TracksCommand::Add
},
AppCommand::Scenes {
command: ScenesCommand::Add
}
]), ]),
MenuItem::new(" Load session ", [ MenuItem::new(" Load session ", [

View file

@ -15,10 +15,11 @@
(info Mode selector.) (info Mode selector.)
(keys :axis/y :confirm) (keys :axis/y :confirm)
(view (view
(bar/n 2 (bsp/e :transport :status) (bar/s 2 (pad/x 1 :transport)
(align/c (bar/n 2 :status
(bar/s 2 (align/x (bg (g 12) :logo)) (align/c
:dialog/menu))))) (bar/s 2 (align/x (bg (g 12) :logo))
:dialog/menu))))))
(keys :back (@escape cancel)) (keys :back (@escape cancel))
(keys :confirm (@enter confirm)) (keys :confirm (@enter confirm))
@ -39,16 +40,28 @@
(mode :scene (keys :scene)) (mode :scene (keys :scene))
(mode :mix (keys :mix)) (mode :mix (keys :mix))
(view (view
(bar/n 2 (bsp/e :transport :status) (bar/n 2 :status
(bar/s 2 :tracks/outputs (bar/s 8
(bar/s 2 :tracks/devices (bar/e 16 (align/nw (pad/x 1 :transport))
(bar/s 2 :tracks/names (bar/w 16 (bar/s 2 (bar/w 4 :tracks/outputs/add :tracks/outputs/count)
(bar/n 2 :tracks/inputs (bar/s 2 (bar/w 4 :tracks/devices/add :tracks/devices/count)
(bar/e 16 :scenes/names :scenes/clips)))))))) (bar/s 2 (bar/w 4 :tracks/names/add :tracks/names/count)
(bar/w 4 :tracks/inputs/add :tracks/inputs/count))))
(align/x (bar/s 2 :tracks/outputs/cells
(bar/s 2 :tracks/devices/cells
(bar/s 2 :tracks/names/cells
:tracks/inputs/cells))))))
(bar/w 16 (text)
(bar/e 16 :scenes/names
:scenes/clips))))))
(keys :global (keys :global
(@f1 (dialog :help)) (@f1 (dialog :help))
(@f2 (project new))
(@f3 (dialog :history))
(@f6 (dialog :save))
(@f8 (dialog :options)) (@f8 (dialog :options))
(@f9 (dialog :load))
(@f10 (dialog :quit))) (@f10 (dialog :quit)))
(keys :clock (keys :clock
@ -56,10 +69,10 @@
(@shift/space (clock/toggle 0))) (@shift/space (clock/toggle 0)))
(keys :color (keys :color
(@c color)) (@c (color)))
(keys :launch (keys :launch
(@q launch)) (@q (launch)))
(keys :scenes (keys :scenes
(@shift/S (scenes add)) (@shift/S (scenes add))
@ -155,8 +168,8 @@
(@shift/R sampler/record/back)) (@shift/R sampler/record/back))
(keys :sampler/play (@p sampler/play/sample :sample/selected) (keys :sampler/play (@p sampler/play/sample :sample/selected)
(@P sampler/stop/sample :sample/selected)) (@P sampler/stop/sample :sample/selected))
(keys :sampler/import-export (@shift/f6 dialog :dialog/export/sample) (keys :sampler/import-export (@shift/f6 dialog :export/sample)
(@shift/f9 dialog :dialog/import/sample)) (@shift/f9 dialog :import/sample))
(keys :sampler/directions (@up sampler/select :sample/above) (keys :sampler/directions (@up sampler/select :sample/above)
(@down sampler/select :sample/below) (@down sampler/select :sample/below)
(@left sampler/select :sample/to/left) (@left sampler/select :sample/to/left)

View file

@ -369,34 +369,42 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra
/// Draw name of each track /// Draw name of each track
fn view_track_names (&self, theme: ItemTheme) -> impl Draw<Tui> { fn view_track_names (&self, theme: ItemTheme) -> impl Draw<Tui> {
let btn1t = self.view_track_count();
let btn1s = self.view_scene_count();
let btns1 = south(btn1t, btn1s);
let btns2 = south(self.view_track_add(), self.view_scene_add());
view_track_row_section(theme, view_track_row_section(theme,
btns1, self.view_track_names_count(),
btns2, self.view_track_names_add(),
bg(theme.darker.term, self.view_track_names_cells()
iter_east(||self.tracks_with_sizes().map(|(index, track, _x1, _x2)|{ )
let b = if self.selection().track() == Some(index) { }
track.color.light.term
} else { fn view_track_names_add (&self) -> impl Draw<Tui> {
track.color.base.term south(self.view_track_add(), self.view_scene_add())
}; }
bg(b, south(
east!( fn view_track_names_count (&self) -> impl Draw<Tui> {
"·t", south(self.view_track_count(), self.view_scene_count())
index, }
" ",
fg(Rgb(255, 255, 255), bold(true, &track.name)) fn view_track_names_cells <'a> (&'a self) -> impl Draw<Tui> {
) iter_east(||self.tracks_with_sizes().map(|(index, track, _x1, _x2)|{
.align_nw() let b = if self.selection().track() == Some(index) {
.full_w(), track.color.light.term
"" } else {
)) track.color.base.term
.exact_w(track_width(index, track)) };
.exact_h(2) bg(b, south(
})))) east!(
"·t",
index,
" ",
fg(Rgb(255, 255, 255), bold(true, &track.name))
)
.align_nw()
.full_w(),
""
))
.exact_w(track_width(index, track))
.exact_h(2)
}))
} }
/// Draw outputs per track /// Draw outputs per track
@ -404,52 +412,36 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra
view_track_row_section(theme, view_track_row_section(theme,
self.view_track_output_count(), self.view_track_output_count(),
self.view_track_output_add(), self.view_track_output_add(),
bg(theme.darker.term, self.view_track_output_cells()
iter_east(move||self.tracks_with_sizes().map(move|(index, track, _x1, _x2)|{ )
let f = Rgb(255, 255, 255);
let b = track.color.dark.term;
iter_south(move||track.sequencer.midi_outs.iter().map(move|port: &MidiOutput|{
fg(f, bg(b, east!("·o", index, " ", port.port_name()).full_w().align_w()).exact_h(1))
}))
.full_h()
.align_nw()
.exact_w(track_width(index, track))
}))))
} }
/// Draw inputs per track fn view_track_output_cells <'a> (&'a self) -> impl Draw<Tui> {
fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Draw<Tui> { iter_east(move||self.tracks_with_sizes().map(move|(index, track, _x1, _x2)|{
let mut height = 0u16; let f = Rgb(255, 255, 255);
for track in self.tracks().iter() { let b = track.color.dark.term;
height = height.max(track.sequencer.midi_ins.len() as u16); iter_south(move||track.sequencer.midi_outs.iter().map(move|port: &MidiOutput|{
} fg(f, bg(b, east!("·o", index, " ", port.port_name()).full_w().align_w()).exact_h(1))
view_track_row_section(theme, }))
self.view_track_input_count(), .full_h()
self.view_track_input_add(), .align_nw()
bg(theme.darker.term, iter_east(move||self .exact_w(track_width(index, track))
.tracks_with_sizes() }))
.map(move|(index, track, _x1, _x2)|{ }
south(
bg(track.color.base.term, fn view_track_device_count (&self) -> impl Draw<Tui> {
east!( button_3("d", "evice", self.track().map(|t|t.devices.len()).unwrap_or(0), false).min_h(2)
either(track.sequencer.monitoring, fg(Green, "●mon "), "·mon "),
either(track.sequencer.recording, fg(Red, "●rec "), "·rec "),
either(track.sequencer.overdub, fg(Yellow, "●dub "), "·dub "),
).align_w().full_w()),
iter_south(move||track.sequencer.midi_ins.iter().map(move|port|fg_bg(
Rgb(255, 255, 255),
track.color.dark.term,
east!("·i", index, " ", port.port_name()).align_w().full_w()))))
.align_nw()
.exact_wh(track_width(index, track), height + 1)
}))).align_w())
} }
fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<Tui> { fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<Tui> {
let height = self.tracks_devices_height(); let height = self.tracks_devices_height();
let btn1 = button_3("d", "evice", self.track().map(|t|t.devices.len()).unwrap_or(0), false).min_h(2); let btn1 = self.view_track_device_count();
let btn2 = self.view_track_device_add(); let btn2 = self.view_track_device_add();
view_track_row_section(theme, btn1, btn2, iter_east(move||self.tracks_with_sizes() view_track_row_section(theme, btn1, btn2, self.view_track_device_cells(height))
}
fn view_track_device_cells (&self, height: u16) -> impl Draw<Tui> {
iter_east(move||self.tracks_with_sizes()
.enumerate() .enumerate()
.map(move|(index, (_, track, _x1, _x2))|bg( .map(move|(index, (_, track, _x1, _x2))|bg(
track.color.dark.term, track.color.dark.term,
@ -463,7 +455,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra
.exact_wh( .exact_wh(
Some(track_width(index, track)), Some(track_width(index, track)),
Some(height + 1), Some(height + 1),
)))) )))
} }
fn tracks_devices_height (&self) -> u16 { fn tracks_devices_height (&self) -> u16 {
@ -474,10 +466,51 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra
h as u16 h as u16
} }
fn view_inputs (&self, _theme: ItemTheme) -> impl Draw<Tui> + '_ { fn view_inputs (&self) -> impl Draw<Tui> + '_ {
let title_1 = button_3("i", "nput ", self.midi_ins().len(), false).align_w().exact_wh(20, 1); let title_1 = button_3("i", "nput ", self.midi_ins().len(), false).align_w().exact_wh(20, 1);
let title_2 = self.view_track_input_add().exact_wh(4, 1); let title_2 = self.view_track_input_add().exact_wh(4, 1);
east(title_1, west(title_2, draw(move|to: &mut Tui|{ east(title_1, west(title_2, self.view_input_cells()))
}
fn tracks_inputs_height (&self) -> u16 {
let mut height = 0u16;
for track in self.tracks().iter() {
height = height.max(track.sequencer.midi_ins.len() as u16);
}
height
}
/// Draw inputs per track
fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Draw<Tui> {
view_track_row_section(theme,
self.view_track_input_count(),
self.view_track_input_add(),
self.view_track_input_cells(self.tracks_inputs_height())
)
}
fn view_track_input_cells (&self, height: u16) -> impl Draw<Tui> {
iter_east(move||self
.tracks_with_sizes()
.map(move|(index, track, _x1, _x2)|{
south(
bg(track.color.base.term,
east!(
either(track.sequencer.monitoring, fg(Green, "●mon "), "·mon "),
either(track.sequencer.recording, fg(Red, "●rec "), "·rec "),
either(track.sequencer.overdub, fg(Yellow, "●dub "), "·dub "),
).align_w().full_w()),
iter_south(move||track.sequencer.midi_ins.iter().map(move|port|fg_bg(
Rgb(255, 255, 255),
track.color.dark.term,
east!("·i", index, " ", port.port_name()).align_w().full_w()))))
.align_nw()
.exact_wh(track_width(index, track), height + 1)
})).align_w()
}
fn view_input_cells (&self) -> impl Draw<Tui> {
draw(move|to: &mut Tui|{
for (_index, track, x1, _x2) in self.tracks_with_sizes() { for (_index, track, x1, _x2) in self.tracks_with_sizes() {
let _ = south( let _ = south(
bg(track.color.dark.term, east!( bg(track.color.dark.term, east!(
@ -502,12 +535,12 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra
) )
).push_x(index as u16 * 10).exact_h(1).draw(to)?; ).push_x(index as u16 * 10).exact_h(1).draw(to)?;
} }
todo!() Ok(None)
}) })
).draw(to)?; ).draw(to)?;
} }
Ok(Some(to.area())) Ok(Some(to.area()))
}))) })
} }
fn outputs_height (&self) -> u16 { fn outputs_height (&self) -> u16 {

View file

@ -262,13 +262,30 @@ impl View<Tui, App> {
Some("output") => Self::boxed(move|_, to|bg(Rgb(30, 30, 30), "Output Meters".align_s().full_h()).draw(to)), Some("output") => Self::boxed(move|_, to|bg(Rgb(30, 30, 30), "Output Meters".align_s().full_h()).draw(to)),
_ => panic!() _ => panic!()
}, },
Some(":tracks") => match word.split("/").skip(1).next() { Some(":tracks") => match &word[(":tracks".len()+1)..] {
None => Self::boxed(move|_, to|"TODO tracks".draw(to)), "" => Self::boxed(move|_, to|"TODO tracks".draw(to)),
Some("names") => draw!(|app|app.project.view_track_names(app.color.clone())),
Some("inputs") => draw!(|app|app.project.view_track_inputs(app.color.clone())), "names" => draw!(|app|app.project.view_track_names(app.color.clone())),
Some("devices") => draw!(|app|app.project.view_track_devices(app.color.clone())), "names/count" => draw!(|app|app.project.view_track_names_count()),
Some("outputs") => draw!(|app|app.project.view_track_outputs(app.color.clone(), 0)), "names/cells" => draw!(|app|app.project.view_track_names_cells()),
_ => panic!() "names/add" => draw!(|app|app.project.view_track_names_add()),
"devices" => draw!(|app|app.project.view_track_devices(app.color.clone())),
"devices/count" => draw!(|app|app.project.view_track_device_count()),
"devices/cells" => draw!(|app|app.project.view_track_device_cells(app.tracks_devices_height())),
"devices/add" => draw!(|app|app.project.view_track_device_add()),
"inputs" => draw!(|app|app.project.view_track_inputs(app.color.clone())),
"inputs/count" => draw!(|app|app.project.view_track_input_count()),
"inputs/cells" => draw!(|app|app.project.view_track_input_cells(app.tracks_inputs_height())),
"inputs/add" => draw!(|app|app.project.view_track_input_add()),
"outputs" => draw!(|app|app.project.view_track_outputs(app.color.clone(), 0)),
"outputs/count" => draw!(|app|app.project.view_track_output_count()),
"outputs/cells" => draw!(|app|app.project.view_track_output_cells()),
"outputs/add" => draw!(|app|app.project.view_track_output_add()),
_ => panic!("unknown: {word}")
}, },
Some(":scenes") => match word.split("/").skip(1).next() { Some(":scenes") => match word.split("/").skip(1).next() {
Some("clips") => draw!(|app|app.view_scenes_clips()), Some("clips") => draw!(|app|app.view_scenes_clips()),