diff --git a/crates/app/src/model.rs b/crates/app/src/model.rs index 26ad7ee1..48285fc1 100644 --- a/crates/app/src/model.rs +++ b/crates/app/src/model.rs @@ -158,9 +158,6 @@ impl App { #[tengri_proc::expose] impl App { - fn _todo_bool_stub (&self) -> bool { - todo!() - } fn _todo_isize_stub (&self) -> isize { todo!() } diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index e059a3ec..f590466a 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -2,49 +2,49 @@ use crate::*; pub(crate) use std::fmt::Write; pub(crate) use ::tengri::tui::ratatui::prelude::Position; -// Thunks can be natural error boundaries! -struct ErrorBoundary>(std::marker::PhantomData, Perhaps); -impl> Content for ErrorBoundary { - fn content (&self) -> impl Render + '_ { - ThunkRender::new(|to|match self.1.as_ref() { - Ok(Some(content)) => content.render(to), - Ok(None) => to.blit(&"empty?", 0, 0, Some(Style::default().yellow())), - Err(e) => Content::render(&Tui::fg_bg( - Rgb(255,224,244), - Rgb(96,24,24), - Bsp::s( - Bsp::e(Tui::bold(true, "oops. "), "rendering failed."), - Bsp::e("\"why?\" ", Tui::bold(true, &format!("{e}"))), - )), to) - }) +impl App { + pub fn view (&self) -> impl Content + '_ { + let view: Perhaps>> = + Namespace::take_from(self, &mut self.config.view.clone()); + Either(view.is_ok(), + ThunkRender::new(move|to|if let Some(view) = view.as_ref().unwrap().as_ref() { + Content::render(view, to) + }), + "error?!") + //Fill::xy(self.size.of(col! { + //Tui::bg(Rgb(72,72,0), When(matches!(&view, Err(_)), Bsp::s( + //Fixed::y(1, "view error"), + //Bsp::s( + //view.as_ref().err().map(|e|format!("{e}")), + //format!("{}", &self.config.view.0.0))))), + //When(matches!(&view, Ok(None)), Bsp::s( + //Tui::bg(Rgb(96,48,0), + //Fixed::y(1, Fill::x(Align::w("no view returned, as defined by:")))), + //Tui::bg(Rgb(72,32,0), + //Fill::x(Stack::south(|add: &mut dyn FnMut(&dyn Render)|{ + //for line in self.config.view.0.0.split('\n') { + //add(&Fill::x(Align::w(line))); + //} + //}))))), + //When(matches!(&view, Ok(Some(_))), &view.unwrap().unwrap()), + //})) } } #[tengri_proc::view(TuiOut)] impl App { - pub fn view (app: &Self) -> impl Content { - ErrorBoundary:: + '_>>( - Default::default(), - Namespace::take_from(app, &mut app.config.view.clone()) - ) - } - pub fn view_nil (app: &Self) -> impl Content { + pub fn view_nil (&self) -> impl Content + use<'_> { "nil" } - pub fn view_history (app: &Self) -> impl Content { - Fixed::y(1, Fill::x(Align::w(FieldH(app.color, - format!("History ({})", app.history.len()), - app.history.last().map(|last|Fill::x(Align::w(format!("{:?}", last.0)))))))) + pub fn view_history (&self) -> impl Content { + Fixed::y(1, Fill::x(Align::w(FieldH(self.color, + format!("History ({})", self.history.len()), + self.history.last().map(|last|Fill::x(Align::w(format!("{:?}", last.0)))))))) } - pub fn view_status_h2 (app: &Self) -> impl Content { - app.update_clock(); - let theme = app.color; - let clock = app.clock(); - let playing = clock.is_rolling(); - let cache = clock.view_cache.clone(); - //let selection = app.selection().describe(app.tracks(), app.scenes()); - let hist_len = app.history.len(); - let hist_last = app.history.last(); + pub fn view_status_h2 (&self) -> impl Content + use<'_> { + self.update_clock(); + let theme = self.color; + let playing = self.clock().is_rolling(); Fixed::y(2, Stack::east(move|add: &mut dyn FnMut(&dyn Render)|{ add(&Fixed::x(5, Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, Either::new(false, // TODO @@ -60,7 +60,7 @@ impl App { ))); add(&" "); { - let cache = cache.read().unwrap(); + let cache = self.project.clock.view_cache.read().unwrap(); add(&Fixed::x(15, Align::w(Bsp::s( FieldH(theme, "Beat", cache.beat.view.clone()), FieldH(theme, "Time", cache.time.view.clone()), @@ -73,143 +73,145 @@ impl App { Fill::x(Align::w(FieldH(theme, "Buf", cache.buf.view.clone()))), Fill::x(Align::w(FieldH(theme, "Lat", cache.lat.view.clone()))), )))); - //add(&Bsp::s( - ////Fill::x(Align::w(FieldH(theme, "Selected", Align::w(selection)))), - //Fill::x(Align::w(FieldH(theme, format!("History ({})", hist_len), - //hist_last.map(|last|Fill::x(Align::w(format!("{:?}", last.0))))))), - //"" - //)); - ////if let Some(last) = app.history.last() { - ////add(&FieldV(theme, format!("History ({})", app.history.len()), - ////Fill::x(Align::w(format!("{:?}", last.0))))); - ////} + add(&Bsp::s( + Fill::x(Align::w(FieldH(theme, "Selected", Align::w(self.selection().describe( + self.tracks(), + self.scenes() + ))))), + Fill::x(Align::w(FieldH(theme, format!("History ({})", self.history.len()), + self.history.last().map(|last|Fill::x(Align::w(format!("{:?}", last.0))))))) + )); + //if let Some(last) = self.history.last() { + //add(&FieldV(theme, format!("History ({})", self.history.len()), + //Fill::x(Align::w(format!("{:?}", last.0))))); + //} } })) } - //pub fn view_status_v (&self) -> impl Content + use<'_> { - //self.update_clock(); - //let cache = self.project.clock.view_cache.read().unwrap(); - //let theme = self.color; - //let playing = self.clock().is_rolling(); - //Tui::bg(theme.darker.rgb, Fixed::xy(20, 5, Outer(true, Style::default().fg(Tui::g(96))).enclose( - //col!( - //Fill::x(Align::w(Bsp::e( - //Align::w(Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, - //Either::new(false, // TODO - //Thunk::new(move||Fixed::x(9, Either::new(playing, - //Tui::fg(Rgb(0, 255, 0), " PLAYING "), - //Tui::fg(Rgb(255, 128, 0), " STOPPED "))) - //), - //Thunk::new(move||Fixed::x(5, Either::new(playing, - //Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), - //Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))) - //) - //) - //)), - //Bsp::s( - //FieldH(theme, "Beat", cache.beat.view.clone()), - //FieldH(theme, "Time", cache.time.view.clone()), - //), - //))), - //Fill::x(Align::w(FieldH(theme, "BPM", cache.bpm.view.clone()))), - //Fill::x(Align::w(FieldH(theme, "SR ", cache.sr.view.clone()))), - //Fill::x(Align::w(FieldH(theme, "Buf", Bsp::e(cache.buf.view.clone(), Bsp::e(" = ", cache.lat.view.clone()))))), - //)))) - //} - //pub fn view_status (&self) -> impl Content + use<'_> { - //self.update_clock(); - //let cache = self.project.clock.view_cache.read().unwrap(); - //view_status(Some(self.project.selection.describe(self.tracks(), self.scenes())), - //cache.sr.view.clone(), cache.buf.view.clone(), cache.lat.view.clone()) - //} - //pub fn view_transport (&self) -> impl Content + use<'_> { - //self.update_clock(); - //let cache = self.project.clock.view_cache.read().unwrap(); - //view_transport(self.project.clock.is_rolling(), - //cache.bpm.view.clone(), cache.beat.view.clone(), cache.time.view.clone()) - //} - //pub fn view_editor (&self) -> impl Content + use<'_> { - //let bg = self.editor() - //.and_then(|editor|editor.clip().clone()) - //.map(|clip|clip.read().unwrap().color.darker) - //.unwrap_or(self.color.darker); - //Fill::xy(Tui::bg(bg.rgb, self.editor())) - //} - //pub fn view_editor_status (&self) -> impl Content + use<'_> { - //self.editor().map(|e|Fixed::x(20, Outer(true, Style::default().fg(Tui::g(96))).enclose( - //Fill::y(Align::n(Bsp::s(e.clip_status(), e.edit_status())))))) - //} - //pub fn view_midi_ins_status (&self) -> impl Content + use<'_> { - //self.project.view_midi_ins_status(self.color) - //} - //pub fn view_midi_outs_status (&self) -> impl Content + use<'_> { - //self.project.view_midi_outs_status(self.color) - //} - //pub fn view_audio_ins_status (&self) -> impl Content + use<'_> { - //self.project.view_audio_ins_status(self.color) - //} - //pub fn view_audio_outs_status (&self) -> impl Content + use<'_> { - //self.project.view_audio_outs_status(self.color) - //} - //pub fn view_scenes (&self) -> impl Content + use<'_> { - //Bsp::e( - //Fixed::x(20, Align::nw(self.project.view_scenes_names())), - //self.project.view_scenes_clips(), - //) - //} - //pub fn view_scenes_names (&self) -> impl Content + use<'_> { - //self.project.view_scenes_names() - //} - //pub fn view_scenes_clips (&self) -> impl Content + use<'_> { - //self.project.view_scenes_clips() - //} - //pub fn view_tracks_inputs <'a> (&'a self) -> impl Content + use<'a> { - //Fixed::y(1 + self.project.midi_ins.len() as u16, - //self.project.view_inputs(self.color)) - //} - //pub fn view_tracks_outputs <'a> (&'a self) -> impl Content + use<'a> { - //self.project.view_outputs(self.color) - //} - //pub fn view_tracks_devices <'a> (&'a self) -> impl Content + use<'a> { - //Fixed::y(4, self.project.view_track_devices(self.color)) - //} - //pub fn view_tracks_names <'a> (&'a self) -> impl Content + use<'a> { - //Fixed::y(2, self.project.view_track_names(self.color)) - //} - //pub fn view_pool (&self) -> impl Content + use<'_> { - //Fixed::x(20, Bsp::s( - //Fill::x(Align::w(FieldH(self.color, "Clip pool:", ""))), - //Fill::y(Align::n(Tui::bg(Rgb(0, 0, 0), Outer(true, Style::default().fg(Tui::g(96))) - //.enclose(PoolView(&self.pool))))))) - //} - //pub fn view_samples_keys (&self) -> impl Content + use<'_> { - //self.project.sampler().map(|s|s.view_list(true, self.editor().unwrap())) - //} - //pub fn view_samples_grid (&self) -> impl Content + use<'_> { - //self.project.sampler().map(|s|s.view_grid()) - //} - //pub fn view_sample_viewer (&self) -> impl Content + use<'_> { - //self.project.sampler().map(|s|s.view_sample(self.editor().unwrap().get_note_pos())) - //} - //pub fn view_sample_info (&self) -> impl Content + use<'_> { - //self.project.sampler().map(|s|s.view_sample_info(self.editor().unwrap().get_note_pos())) - //} - //pub fn view_sample_status (&self) -> impl Content + use<'_> { - //self.project.sampler().map(|s|Outer(true, Style::default().fg(Tui::g(96))).enclose( - //Fill::y(Align::n(s.view_sample_status(self.editor().unwrap().get_note_pos()))))) - //} - //pub fn view_meters_input (&self) -> impl Content + use<'_> { - //self.project.sampler().map(|s|s.view_meters_input()) - //} - //pub fn view_meters_output (&self) -> impl Content + use<'_> { - //self.project.sampler().map(|s|s.view_meters_output()) - //} - //pub fn view_dialog (&self) -> impl Content + use<'_> { - //self.dialog.as_ref().map(|dialog|Bsp::b("", - //Fixed::xy(70, 23, Tui::fg_bg(Rgb(255,255,255), Rgb(16,16,16), Bsp::b( - //Repeat(" "), Outer(true, Style::default().fg(Tui::g(96))) - //.enclose(dialog)))))) - //} + pub fn view_status_v (&self) -> impl Content + use<'_> { + self.update_clock(); + let cache = self.project.clock.view_cache.read().unwrap(); + let theme = self.color; + let playing = self.clock().is_rolling(); + Tui::bg(theme.darker.rgb, Fixed::xy(20, 5, Outer(true, Style::default().fg(Tui::g(96))).enclose( + col!( + Fill::x(Align::w(Bsp::e( + Align::w(Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, + Either::new(false, // TODO + Thunk::new(move||Fixed::x(9, Either::new(playing, + Tui::fg(Rgb(0, 255, 0), " PLAYING "), + Tui::fg(Rgb(255, 128, 0), " STOPPED "))) + ), + Thunk::new(move||Fixed::x(5, Either::new(playing, + Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), + Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))) + ) + ) + )), + Bsp::s( + FieldH(theme, "Beat", cache.beat.view.clone()), + FieldH(theme, "Time", cache.time.view.clone()), + ), + ))), + Fill::x(Align::w(FieldH(theme, "BPM", cache.bpm.view.clone()))), + Fill::x(Align::w(FieldH(theme, "SR ", cache.sr.view.clone()))), + Fill::x(Align::w(FieldH(theme, "Buf", Bsp::e(cache.buf.view.clone(), Bsp::e(" = ", cache.lat.view.clone()))))), + )))) + } + pub fn view_status (&self) -> impl Content + use<'_> { + self.update_clock(); + let cache = self.project.clock.view_cache.read().unwrap(); + view_status(Some(self.project.selection.describe(self.tracks(), self.scenes())), + cache.sr.view.clone(), cache.buf.view.clone(), cache.lat.view.clone()) + } + pub fn view_transport (&self) -> impl Content + use<'_> { + self.update_clock(); + let cache = self.project.clock.view_cache.read().unwrap(); + view_transport(self.project.clock.is_rolling(), + cache.bpm.view.clone(), cache.beat.view.clone(), cache.time.view.clone()) + } + pub fn view_editor (&self) -> impl Content + use<'_> { + let bg = self.editor() + .and_then(|editor|editor.clip().clone()) + .map(|clip|clip.read().unwrap().color.darker) + .unwrap_or(self.color.darker); + Fill::xy(Tui::bg(bg.rgb, self.editor())) + } + pub fn view_editor_status (&self) -> impl Content + use<'_> { + self.editor().map(|e|Fixed::x(20, Outer(true, Style::default().fg(Tui::g(96))).enclose( + Fill::y(Align::n(Bsp::s(e.clip_status(), e.edit_status())))))) + } + pub fn view_midi_ins_status (&self) -> impl Content + use<'_> { + self.project.view_midi_ins_status(self.color) + } + pub fn view_midi_outs_status (&self) -> impl Content + use<'_> { + self.project.view_midi_outs_status(self.color) + } + pub fn view_audio_ins_status (&self) -> impl Content + use<'_> { + self.project.view_audio_ins_status(self.color) + } + pub fn view_audio_outs_status (&self) -> impl Content + use<'_> { + self.project.view_audio_outs_status(self.color) + } + pub fn view_scenes (&self) -> impl Content + use<'_> { + Bsp::e( + Fixed::x(20, Align::nw(self.project.view_scenes_names())), + self.project.view_scenes_clips(), + ) + } + pub fn view_scenes_names (&self) -> impl Content + use<'_> { + self.project.view_scenes_names() + } + pub fn view_scenes_clips (&self) -> impl Content + use<'_> { + self.project.view_scenes_clips() + } + pub fn view_tracks_inputs <'a> (&'a self) -> impl Content + use<'a> { + Fixed::y(1 + self.project.midi_ins.len() as u16, + self.project.view_inputs(self.color)) + } + pub fn view_tracks_outputs <'a> (&'a self) -> impl Content + use<'a> { + self.project.view_outputs(self.color) + } + pub fn view_tracks_devices <'a> (&'a self) -> impl Content + use<'a> { + Fixed::y(4, self.project.view_track_devices(self.color)) + } + pub fn view_tracks_names <'a> (&'a self) -> impl Content + use<'a> { + Fixed::y(2, self.project.view_track_names(self.color)) + } + pub fn view_pool (&self) -> impl Content + use<'_> { + Fixed::x(20, Bsp::s( + Fill::x(Align::w(FieldH(self.color, "Clip pool:", ""))), + Fill::y(Align::n(Tui::bg(Rgb(0, 0, 0), Outer(true, Style::default().fg(Tui::g(96))) + .enclose(PoolView(&self.pool))))))) + } + pub fn view_samples_keys (&self) -> impl Content + use<'_> { + self.project.sampler().map(|s|s.view_list(true, self.editor().unwrap())) + } + pub fn view_samples_grid (&self) -> impl Content + use<'_> { + self.project.sampler().map(|s|s.view_grid()) + } + pub fn view_sample_viewer (&self) -> impl Content + use<'_> { + self.project.sampler().map(|s|s.view_sample(self.editor().unwrap().get_note_pos())) + } + pub fn view_sample_info (&self) -> impl Content + use<'_> { + self.project.sampler().map(|s|s.view_sample_info(self.editor().unwrap().get_note_pos())) + } + pub fn view_sample_status (&self) -> impl Content + use<'_> { + self.project.sampler().map(|s|Outer(true, Style::default().fg(Tui::g(96))).enclose( + Fill::y(Align::n(s.view_sample_status(self.editor().unwrap().get_note_pos()))))) + } + pub fn view_meters_input (&self) -> impl Content + use<'_> { + self.project.sampler().map(|s|s.view_meters_input()) + } + pub fn view_meters_output (&self) -> impl Content + use<'_> { + self.project.sampler().map(|s|s.view_meters_output()) + } + pub fn view_dialog (&self) -> impl Content + use<'_> { + self.dialog.as_ref().map(|dialog|Bsp::b("", + Fixed::xy(70, 23, Tui::fg_bg(Rgb(255,255,255), Rgb(16,16,16), Bsp::b( + Repeat(" "), Outer(true, Style::default().fg(Tui::g(96))) + .enclose(dialog)))))) + } } impl ScenesView for App { diff --git a/crates/device/src/clock/clock_view.rs b/crates/device/src/clock/clock_view.rs index 039b5ea4..09b6561d 100644 --- a/crates/device/src/clock/clock_view.rs +++ b/crates/device/src/clock/clock_view.rs @@ -132,33 +132,4 @@ impl ViewCache { cache.bpm.update(None, rewrite!(buf, "{}", ViewCache::BPM_EMPTY)); } } - - //pub fn view_h2 (&self) -> impl Content { - //let cache = self.project.clock.view_cache.clone(); - //let cache = cache.read().unwrap(); - //add(&Fixed::x(15, Align::w(Bsp::s( - //FieldH(theme, "Beat", cache.beat.view.clone()), - //FieldH(theme, "Time", cache.time.view.clone()), - //)))); - //add(&Fixed::x(13, Align::w(Bsp::s( - //Fill::x(Align::w(FieldH(theme, "BPM", cache.bpm.view.clone()))), - //Fill::x(Align::w(FieldH(theme, "SR ", cache.sr.view.clone()))), - //)))); - //add(&Fixed::x(12, Align::w(Bsp::s( - //Fill::x(Align::w(FieldH(theme, "Buf", cache.buf.view.clone()))), - //Fill::x(Align::w(FieldH(theme, "Lat", cache.lat.view.clone()))), - //)))); - //add(&Bsp::s( - //Fill::x(Align::w(FieldH(theme, "Selected", Align::w(self.selection().describe( - //self.tracks(), - //self.scenes() - //))))), - //Fill::x(Align::w(FieldH(theme, format!("History ({})", self.history.len()), - //self.history.last().map(|last|Fill::x(Align::w(format!("{:?}", last.0))))))) - //)); - ////if let Some(last) = self.history.last() { - ////add(&FieldV(theme, format!("History ({})", self.history.len()), - ////Fill::x(Align::w(format!("{:?}", last.0))))); - ////} - //} } diff --git a/deps/tengri b/deps/tengri index a4a1066f..2048dd22 160000 --- a/deps/tengri +++ b/deps/tengri @@ -1 +1 @@ -Subproject commit a4a1066f187e6b2e119e3c3b471219f1e128d4af +Subproject commit 2048dd2263fc4389c8f510bfd12c851efe34026f