use crate::*; content!(TuiOut:|self: App|VIEW[":view"](self));//if let Ok(Some(view)) = VIEW[":view"] { view(self) } else { panic!() }); struct DslNs<'t, T: 't>(&'t [(&'t str, T)]); type DslCb = fn (&App) -> Box>; impl<'t, D: Dsl> std::ops::Index for DslNs<'t, DslCb> { type Output = DslCb; fn index (&self, index: D) -> &Self::Output { if let Ok(Some(symbol)) = index.src() { for (key, value) in self.0.iter() { if symbol == *key { return value } } } &(view_nil as DslCb) } } pub const VIEW: DslNs<'static, DslCb> = DslNs(&[ (":view", |state|Box::new( Fill::xy(Bsp::a(VIEW[":view/content"](state), VIEW[":view/overlay"](state))))), (":view/content", |state|Box::new( Fill::xy(ErrorBoundary::new(Ok(Some("ยท")))))), (":view/overlay", |state|match &state.dialog { Dialog::Menu(_) => VIEW[":view/menu"](state), Dialog::Device(_) => VIEW[":view/device"](state), Dialog::Browser(_, _) => VIEW[":view/browse"](state), //Dialog::Help(_) => view_help, //Dialog::Message(_) => view_message, //Dialog::Options => view_options, _ => unimplemented!() }), (":view/browse", |state|{ let browser = state.dialog.browser().cloned().unwrap(); Box::new(Bsp::s( Padding::xy(3, 1, VIEW[":view/browse-title"](state)), Outer(true, Style::default().fg(Tui::g(96))) .enclose(Fill::xy(browser)))) }), (":view/browse-title", |state: &App|{ let target = state.dialog.browser_target().unwrap(); Box::new(Fill::x(Align::w(FieldV(Default::default(), match target { BrowserTarget::SaveProject => "Save project:", BrowserTarget::LoadProject => "Load project:", BrowserTarget::ImportSample(_) => "Import sample:", BrowserTarget::ExportSample(_) => "Export sample:", BrowserTarget::ImportClip(_) => "Import clip:", BrowserTarget::ExportClip(_) => "Export clip:", }, Shrink::x(3, Fixed::y(1, Tui::fg(Tui::g(96), RepeatH("๐Ÿญป"))))))))}), (":view/device", |state: &App|{ let selected = state.dialog.device_selected().unwrap(); Box::new(Bsp::s(Tui::bold(true, "Add device"), Map::south(1, move||device_kinds().iter(), move|label: &&'static str, i|{ let bg = if i == selected { Rgb(64,128,32) } else { Rgb(0,0,0) }; let lb = if i == selected { "[ " } else { " " }; let rb = if i == selected { " ]" } else { " " }; Fill::x(Tui::bg(bg, Bsp::e(lb, Bsp::w(rb, "FIXME device name")))) }))) }), (":view/menu", |state|{ let selected = state.dialog.menu_selected(); let outputs = Fill::x(Fixed::y(3, Bsp::a(Fill::x(Align::w(" L AUDIO OUTS")), Bsp::a("MIDI OUT", Fill::x(Align::e("AUDIO OUTS R ")))))); let inputs = Fill::x(Fixed::y(3, Bsp::a(Fill::x(Align::w(" L AUDIO INS")), Bsp::a("MIDI INS", Fill::x(Align::e("AUDIO INS R ")))))); Box::new(Tui::bg(Rgb(0,0,0), Bsp::s(outputs, Bsp::s( Fill::x(Fixed::y(3, Tui::bg(Rgb(33,33,33), Tui::bold(true, "tek 0.3.0-rc0")))), Bsp::n(inputs, Bsp::n( Fill::x(Fixed::y(3, Tui::bg(Rgb(33,33,33), Bsp::e(Tui::fg(Rgb(255,192,48), "[Enter]"), " new session")))), Fill::y(Align::n(Fill::x(VIEW[":view/profiles"](state)))))))))) }), (":view/profiles", |state|Box::new({ let views = state.config.views.clone(); Stack::south(move|add: &mut dyn FnMut(&dyn Render)|{ for (index, (id, profile)) in views.read().unwrap().iter().enumerate() { let bg = if index == 0 { Rgb(64,64,64) } else { Rgb(32,32,32) }; let name = profile.name.as_ref().map(|x|unquote(unquote(x.as_ref()))); let info = profile.info.as_ref().map(|x|unquote(unquote(x.as_ref()))); add(&Fixed::y(3, Tui::bg(bg, Bsp::s( Fill::x(Bsp::a( Fill::x(Align::w(Tui::fg(Rgb(224,192,128), name))), Fill::x(Align::e(Tui::fg(Rgb(224,128,32), &id))))), Fill::x(Align::w(info)))))); } })})) //(":view/options", view_options), ]); fn view_nil (_: &App) -> Box> { "nil".boxed() } fn wrap_dialog (dialog: impl Content) -> impl Content { 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 { fn h_scenes (&self) -> u16 { (self.height() as u16).saturating_sub(20) } fn w_side (&self) -> u16 { 20 } fn w_mid (&self) -> u16 { (self.width() as u16).saturating_sub(self.w_side()) } } //Bsp::s("", //Map::south(1, //move||state.config.binds.layers.iter() //.filter_map(|a|(a.0)(state).then_some(a.1)) //.flat_map(|a|a) //.filter_map(|x|if let Value::Exp(_, iter)=x.value{ Some(iter) } else { None }) //.skip(offset) //.take(20), //|mut b,i|Fixed::x(60, Align::w(Bsp::e("(", Bsp::e( //b.next().map(|t|Fixed::x(16, Align::w(Tui::fg(Rgb(64,224,0), format!("{}", t.value))))), //Bsp::e(" ", Align::w(format!("{}", b.0.0.trim()))))))))))), //Dialog::Browser(BrowserTarget::Load, browser) => { //"bobcat".boxed() ////Bsp::s( ////Fill::x(Align::w(Margin::xy(1, 1, Bsp::e( ////Tui::bold(true, " Load project: "), ////Shrink::x(3, Fixed::y(1, RepeatH("๐Ÿญป"))))))), ////Outer(true, Style::default().fg(Tui::g(96))) ////.enclose(Fill::xy(browser))) //}, //Dialog::Browser(BrowserTarget::Export, browser) => { //"bobcat".boxed() ////Bsp::s( ////Fill::x(Align::w(Margin::xy(1, 1, Bsp::e( ////Tui::bold(true, " Export: "), ////Shrink::x(3, Fixed::y(1, RepeatH("๐Ÿญป"))))))), ////Outer(true, Style::default().fg(Tui::g(96))) ////.enclose(Fill::xy(browser))) //}, //Dialog::Browser(BrowserTarget::Import, browser) => { //"bobcat".boxed() ////Bsp::s( ////Fill::x(Align::w(Margin::xy(1, 1, Bsp::e( ////Tui::bold(true, " Import: "), ////Shrink::x(3, Fixed::y(1, RepeatH("๐Ÿญป"))))))), ////Outer(true, Style::default().fg(Tui::g(96))) ////.enclose(Fill::xy(browser))) //}, // //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_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 (&self) -> impl Content { //self.update_clock(); //let theme = self.color; //let clock = self.clock(); //let playing = clock.is_rolling(); //let cache = clock.view_cache.clone(); ////let selection = self.selection().describe(self.tracks(), self.scenes()); //let hist_len = self.history.len(); //let hist_last = self.history.last(); //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 //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(" โ–—โ–„โ–– ", " โ–โ–€โ–˜ ",)))) //) //) //))); //add(&" "); //{ //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(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) = 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()))))) //} ////let options = ||["Projects", "Settings", "Help", "Quit"].iter(); ////let option = |a,i|Tui::fg(Rgb(255,255,255), format!("{}", a)); ////Bsp::s(Tui::bold(true, "tek!"), Bsp::s("", Map::south(1, options, option)))