From 7ff1d989a9e2de925157177b699f3c8ab7ea2dc5 Mon Sep 17 00:00:00 2001 From: okay stopped screaming Date: Sat, 21 Mar 2026 20:33:51 +0200 Subject: [PATCH] well, 55 errors until up to date and then we fix --- app/tek.rs | 159 +++++++++---------- app/tek_impls.rs | 386 ++++++++++++++++++++++------------------------ app/tek_struct.rs | 14 +- app/tek_trait.rs | 116 +++++++------- tengri | 2 +- 5 files changed, 333 insertions(+), 344 deletions(-) diff --git a/app/tek.rs b/app/tek.rs index d7279804..828827f1 100644 --- a/app/tek.rs +++ b/app/tek.rs @@ -22,10 +22,13 @@ pub(crate) use tengri::{ *, lang::*, play::*, + keys::*, sing::*, time::*, draw::*, - tui::*, + term::*, + color::*, + space::*, crossterm::event::{Event, KeyEvent}, ratatui::{ self, @@ -953,13 +956,13 @@ mod view { /// ``` /// let _ = tek::view_logo(); /// ``` - pub fn view_logo () -> impl Content { - Fixed::XY(32, 7, Tui::bold(true, Tui::fg(Rgb(240,200,180), col!{ - Fixed::Y(1, ""), - Fixed::Y(1, ""), - Fixed::Y(1, "~~ ╓─╥─╖ ╓──╖ ╥ ╖ ~~~~~~~~~~~~"), - Fixed::Y(1, Bsp::e("~~~~ ║ ~ ╟─╌ ~╟─< ~~ ", Bsp::e(Tui::fg(Rgb(230,100,40), "v0.3.0"), " ~~"))), - Fixed::Y(1, "~~~~ ╨ ~ ╙──╜ ╨ ╜ ~~~~~~~~~~~~"), + pub fn view_logo () -> impl Draw { + wh_exact(32, 7, Tui::bold(true, Tui::fg(Rgb(240,200,180), south!{ + h_exact(1, ""), + h_exact(1, ""), + h_exact(1, "~~ ╓─╥─╖ ╓──╖ ╥ ╖ ~~~~~~~~~~~~"), + h_exact(1, east("~~~~ ║ ~ ╟─╌ ~╟─< ~~ ", east(Tui::fg(Rgb(230,100,40), "v0.3.0"), " ~~"))), + h_exact(1, "~~~~ ╨ ~ ╙──╜ ╨ ╜ ~~~~~~~~~~~~"), }))) } @@ -973,14 +976,14 @@ mod view { bpm: Arc>, beat: Arc>, time: Arc>, - ) -> impl Content { + ) -> impl Draw { let theme = ItemTheme::G[96]; - Tui::bg(Black, row!(Bsp::a( - Fill::XY(Align::w(button_play_pause(play))), - Fill::XY(Align::e(row!( - FieldH(theme, "BPM", bpm), - FieldH(theme, "Beat", beat), - FieldH(theme, "Time", time), + Tui::bg(Black, east!(above( + wh_full(origin_w(button_play_pause(play))), + wh_full(origin_e(east!( + field_h(theme, "BPM", bpm), + field_h(theme, "Beat", beat), + field_h(theme, "Time", time), ))) ))) } @@ -995,14 +998,14 @@ mod view { sr: Arc>, buf: Arc>, lat: Arc>, - ) -> impl Content { + ) -> impl Draw { let theme = ItemTheme::G[96]; - Tui::bg(Black, row!(Bsp::a( - Fill::XY(Align::w(sel.map(|sel|FieldH(theme, "Selected", sel)))), - Fill::XY(Align::e(row!( - FieldH(theme, "SR", sr), - FieldH(theme, "Buf", buf), - FieldH(theme, "Lat", lat), + Tui::bg(Black, east!(above( + wh_full(origin_w(sel.map(|sel|field_h(theme, "Selected", sel)))), + wh_full(origin_e(east!( + field_h(theme, "SR", sr), + field_h(theme, "Buf", buf), + field_h(theme, "Lat", lat), ))) ))) } @@ -1010,17 +1013,17 @@ mod view { /// ``` /// let _ = tek::button_play_pause(true); /// ``` - pub fn button_play_pause (playing: bool) -> impl Content { + pub fn button_play_pause (playing: bool) -> impl Draw { let compact = true;//self.is_editing(); Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, - Either::new(compact, - Thunk::new(move|to: &mut TuiOut|to.place(&Fixed::X(9, Either::new(playing, + either(compact, + Thunk::new(move|to: &mut Tui|to.place(&w_exact(9, either(playing, Tui::fg(Rgb(0, 255, 0), " PLAYING "), Tui::fg(Rgb(255, 128, 0), " STOPPED "))) )), - Thunk::new(move|to: &mut TuiOut|to.place(&Fixed::X(5, Either::new(playing, - Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), - Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))) + Thunk::new(move|to: &mut Tui|to.place(&w_exact(5, either(playing, + Tui::fg(Rgb(0, 255, 0), south(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), + Tui::fg(Rgb(255, 128, 0), south(" ▗▄▖ ", " ▝▀▘ ",)))) )) ) ) @@ -1028,12 +1031,12 @@ mod view { #[cfg(feature = "track")] pub fn view_track_row_section ( _theme: ItemTheme, - button: impl Content, - button_add: impl Content, - content: impl Content, - ) -> impl Content { - Bsp::w(Fill::Y(Fixed::X(4, Align::nw(button_add))), - Bsp::e(Fixed::X(20, Fill::Y(Align::nw(button))), Fill::XY(Align::c(content)))) + button: impl Draw, + button_add: impl Draw, + content: impl Draw, + ) -> impl Draw { + west(h_full(w_exact(4, origin_nw(button_add))), + east(w_exact(20, h_full(origin_nw(button))), wh_full(origin_c(content)))) } /// ``` @@ -1041,20 +1044,20 @@ mod view { /// let fg = tengri::ratatui::style::Color::Green; /// let _ = tek::view_wrap(bg, fg, "and then blue, too!"); /// ``` - pub fn view_wrap (bg: Color, fg: Color, content: impl Content) -> impl Content { - let left = Tui::fg_bg(bg, Reset, Fixed::X(1, Repeat::Y("▐"))); - let right = Tui::fg_bg(bg, Reset, Fixed::X(1, Repeat::Y("▌"))); - Bsp::e(left, Bsp::w(right, Tui::fg_bg(fg, bg, content))) + pub fn view_wrap (bg: Color, fg: Color, content: impl Draw) -> impl Draw { + let left = Tui::fg_bg(bg, Reset, w_exact(1, y_repeat("▐"))); + let right = Tui::fg_bg(bg, Reset, w_exact(1, y_repeat("▌"))); + east(left, west(right, Tui::fg_bg(fg, bg, content))) } /// ``` /// let _ = tek::view_meter("", 0.0); /// let _ = tek::view_meters(&[0.0, 0.0]); /// ``` - pub fn view_meter <'a> (label: &'a str, value: f32) -> impl Content + 'a { - col!( - FieldH(ItemTheme::G[128], label, format!("{:>+9.3}", value)), - Fixed::XY(if value >= 0.0 { 13 } + pub fn view_meter <'a> (label: &'a str, value: f32) -> impl Draw + 'a { + south!( + field_h(ItemTheme::G[128], label, format!("{:>+9.3}", value)), + wh_exact(if value >= 0.0 { 13 } else if value >= -1.0 { 12 } else if value >= -2.0 { 11 } else if value >= -3.0 { 10 } @@ -1072,47 +1075,47 @@ mod view { else { Green }, ()))) } - pub fn view_meters (values: &[f32;2]) -> impl Content + use<'_> { + pub fn view_meters (values: &[f32;2]) -> impl Draw + use<'_> { let left = format!("L/{:>+9.3}", values[0]); let right = format!("R/{:>+9.3}", values[1]); - Bsp::s(left, right) + south(left, right) } - pub fn draw_info (sample: Option<&Arc>>) -> impl Content + use<'_> { - When::new(sample.is_some(), Thunk::new(move|to: &mut TuiOut|{ + pub fn draw_info (sample: Option<&Arc>>) -> impl Draw + use<'_> { + when(sample.is_some(), Thunk::new(move|to: &mut Tui|{ let sample = sample.unwrap().read().unwrap(); let theme = sample.color; - to.place(&row!( - FieldH(theme, "Name", format!("{:<10}", sample.name.clone())), - FieldH(theme, "Length", format!("{:<8}", sample.channels[0].len())), - FieldH(theme, "Start", format!("{:<8}", sample.start)), - FieldH(theme, "End", format!("{:<8}", sample.end)), - FieldH(theme, "Trans", "0"), - FieldH(theme, "Gain", format!("{}", sample.gain)), + to.place(&east!( + field_h(theme, "Name", format!("{:<10}", sample.name.clone())), + field_h(theme, "Length", format!("{:<8}", sample.channels[0].len())), + field_h(theme, "Start", format!("{:<8}", sample.start)), + field_h(theme, "End", format!("{:<8}", sample.end)), + field_h(theme, "Trans", "0"), + field_h(theme, "Gain", format!("{}", sample.gain)), )) })) } - pub fn draw_info_v (sample: Option<&Arc>>) -> impl Content + use<'_> { - Either::new(sample.is_some(), Thunk::new(move|to: &mut TuiOut|{ + pub fn draw_info_v (sample: Option<&Arc>>) -> impl Draw + use<'_> { + either(sample.is_some(), Thunk::new(move|to: &mut Tui|{ let sample = sample.unwrap().read().unwrap(); let theme = sample.color; - to.place(&Fixed::X(20, col!( - Fill::X(Align::w(FieldH(theme, "Name ", format!("{:<10}", sample.name.clone())))), - Fill::X(Align::w(FieldH(theme, "Length", format!("{:<8}", sample.channels[0].len())))), - Fill::X(Align::w(FieldH(theme, "Start ", format!("{:<8}", sample.start)))), - Fill::X(Align::w(FieldH(theme, "End ", format!("{:<8}", sample.end)))), - Fill::X(Align::w(FieldH(theme, "Trans ", "0"))), - Fill::X(Align::w(FieldH(theme, "Gain ", format!("{}", sample.gain)))), + to.place(&w_exact(20, south!( + w_full(origin_w(field_h(theme, "Name ", format!("{:<10}", sample.name.clone())))), + w_full(origin_w(field_h(theme, "Length", format!("{:<8}", sample.channels[0].len())))), + w_full(origin_w(field_h(theme, "Start ", format!("{:<8}", sample.start)))), + w_full(origin_w(field_h(theme, "End ", format!("{:<8}", sample.end)))), + w_full(origin_w(field_h(theme, "Trans ", "0"))), + w_full(origin_w(field_h(theme, "Gain ", format!("{}", sample.gain)))), ))) - }), Thunk::new(|to: &mut TuiOut|to.place(&Tui::fg(Red, col!( + }), Thunk::new(|to: &mut Tui|to.place(&Tui::fg(Red, south!( Tui::bold(true, "× No sample."), "[r] record", "[Shift-F9] import", ))))) } - pub fn draw_status (sample: Option<&Arc>>) -> impl Content { + pub fn draw_status (sample: Option<&Arc>>) -> impl Draw { Tui::bold(true, Tui::fg(Tui::g(224), sample .map(|sample|{ let sample = sample.read().unwrap(); @@ -1121,31 +1124,31 @@ mod view { .unwrap_or_else(||"No sample".to_string()))) } - pub fn view_track_header (theme: ItemTheme, content: impl Content) -> impl Content { - Fixed::X(12, Tui::bg(theme.darker.rgb, Fill::X(Align::e(content)))) + pub fn view_track_header (theme: ItemTheme, content: impl Draw) -> impl Draw { + w_exact(12, Tui::bg(theme.darker.rgb, w_full(origin_e(content)))) } pub fn view_ports_status <'a, T: JackPort> (theme: ItemTheme, title: &'a str, ports: &'a [T]) - -> impl Content + use<'a, T> + -> impl Draw + use<'a, T> { let ins = ports.len() as u16; let frame = Outer(true, Style::default().fg(Tui::g(96))); let iter = move||ports.iter(); - let names = Map::south(1, iter, move|port, index|Fill::Y(Align::w(format!(" {index} {}", port.port_name())))); - let field = FieldV(theme, title, names); - Fixed::XY(20, 1 + ins, frame.enclose(Fixed::XY(20, 1 + ins, field))) + let names = iter_south(1, iter, move|port, index|h_full(origin_w(format!(" {index} {}", port.port_name())))); + let field = field_v(theme, title, names); + wh_exact(20, 1 + ins, frame.enclose(wh_exact(20, 1 + ins, field))) } pub fn io_ports <'a, T: PortsSizes<'a>> ( - fg: Color, bg: Color, iter: impl Fn()->T + Send + Sync + 'a - ) -> impl Content + 'a { - Map::new(iter, move|( + fg: Color, bg: Color, items: impl Fn()->T + Send + Sync + 'a + ) -> impl Draw + 'a { + iter(items, move|( _index, name, connections, y, y2 ): (usize, &'a Arc, &'a [Connect], usize, usize), _| - map_south(y as u16, (y2-y) as u16, Bsp::s( - Fill::Y(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(Bsp::e(&" 󰣲 ", name))))), - Map::new(||connections.iter(), move|connect: &'a Connect, index|map_south(index as u16, 1, - Fill::Y(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, + iter_south(y as u16, (y2-y) as u16, south( + h_full(Tui::bold(true, Tui::fg_bg(fg, bg, origin_w(east(&" 󰣲 ", name))))), + iter(||connections.iter(), move|connect: &'a Connect, index|iter_south(index as u16, 1, + h_full(origin_w(Tui::bold(false, Tui::fg_bg(fg, bg, &connect.info))))))))) } } @@ -1210,5 +1213,5 @@ pub const NOTE_NAMES: [&str; 128] = [ pub(crate) const HEADER: &'static str = r#" ~ █▀█▀█ █▀▀█ █ █ ~~~ ~ ~ ~~ ~ ~ ~ ~~ ~ ~ ~ ~ - █ █▀ █▀▀▄ ~ v0.4.0, 2026 winter (or is it) ~ + █ term█▀ █▀▀▄ ~ v0.4.0, 2026 winter (or is it) ~ ~ ▀ █▀▀█ ▀ ▀ ~ ~~~ ~ ~ ~ ~ ~~~ ~~~ ~ ~~ "#; diff --git a/app/tek_impls.rs b/app/tek_impls.rs index 67a6a9d1..b6b498cf 100644 --- a/app/tek_impls.rs +++ b/app/tek_impls.rs @@ -64,18 +64,18 @@ use std::fmt::Write; } } -impl +AsMut> HasClock for T {} -impl +AsMut> HasSelection for T {} -impl +AsMut> HasSequencer for T {} -impl >+AsMut>> HasScenes for T {} -impl >+AsMut>> HasTracks for T {} -impl +AsMutOpt> HasEditor for T {} -impl +AsMutOpt+Send+Sync> HasScene for T {} -impl +AsMutOpt+Send+Sync> HasTrack for T {} -impl MidiPoint for T {} -impl > TracksView for T {} -impl MidiRange for T {} -impl ClipsView for T {} +impl +AsMut> HasClock for T {} +impl +AsMut> HasSelection for T {} +impl +AsMut> HasSequencer for T {} +impl >+AsMut>> HasScenes for T {} +impl >+AsMut>> HasTracks for T {} +impl +AsMutOpt> HasEditor for T {} +impl +AsMutOpt+Send+Sync> HasScene for T {} +impl +AsMutOpt+Send+Sync> HasTrack for T {} +impl MidiPoint for T {} +impl > TracksView for T {} +impl MidiRange for T {} +impl ClipsView for T {} mod app { use crate::*; @@ -84,24 +84,24 @@ mod app { impl_has!(Vec: |self: App|self.project.midi_outs); impl_has!(Dialog: |self: App|self.dialog); impl_has!(Jack<'static>: |self: App|self.jack); - impl_has!(Measure: |self: App|self.size); + impl_has!(Measure: |self: App|self.size); impl_has!(Pool: |self: App|self.pool); impl_has!(Selection: |self: App|self.project.selection); impl_as_ref!(Vec: |self: App|self.project.as_ref()); impl_as_mut!(Vec: |self: App|self.project.as_mut()); impl_as_ref_opt!(MidiEditor: |self: App|self.project.as_ref_opt()); impl_as_mut_opt!(MidiEditor: |self: App|self.project.as_mut_opt()); - has_clips!( |self: App|self.pool.clips); + impl_has_clips!( |self: App|self.pool.clips); impl_audio!(App: tek_jack_process, tek_jack_event); - handle!(TuiIn: |self: App, input|{ + impl_handle!(TuiIn: |self: App, input|{ let commands = collect_commands(self, input)?; let history = execute_commands(self, commands)?; self.history.extend(history.into_iter()); Ok(None) }); - impl Draw for App { - fn draw (&self, to: &mut TuiOut) { + impl Draw for App { + fn draw (self, to: &mut Tui) -> Usually> { if let Some(e) = self.error.read().unwrap().as_ref() { to.show(to.area(), e); } @@ -125,16 +125,16 @@ mod app { }); } - impl Understand for App { - fn understand_expr <'a> (&'a self, to: &mut TuiOut, lang: &'a impl Expression) -> Usually<()> { + impl Understand for App { + fn understand_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression) -> Usually<()> { app_understand_expr(self, to, lang) } - fn understand_word <'a> (&'a self, to: &mut TuiOut, lang: &'a impl Expression) -> Usually<()> { + fn understand_word <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression) -> Usually<()> { app_understand_word(self, to, lang) } } - fn app_understand_expr (state: &App, to: &mut TuiOut, lang: &impl Expression) -> Usually<()> { + fn app_understand_expr (state: &App, to: &mut Tui, lang: &impl Expression) -> Usually<()> { if evaluate_output_expression(state, to, lang)? || evaluate_output_expression_tui(state, to, lang)? { Ok(()) @@ -143,22 +143,22 @@ mod app { } } - fn app_understand_word (state: &App, to: &mut TuiOut, dsl: &impl Expression) -> Usually<()> { + fn app_understand_word (state: &App, to: &mut Tui, dsl: &impl Expression) -> Usually<()> { let mut frags = dsl.src()?.unwrap().split("/"); match frags.next() { Some(":logo") => to.place(&view_logo()), - Some(":status") => to.place(&Fixed::Y(1, "TODO: Status Bar")), + Some(":status") => to.place(&h_exact(1, "TODO: Status Bar")), Some(":meters") => match frags.next() { - Some("input") => to.place(&Tui::bg(Rgb(30, 30, 30), Fill::Y(Align::s("Input Meters")))), - Some("output") => to.place(&Tui::bg(Rgb(30, 30, 30), Fill::Y(Align::s("Output Meters")))), + Some("input") => to.place(&Tui::bg(Rgb(30, 30, 30), h_full(origin_s("Input Meters")))), + Some("output") => to.place(&Tui::bg(Rgb(30, 30, 30), h_full(origin_s("Output Meters")))), _ => panic!() }, Some(":tracks") => match frags.next() { None => to.place(&"TODO tracks"), - Some("names") => to.place(&state.project.view_track_names(state.color.clone())),//Tui::bg(Rgb(40, 40, 40), Fill::X(Align::w("Track Names")))), - Some("inputs") => to.place(&Tui::bg(Rgb(40, 40, 40), Fill::X(Align::w("Track Inputs")))), - Some("devices") => to.place(&Tui::bg(Rgb(40, 40, 40), Fill::X(Align::w("Track Devices")))), - Some("outputs") => to.place(&Tui::bg(Rgb(40, 40, 40), Fill::X(Align::w("Track Outputs")))), + Some("names") => to.place(&state.project.view_track_names(state.color.clone())),//Tui::bg(Rgb(40, 40, 40), w_full(origin_w("Track Names")))), + Some("inputs") => to.place(&Tui::bg(Rgb(40, 40, 40), w_full(origin_w("Track Inputs")))), + Some("devices") => to.place(&Tui::bg(Rgb(40, 40, 40), w_full(origin_w("Track Devices")))), + Some("outputs") => to.place(&Tui::bg(Rgb(40, 40, 40), w_full(origin_w("Track Outputs")))), _ => panic!() }, Some(":scenes") => match frags.next() { @@ -171,13 +171,13 @@ mod app { Some("menu") => to.place(&if let Dialog::Menu(selected, items) = &state.dialog { let items = items.clone(); let selected = selected; - Some(Fill::XY(Thunk::new(move|to: &mut TuiOut|{ + Some(wh_full(Thunk::new(move|to: &mut Tui|{ for (index, MenuItem(item, _)) in items.0.iter().enumerate() { - to.place(&Push::Y((2 * index) as u16, + to.place(&y_push((2 * index) as u16, Tui::fg_bg( if *selected == index { Rgb(240,200,180) } else { Rgb(200, 200, 200) }, if *selected == index { Rgb(80, 80, 50) } else { Rgb(30, 30, 30) }, - Fixed::Y(2, Align::n(Fill::X(item))) + h_exact(2, origin_n(w_full(item))) ))); } }))) @@ -189,31 +189,31 @@ mod app { Some(":templates") => to.place(&{ let modes = state.config.modes.clone(); let height = (modes.read().unwrap().len() * 2) as u16; - Fixed::Y(height, Min::X(30, Thunk::new(move |to: &mut TuiOut|{ + h_exact(height, w_min(30, Thunk::new(move |to: &mut Tui|{ for (index, (id, profile)) in modes.read().unwrap().iter().enumerate() { let bg = if index == 0 { Rgb(70,70,70) } else { Rgb(50,50,50) }; let name = profile.name.get(0).map(|x|x.as_ref()).unwrap_or(""); let info = profile.info.get(0).map(|x|x.as_ref()).unwrap_or(""); let fg1 = Rgb(224, 192, 128); let fg2 = Rgb(224, 128, 32); - let field_name = Fill::X(Align::w(Tui::fg(fg1, name))); - let field_id = Fill::X(Align::e(Tui::fg(fg2, id))); - let field_info = Fill::X(Align::w(info)); - to.place(&Push::Y((2 * index) as u16, - Fixed::Y(2, Fill::X(Tui::bg(bg, Bsp::s( - Bsp::a(field_name, field_id), field_info)))))); + let field_name = w_full(origin_w(Tui::fg(fg1, name))); + let field_id = w_full(origin_e(Tui::fg(fg2, id))); + let field_info = w_full(origin_w(info)); + to.place(&y_push((2 * index) as u16, + h_exact(2, w_full(Tui::bg(bg, south( + above(field_name, field_id), field_info)))))); } }))) }), - Some(":sessions") => to.place(&Fixed::Y(6, Min::X(30, Thunk::new(|to: &mut TuiOut|{ + Some(":sessions") => to.place(&h_exact(6, w_min(30, Thunk::new(|to: &mut Tui|{ let fg = Rgb(224, 192, 128); for (index, name) in ["session1", "session2", "session3"].iter().enumerate() { let bg = if index == 0 { Rgb(50,50,50) } else { Rgb(40,40,40) }; - to.place(&Push::Y((2 * index) as u16, - &Fixed::Y(2, Fill::X(Tui::bg(bg, Align::w(Tui::fg(fg, name))))))); + to.place(&y_push((2 * index) as u16, + &h_exact(2, w_full(Tui::bg(bg, origin_w(Tui::fg(fg, name))))))); } })))), - Some(":browse/title") => to.place(&Fill::X(Align::w(FieldV(ItemColor::default(), + Some(":browse/title") => to.place(&w_full(origin_w(field_v(ItemColor::default(), match state.dialog.browser_target().unwrap() { BrowseTarget::SaveProject => "Save project:", BrowseTarget::LoadProject => "Load project:", @@ -221,18 +221,18 @@ mod app { BrowseTarget::ExportSample(_) => "Export sample:", BrowseTarget::ImportClip(_) => "Import clip:", BrowseTarget::ExportClip(_) => "Export clip:", - }, Shrink::X(3, Fixed::Y(1, Tui::fg(Tui::g(96), Repeat::X("🭻")))))))), + }, w_shrink(3, h_exact(1, Tui::fg(Tui::g(96), x_repeat("🭻")))))))), Some(":device") => { let selected = state.dialog.device_kind().unwrap(); - to.place(&Bsp::s(Tui::bold(true, "Add device"), Map::south(1, + to.place(&south(Tui::bold(true, "Add device"), iter_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")))) }))) + w_full(Tui::bg(bg, east(lb, west(rb, "FIXME device name")))) }))) }, - Some(":debug") => to.place(&Fixed::Y(1, format!("[{:?}]", to.area()))), + Some(":debug") => to.place(&h_exact(1, format!("[{:?}]", to.area()))), Some(_) => { let views = state.config.views.read().unwrap(); if let Some(dsl) = views.get(dsl.src()?.unwrap()) { @@ -375,7 +375,7 @@ mod app { mod arrange { use crate::*; impl_has!(Jack<'static>: |self: Arrangement| self.jack); - impl_has!(Measure: |self: Arrangement| self.size); + impl_has!(Measure: |self: Arrangement| self.size); impl_has!(Vec: |self: Arrangement| self.tracks); impl_has!(Vec: |self: Arrangement| self.scenes); impl_has!(Vec: |self: Arrangement| self.midi_ins); @@ -479,79 +479,79 @@ mod arrange { } Ok((index, &mut self.tracks_mut()[index])) } - #[cfg(feature = "track")] pub fn view_inputs (&self, _theme: ItemTheme) -> impl Content + '_ { - Bsp::s( - Fixed::Y(1, self.view_inputs_header()), - Thunk::new(|to: &mut TuiOut|{ + #[cfg(feature = "track")] pub fn view_inputs (&self, _theme: ItemTheme) -> impl Draw + '_ { + south( + h_exact(1, self.view_inputs_header()), + Thunk::new(|to: &mut Tui|{ for (index, port) in self.midi_ins().iter().enumerate() { - to.place(&Push::X(index as u16 * 10, Fixed::Y(1, self.view_inputs_row(port)))) + to.place(&x_push(index as u16 * 10, h_exact(1, self.view_inputs_row(port)))) } }) ) } - #[cfg(feature = "track")] fn view_inputs_header (&self) -> impl Content + '_ { - Bsp::e(Fixed::X(20, Align::w(button_3("i", "nput ", format!("{}", self.midi_ins.len()), false))), - Bsp::w(Fixed::X(4, button_2("I", "+", false)), Thunk::new(move|to: &mut TuiOut|for (_index, track, x1, _x2) in self.tracks_with_sizes() { + #[cfg(feature = "track")] fn view_inputs_header (&self) -> impl Draw + '_ { + east(w_exact(20, origin_w(button_3("i", "nput ", format!("{}", self.midi_ins.len()), false))), + west(w_exact(4, button_2("I", "+", false)), Thunk::new(move|to: &mut Tui|for (_index, track, x1, _x2) in self.tracks_with_sizes() { #[cfg(feature = "track")] - to.place(&Push::X(x1 as u16, Tui::bg(track.color.dark.rgb, Align::w(Fixed::X(track.width as u16, row!( - Either::new(track.sequencer.monitoring, Tui::fg(Green, "mon "), "mon "), - Either::new(track.sequencer.recording, Tui::fg(Red, "rec "), "rec "), - Either::new(track.sequencer.overdub, Tui::fg(Yellow, "dub "), "dub "), + to.place(&x_push(x1 as u16, Tui::bg(track.color.dark.rgb, origin_w(w_exact(track.width as u16, east!( + either(track.sequencer.monitoring, Tui::fg(Green, "mon "), "mon "), + either(track.sequencer.recording, Tui::fg(Red, "rec "), "rec "), + either(track.sequencer.overdub, Tui::fg(Yellow, "dub "), "dub "), )))))) }))) } - #[cfg(feature = "track")] fn view_inputs_row (&self, port: &MidiInput) -> impl Content { - Bsp::e(Fixed::X(20, Align::w(Bsp::e(" ● ", Tui::bold(true, Tui::fg(Rgb(255,255,255), port.port_name()))))), - Bsp::w(Fixed::X(4, ()), Thunk::new(move|to: &mut TuiOut|for (_index, track, _x1, _x2) in self.tracks_with_sizes() { + #[cfg(feature = "track")] fn view_inputs_row (&self, port: &MidiInput) -> impl Draw { + east(w_exact(20, origin_w(east(" ● ", Tui::bold(true, Tui::fg(Rgb(255,255,255), port.port_name()))))), + west(w_exact(4, ()), Thunk::new(move|to: &mut Tui|for (_index, track, _x1, _x2) in self.tracks_with_sizes() { #[cfg(feature = "track")] - to.place(&Tui::bg(track.color.darker.rgb, Align::w(Fixed::X(track.width as u16, row!( - Either::new(track.sequencer.monitoring, Tui::fg(Green, " ● "), " · "), - Either::new(track.sequencer.recording, Tui::fg(Red, " ● "), " · "), - Either::new(track.sequencer.overdub, Tui::fg(Yellow, " ● "), " · "), + to.place(&Tui::bg(track.color.darker.rgb, origin_w(w_exact(track.width as u16, east!( + either(track.sequencer.monitoring, Tui::fg(Green, " ● "), " · "), + either(track.sequencer.recording, Tui::fg(Red, " ● "), " · "), + either(track.sequencer.overdub, Tui::fg(Yellow, " ● "), " · "), ))))) }))) } - #[cfg(feature = "track")] pub fn view_outputs (&self, theme: ItemTheme) -> impl Content { + #[cfg(feature = "track")] pub fn view_outputs (&self, theme: ItemTheme) -> impl Draw { let mut h = 1; for output in self.midi_outs().iter() { h += 1 + output.connections.len(); } let h = h as u16; - let list = Bsp::s( - Fixed::Y(1, Fill::X(Align::w(button_3("o", "utput", format!("{}", self.midi_outs.len()), false)))), - Fixed::Y(h - 1, Fill::XY(Align::nw(Thunk::new(|to: &mut TuiOut|{ + let list = south( + h_exact(1, w_full(origin_w(button_3("o", "utput", format!("{}", self.midi_outs.len()), false)))), + h_exact(h - 1, wh_full(origin_nw(Thunk::new(|to: &mut Tui|{ for (_index, port) in self.midi_outs().iter().enumerate() { - to.place(&Fixed::Y(1,Fill::X(Bsp::e( - Align::w(Bsp::e(" ● ", Tui::fg(Rgb(255,255,255),Tui::bold(true, port.port_name())))), - Fill::X(Align::e(format!("{}/{} ", + to.place(&h_exact(1,w_full(east( + origin_w(east(" ● ", Tui::fg(Rgb(255,255,255),Tui::bold(true, port.port_name())))), + w_full(origin_e(format!("{}/{} ", port.port().get_connections().len(), port.connections.len()))))))); for (index, conn) in port.connections.iter().enumerate() { - to.place(&Fixed::Y(1, Fill::X(Align::w(format!(" c{index:02}{}", conn.info()))))); + to.place(&h_exact(1, w_full(origin_w(format!(" c{index:02}{}", conn.info()))))); } } }))))); - Fixed::Y(h, view_track_row_section(theme, list, button_2("O", "+", false), - Tui::bg(theme.darker.rgb, Align::w(Fill::X( - Thunk::new(|to: &mut TuiOut|{ + h_exact(h, view_track_row_section(theme, list, button_2("O", "+", false), + Tui::bg(theme.darker.rgb, origin_w(w_full( + Thunk::new(|to: &mut Tui|{ for (index, track, _x1, _x2) in self.tracks_with_sizes() { - to.place(&Fixed::X(track_width(index, track), - Thunk::new(|to: &mut TuiOut|{ - to.place(&Fixed::Y(1, Align::w(Bsp::e( - Either::new(true, Tui::fg(Green, "play "), "play "), - Either::new(false, Tui::fg(Yellow, "solo "), "solo "), + to.place(&w_exact(track_width(index, track), + Thunk::new(|to: &mut Tui|{ + to.place(&h_exact(1, origin_w(east( + either(true, Tui::fg(Green, "play "), "play "), + either(false, Tui::fg(Yellow, "solo "), "solo "), )))); for (_index, port) in self.midi_outs().iter().enumerate() { - to.place(&Fixed::Y(1, Align::w(Bsp::e( - Either::new(true, Tui::fg(Green, " ● "), " · "), - Either::new(false, Tui::fg(Yellow, " ● "), " · "), + to.place(&h_exact(1, origin_w(east( + either(true, Tui::fg(Green, " ● "), " · "), + either(false, Tui::fg(Yellow, " ● "), " · "), )))); for (_index, _conn) in port.connections.iter().enumerate() { - to.place(&Fixed::Y(1, Fill::X(""))); + to.place(&h_exact(1, w_full(""))); } }})))}})))))) } - #[cfg(feature = "track")] pub fn view_track_devices (&self, theme: ItemTheme) -> impl Content { + #[cfg(feature = "track")] pub fn view_track_devices (&self, theme: ItemTheme) -> impl Draw { let mut h = 2u16; for track in self.tracks().iter() { h = h.max(track.devices.len() as u16 * 2); @@ -559,14 +559,14 @@ mod arrange { view_track_row_section(theme, button_3("d", "evice", format!("{}", self.track().map(|t|t.devices.len()).unwrap_or(0)), false), button_2("D", "+", false), - Thunk::new(move|to: &mut TuiOut|for (index, track, _x1, _x2) in self.tracks_with_sizes() { - to.place(&Fixed::XY(track_width(index, track), h + 1, - Tui::bg(track.color.dark.rgb, Align::nw(Map::south(2, move||0..h, - |_, _index|Fixed::XY(track.width as u16, 2, + Thunk::new(move|to: &mut Tui|for (index, track, _x1, _x2) in self.tracks_with_sizes() { + to.place(&wh_exact(track_width(index, track), h + 1, + Tui::bg(track.color.dark.rgb, origin_nw(iter_south(2, move||0..h, + |_, _index|wh_exact(track.width as u16, 2, Tui::fg_bg( ItemTheme::G[32].lightest.rgb, ItemTheme::G[32].dark.rgb, - Align::nw(format!(" · {}", "--"))))))))); + origin_nw(format!(" · {}", "--"))))))))); })) } /// Put a clip in a slot @@ -616,7 +616,7 @@ mod arrange { } } impl HasClipsSize for Arrangement { - fn clips_size (&self) -> &Measure { &self.size_inner } + fn clips_size (&self) -> &Measure { &self.size_inner } } } @@ -657,14 +657,14 @@ mod browse { fn _todo_stub_usize (&self) -> usize { todo!() } fn _todo_stub_arc_str (&self) -> Arc { todo!() } } - impl HasContent for Browse { - fn content (&self) -> impl Content { - Map::south(1, ||EntriesIterator { + impl Browse { + fn tui (&self) -> impl Draw { + iter_south(1, ||EntriesIterator { offset: 0, index: 0, length: self.dirs.len() + self.files.len(), browser: self, - }, |entry, _index|Fill::X(Align::w(entry))) + }, |entry, _index|w_full(origin_w(entry))) } } impl<'a> Iterator for EntriesIterator<'a> { @@ -1117,7 +1117,7 @@ impl InteriorMutable for AtomicUsize { fn set (&self, value: usize) -> us impl PartialEq for MenuItem { fn eq (&self, other: &Self) -> bool { self.0 == other.0 } } impl AsRef> for MenuItems { fn as_ref (&self) -> &Arc<[MenuItem]> { &self.0 } } -impl HasClipsSize for App { fn clips_size (&self) -> &Measure { &self.project.size_inner } } +impl HasClipsSize for App { fn clips_size (&self) -> &Measure { &self.project.size_inner } } impl HasJack<'static> for MidiInput { fn jack (&self) -> &Jack<'static> { &self.jack } } impl HasJack<'static> for MidiOutput { fn jack (&self) -> &Jack<'static> { &self.jack } } impl HasJack<'static> for AudioInput { fn jack (&self) -> &Jack<'static> { &self.jack } } @@ -1795,11 +1795,8 @@ mod audio { mod meter { use crate::*; - impl Layout for RmsMeter {} - impl Layout for Log10Meter {} - - impl Draw for RmsMeter { - fn draw (&self, to: &mut TuiOut) { + impl Draw for RmsMeter { + fn draw(self, to: &mut Tui) -> Usually> { let XYWH(x, y, w, h) = to.area(); let signal = f32::max(0.0, f32::min(100.0, self.0.abs())); let v = (signal * h as f32).ceil() as u16; @@ -1813,8 +1810,8 @@ mod audio { } } - impl Draw for Log10Meter { - fn draw (&self, to: &mut TuiOut) { + impl Draw for Log10Meter { + fn draw(self, to: &mut Tui) -> Usually> { let XYWH(x, y, w, h) = to.area(); let signal = 100.0 - f32::max(0.0, f32::min(100.0, self.0.abs())); let v = (signal * h as f32 / 100.0).ceil() as u16; @@ -1828,9 +1825,9 @@ mod audio { } } - fn draw_meters (meters: &[f32]) -> impl Content + use<'_> { - Tui::bg(Black, Fixed::X(2, Map::east(1, ||meters.iter(), |value, _index|{ - Fill::Y(RmsMeter(*value)) + fn draw_meters (meters: &[f32]) -> impl Draw + use<'_> { + Tui::bg(Black, w_exact(2, iter_east(1, ||meters.iter(), |value, _index|{ + h_full(RmsMeter(*value)) }))) } } @@ -1879,14 +1876,14 @@ mod audio { fn _todo_usize_stub_ (&self) -> usize { todo!() } fn _todo_arc_str_stub_ (&self) -> Arc { todo!() } fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() } - pub fn per <'a, T: Content + 'a, U: TracksSizes<'a>> ( + pub fn per <'a, T: Draw + 'a, U: TracksSizes<'a>> ( tracks: impl Fn() -> U + Send + Sync + 'a, callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a - ) -> impl Content + 'a { - Map::new(tracks, + ) -> impl Draw + 'a { + iter(tracks, move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _|{ let width = (x2 - x1) as u16; - map_east(x1 as u16, width, Fixed::X(width, Tui::fg_bg( + iter_east(x1 as u16, width, w_exact(width, Tui::fg_bg( track.color.lightest.rgb, track.color.base.rgb, callback(index, track))))}) @@ -1932,21 +1929,21 @@ mod audio { } } - pub fn per_track <'a, T: Content + 'a, U: TracksSizes<'a>> ( + pub fn per_track <'a, T: Draw + 'a, U: TracksSizes<'a>> ( tracks: impl Fn() -> U + Send + Sync + 'a, callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a - ) -> impl Content + 'a { - per_track_top(tracks, move|index, track|Fill::Y(Align::y(callback(index, track)))) + ) -> impl Draw + 'a { + per_track_top(tracks, move|index, track|h_full(origin_y(callback(index, track)))) } - pub fn per_track_top <'a, T: Content + 'a, U: TracksSizes<'a>> ( + pub fn per_track_top <'a, T: Draw + 'a, U: TracksSizes<'a>> ( tracks: impl Fn() -> U + Send + Sync + 'a, callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a - ) -> impl Content + 'a { - Align::x(Tui::bg(Reset, Map::new(tracks, + ) -> impl Draw + 'a { + origin_x(Tui::bg(Reset, iter(tracks, move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _|{ let width = (x2 - x1) as u16; - map_east(x1 as u16, width, Fixed::X(width, Tui::fg_bg( + iter_east(x1 as u16, width, w_exact(width, Tui::fg_bg( track.color.lightest.rgb, track.color.base.rgb, callback(index, track))))}))) @@ -2007,8 +2004,8 @@ mod audio { impl_has!(Clock: |self: Sequencer| self.clock); impl_has!(Vec: |self: Sequencer| self.midi_ins); impl_has!(Vec: |self: Sequencer| self.midi_outs); - impl_has!(Measure: |self: MidiEditor| self.size); - impl_has!(Measure: |self: PianoHorizontal| self.size); + impl_has!(Measure: |self: MidiEditor| self.size); + impl_has!(Measure: |self: PianoHorizontal| self.size); impl_default!(Sequencer: Self { clock: Clock::default(), play_clip: None, @@ -2224,11 +2221,15 @@ mod audio { } } } - impl Draw for MidiEditor { - fn draw (&self, to: &mut TuiOut) { self.content().draw(to) } + impl Draw for MidiEditor { + fn draw(self, to: &mut Tui) -> Usually> { + self.tui().draw(to) + } } - impl Draw for PianoHorizontal { - fn draw (&self, to: &mut TuiOut) { self.content().draw(to) } + impl Draw for PianoHorizontal { + fn draw(self, to: &mut Tui) -> Usually> { + self.tui().draw(to) + } } } @@ -2320,23 +2321,23 @@ mod audio { self.get_time_pos().overflowing_sub(1) .0.min(self.clip_length().saturating_sub(1)) } - pub fn clip_status (&self) -> impl Content + '_ { + pub fn clip_status (&self) -> impl Draw + '_ { let (_color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { (clip.color, clip.name.clone(), clip.length, clip.looped) } else { (ItemTheme::G[64], String::new().into(), 0, false) }; - Fixed::X(20, col!( - Fill::X(Align::w(Bsp::e( + w_exact(20, south!( + w_full(origin_w(east( button_2("f2", "name ", false), - Fill::X(Align::e(Tui::fg(Rgb(255, 255, 255), format!("{name} "))))))), - Fill::X(Align::w(Bsp::e( + w_full(origin_e(Tui::fg(Rgb(255, 255, 255), format!("{name} "))))))), + w_full(origin_w(east( button_2("l", "ength ", false), - Fill::X(Align::e(Tui::fg(Rgb(255, 255, 255), format!("{length} "))))))), - Fill::X(Align::w(Bsp::e( + w_full(origin_e(Tui::fg(Rgb(255, 255, 255), format!("{length} "))))))), + w_full(origin_w(east( button_2("r", "epeat ", false), - Fill::X(Align::e(Tui::fg(Rgb(255, 255, 255), format!("{looped} "))))))), + w_full(origin_e(Tui::fg(Rgb(255, 255, 255), format!("{looped} "))))))), )) } - pub fn edit_status (&self) -> impl Content + '_ { + pub fn edit_status (&self) -> impl Draw + '_ { let (_color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { (clip.color, clip.length) } else { (ItemTheme::G[64], 0) }; @@ -2347,18 +2348,18 @@ mod audio { 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()); - Fixed::X(20, col!( - Fill::X(Align::w(Bsp::e( + w_exact(20, south!( + w_full(origin_w(east( button_2("t", "ime ", false), - Fill::X(Align::e(Tui::fg(Rgb(255, 255, 255), + w_full(origin_e(Tui::fg(Rgb(255, 255, 255), format!("{length} /{time_zoom} +{time_pos} "))))))), - Fill::X(Align::w(Bsp::e( + w_full(origin_w(east( button_2("z", "lock ", false), - Fill::X(Align::e(Tui::fg(Rgb(255, 255, 255), + w_full(origin_e(Tui::fg(Rgb(255, 255, 255), format!("{time_lock}"))))))), - Fill::X(Align::w(Bsp::e( + w_full(origin_w(east( button_2("x", "note ", false), - Fill::X(Align::e(Tui::fg(Rgb(255, 255, 255), + w_full(origin_e(Tui::fg(Rgb(255, 255, 255), format!("{note_name} {note_pos} {note_len}"))))))), )) } @@ -2394,12 +2395,8 @@ mod audio { fn set_clip (&mut self, p: Option<&Arc>>) { self.mode.set_clip(p) } } - impl Layout for MidiEditor { - fn layout (&self, to: XYWH) -> XYWH { self.content().layout(to) } - } - - impl HasContent for MidiEditor { - fn content (&self) -> impl Content { self.autoscroll(); /*self.autozoom();*/ self.size.of(&self.mode) } + impl MidiEditor { + fn tui (&self) -> impl Draw { self.autoscroll(); /*self.autozoom();*/ self.size.of(&self.mode) } } @@ -2423,14 +2420,11 @@ mod audio { } } - impl Layout for PianoHorizontal { - fn layout (&self, to: XYWH) -> XYWH { self.content().layout(to) } - } - impl HasContent for PianoHorizontal { - fn content (&self) -> impl Content { - Bsp::s( - Bsp::e(Fixed::X(5, format!("{}x{}", self.size.w(), self.size.h())), self.timeline()), - Bsp::e(self.keys(), self.size.of(Bsp::b(Fill::XY(self.notes()), Fill::XY(self.cursor())))), + impl PianoHorizontal { + fn tui (&self) -> impl Draw { + south( + east(w_exact(5, format!("{}x{}", self.size.w(), self.size.h())), self.timeline()), + east(self.keys(), self.size.of(below(wh_full(self.notes()), wh_full(self.cursor())))), ) } } @@ -2507,12 +2501,12 @@ mod audio { } } - fn notes (&self) -> impl Content { + fn notes (&self) -> impl Draw { let time_start = self.get_time_start(); let note_lo = self.get_note_lo(); let note_hi = self.get_note_hi(); let buffer = self.buffer.clone(); - Thunk::new(move|to: &mut TuiOut|{ + Thunk::new(move|to: &mut Tui|{ let source = buffer.read().unwrap(); let XYWH(x0, y0, w, _h) = to.area(); //if h as usize != note_axis { @@ -2538,7 +2532,7 @@ mod audio { } }) } - fn cursor (&self) -> impl Content { + fn cursor (&self) -> impl Draw { let note_hi = self.get_note_hi(); let note_lo = self.get_note_lo(); let note_pos = self.get_note_pos(); @@ -2547,7 +2541,7 @@ mod audio { let time_start = self.get_time_start(); let time_zoom = self.get_time_zoom(); let style = Some(Style::default().fg(self.color.lightest.rgb)); - Thunk::new(move|to: &mut TuiOut|{ + Thunk::new(move|to: &mut Tui|{ let XYWH(x0, y0, w, _) = to.area(); for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) { if note == note_pos { @@ -2569,7 +2563,7 @@ mod audio { } }) } - fn keys (&self) -> impl Content { + fn keys (&self) -> impl Draw { let state = self; let color = state.color; let note_lo = state.get_note_lo(); @@ -2578,7 +2572,7 @@ mod audio { let key_style = Some(Style::default().fg(Rgb(192, 192, 192)).bg(Rgb(0, 0, 0))); let off_style = Some(Style::default().fg(Tui::g(255))); let on_style = Some(Style::default().fg(Rgb(255,0,0)).bg(color.base.rgb).bold()); - Fill::Y(Fixed::X(self.keys_width, Thunk::new(move|to: &mut TuiOut|{ + h_full(w_exact(self.keys_width, Thunk::new(move|to: &mut Tui|{ let XYWH(x, y0, _w, _h) = to.area(); for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) { to.blit(&to_key(note), x, screen_y, key_style); @@ -2593,8 +2587,8 @@ mod audio { } }))) } - fn timeline (&self) -> impl Content + '_ { - Fill::X(Fixed::Y(1, Thunk::new(move|to: &mut TuiOut|{ + fn timeline (&self) -> impl Draw + '_ { + w_full(h_exact(1, Thunk::new(move|to: &mut Tui|{ let XYWH(x, y, w, _h) = to.area(); let style = Some(Style::default().dim()); let length = self.clip.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1); @@ -2635,7 +2629,7 @@ mod audio { fn clip_mut (&mut self) -> &mut Option>> { &mut self.clip } /// Determine the required space to render the clip. fn buffer_size (&self, clip: &MidiClip) -> (usize, usize) { (clip.length / self.range.time_zoom().get(), 128) } - fn redraw (&self) { + fn redraw(self) { *self.buffer.write().unwrap() = if let Some(clip) = self.clip.as_ref() { let clip = clip.read().unwrap(); let buf_size = self.buffer_size(&clip); @@ -2671,9 +2665,9 @@ mod audio { self.colors[if self.on[pitch] { 2 } else { match pitch { 0 | 2 | 4 | 5 | 6 | 8 | 10 => 0, _ => 1 } }] } } - impl HasContent for OctaveVertical { - fn content (&self) -> impl Content { - row!( + impl OctaveVertical { + fn tui (&self) -> impl Draw { + east!( Tui::fg_bg(self.color(0), self.color(1), "▙"), Tui::fg_bg(self.color(2), self.color(3), "▙"), Tui::fg_bg(self.color(4), self.color(5), "▌"), @@ -3107,8 +3101,8 @@ mod audio { Ok(()) } } - impl Draw for SampleAdd { - fn draw (&self, _to: &mut TuiOut) { + impl Draw for SampleAdd { + fn draw (self, _to: &mut Tui) -> Usually> { todo!() } } @@ -3129,9 +3123,9 @@ mod audio { } } - fn draw_viewer (sample: Option<&Arc>>) -> impl Content + use<'_> { + fn draw_viewer (sample: Option<&Arc>>) -> impl Draw + use<'_> { let min_db = -64.0; - Thunk::new(move|to: &mut TuiOut|{ + Thunk::new(move|to: &mut Tui|{ let XYWH(x, y, width, height) = to.area(); let area = Rect { x, y, width, height }; if let Some(sample) = &sample { @@ -3216,7 +3210,7 @@ mod audio { } fn draw_sample ( - to: &mut TuiOut, x: u16, y: u16, note: Option<&u7>, sample: &Sample, focus: bool + to: &mut Tui, x: u16, y: u16, note: Option<&u7>, sample: &Sample, focus: bool ) -> Usually { let style = if focus { Style::default().green() } else { Style::default() }; if focus { @@ -3340,8 +3334,8 @@ mod audio { } } - impl Draw for Lv2 { - fn draw (&self, to: &mut TuiOut) { + impl Draw for Lv2 { + fn draw(self, to: &mut Tui) { let area = to.area(); let XYWH(x, y, _, height) = area; let mut width = 20u16; @@ -3373,7 +3367,7 @@ mod audio { } - fn draw_header (state: &Lv2, to: &mut TuiOut, x: u16, y: u16, w: u16) { + fn draw_header (state: &Lv2, to: &mut Tui, x: u16, y: u16, w: u16) { let style = Style::default().gray(); let label1 = format!(" {}", state.name); to.blit(&label1, x + 1, y, Some(style.white().bold())); @@ -3387,8 +3381,8 @@ mod audio { mod pool { use crate::*; - has_clips!(|self: Pool|self.clips); has_clip!(|self: Pool|self.clips().get(self.clip_index()).map(|c|c.clone())); + impl_has_clips!(|self: Pool|self.clips); impl_from!(Pool: |clip:&Arc>|{ let model = Self::default(); model.clips.write().unwrap().push(clip.clone()); @@ -3523,14 +3517,14 @@ mod pool { fn _color_random (&self) -> ItemColor { ItemColor::random() } } - impl<'a> HasContent for PoolView<'a> { - fn content (&self) -> impl Content { + impl<'a> PoolView<'a> { + fn tui (&self) -> impl Draw { let Self(pool) = self; //let color = self.1.clip().map(|c|c.read().unwrap().color).unwrap_or_else(||Tui::g(32).into()); - //let on_bg = |x|x;//Bsp::b(Repeat(" "), Tui::bg(color.darkest.rgb, x)); + //let on_bg = |x|x;//below(Repeat(" "), Tui::bg(color.darkest.rgb, x)); //let border = |x|x;//Outer(Style::default().fg(color.dark.rgb).bg(color.darkest.rgb)).enclose(x); //let height = pool.clips.read().unwrap().len() as u16; - Fixed::X(20, Fill::Y(Align::n(Map::new( + w_exact(20, h_full(origin_n(iter( ||pool.clips().clone().into_iter(), move|clip: Arc>, i: usize|{ let item_height = 1; @@ -3541,26 +3535,26 @@ mod pool { let fg = color.lightest.rgb; let name = if false { format!(" {i:>3}") } else { format!(" {i:>3} {name}") }; let length = if false { String::default() } else { format!("{length} ") }; - Fixed::Y(1, map_south(item_offset, item_height, Tui::bg(bg, lay!( - Fill::X(Align::w(Tui::fg(fg, Tui::bold(selected, name)))), - Fill::X(Align::e(Tui::fg(fg, Tui::bold(selected, length)))), - Fill::X(Align::w(When::new(selected, Tui::bold(true, Tui::fg(Tui::g(255), "▶"))))), - Fill::X(Align::e(When::new(selected, Tui::bold(true, Tui::fg(Tui::g(255), "◀"))))), + h_exact(1, iter_south(item_offset, item_height, Tui::bg(bg, below!( + w_full(origin_w(Tui::fg(fg, Tui::bold(selected, name)))), + w_full(origin_e(Tui::fg(fg, Tui::bold(selected, length)))), + w_full(origin_w(When::new(selected, Tui::bold(true, Tui::fg(Tui::g(255), "▶"))))), + w_full(origin_e(When::new(selected, Tui::bold(true, Tui::fg(Tui::g(255), "◀"))))), )))) })))) } } - impl HasContent for ClipLength { - fn content (&self) -> impl Content { + impl ClipLength { + fn tui (&self) -> impl Draw { use ClipLengthFocus::*; let bars = ||self.bars_string(); let beats = ||self.beats_string(); let ticks = ||self.ticks_string(); match self.focus { - None => row!(" ", bars(), ".", beats(), ".", ticks()), - Some(Bar) => row!("[", bars(), "]", beats(), ".", ticks()), - Some(Beat) => row!(" ", bars(), "[", beats(), "]", ticks()), - Some(Tick) => row!(" ", bars(), ".", beats(), "[", ticks()), + None => east!(" ", bars(), ".", beats(), ".", ticks()), + Some(Bar) => east!("[", bars(), "]", beats(), ".", ticks()), + Some(Beat) => east!(" ", bars(), "[", beats(), "]", ticks()), + Some(Tick) => east!(" ", bars(), ".", beats(), "[", ticks()), } } } @@ -3848,14 +3842,6 @@ mod config { Ok(map) } } - - // Each mode contains a view, so here we should be drawing it. - // I'm not sure what's going on with this code, though. - impl Draw for Mode { - fn draw (&self, _to: &mut TuiOut) { - //self.content().draw(to) - } - } } mod dialog { diff --git a/app/tek_struct.rs b/app/tek_struct.rs index 34d2277f..6af03ff8 100644 --- a/app/tek_struct.rs +++ b/app/tek_struct.rs @@ -31,7 +31,7 @@ use builder_pattern::Builder; /// Must not be dropped for the duration of the process pub jack: Jack<'static>, /// Display size - pub size: Measure, + pub size: Measure, /// Performance counter pub perf: PerfModel, /// Available view modes and input bindings @@ -468,9 +468,9 @@ use builder_pattern::Builder; /// TODO rename to "render_cache" or smth pub arranger: Arc>, /// Display size - pub size: Measure, + pub size: Measure, /// Display size of clips area - pub size_inner: Measure, + pub size_inner: Measure, /// Source of time #[cfg(feature = "clock")] pub clock: Clock, /// Allows one MIDI clip to be edited @@ -509,7 +509,7 @@ use builder_pattern::Builder; pub filter: String, pub index: usize, pub scroll: usize, - pub size: Measure, + pub size: Measure, } pub(crate) struct EntriesIterator<'a> { @@ -598,7 +598,7 @@ pub struct DeviceAudio<'a>(pub &'a mut Device); /// ``` pub struct MidiEditor { /// Size of editor on screen - pub size: Measure, + pub size: Measure, /// View mode and state of editor pub mode: PianoHorizontal, } @@ -613,7 +613,7 @@ pub struct MidiEditor { /// Buffer where the whole clip is rerendered on change pub buffer: Arc>, /// Size of actual notes area - pub size: Measure, + pub size: Measure, /// The display window pub range: MidiSelection, /// The note cursor @@ -808,7 +808,7 @@ pub struct PoolView<'a>(pub &'a Pool); /// Currently active modal, if any. pub mode: Option, /// Size of rendered sampler. - pub size: Measure, + pub size: Measure, /// Lowest note displayed. pub note_lo: AtomicUsize, /// Currently selected note. diff --git a/app/tek_trait.rs b/app/tek_trait.rs index 2ee87476..57fdde33 100644 --- a/app/tek_trait.rs +++ b/app/tek_trait.rs @@ -119,7 +119,7 @@ pub trait MidiRange: TimeRange + NoteRange {} /// can be clocked in microseconds with f64 without losing precision. pub trait TimeUnit: InteriorMutable {} -pub trait HasClipsSize { fn clips_size (&self) -> &Measure; } +pub trait HasClipsSize { fn clips_size (&self) -> &Measure; } pub trait HasMidiClip { fn clip (&self) -> Option>>; @@ -327,16 +327,16 @@ pub trait HasTracks: AsRef> + AsMut> { pub trait HasTrack: AsRefOpt + AsMutOpt { fn track (&self) -> Option<&Track> { self.as_ref_opt() } fn track_mut (&mut self) -> Option<&mut Track> { self.as_mut_opt() } - #[cfg(feature = "port")] fn view_midi_ins_status <'a> (&'a self, theme: ItemTheme) -> impl Content + 'a { + #[cfg(feature = "port")] fn view_midi_ins_status <'a> (&'a self, theme: ItemTheme) -> impl Draw + 'a { self.track().map(move|track|view_ports_status(theme, "MIDI ins: ", &track.sequencer.midi_ins)) } - #[cfg(feature = "port")] fn view_midi_outs_status (&self, theme: ItemTheme) -> impl Content + '_ { + #[cfg(feature = "port")] fn view_midi_outs_status (&self, theme: ItemTheme) -> impl Draw + '_ { self.track().map(move|track|view_ports_status(theme, "MIDI outs: ", &track.sequencer.midi_outs)) } - #[cfg(feature = "port")] fn view_audio_ins_status (&self, theme: ItemTheme) -> impl Content { + #[cfg(feature = "port")] fn view_audio_ins_status (&self, theme: ItemTheme) -> impl Draw { self.track().map(move|track|view_ports_status(theme, "Audio ins: ", &track.audio_ins())) } - #[cfg(feature = "port")] fn view_audio_outs_status (&self, theme: ItemTheme) -> impl Content { + #[cfg(feature = "port")] fn view_audio_outs_status (&self, theme: ItemTheme) -> impl Draw { self.track().map(move|track|view_ports_status(theme, "Audio outs:", &track.audio_outs())) } } @@ -379,7 +379,7 @@ pub trait HasPlayClip: HasClock { *self.reset_mut() = true; } - fn play_status (&self) -> impl Content { + fn play_status (&self) -> impl Draw { let (name, color): (Arc, ItemTheme) = if let Some((_, Some(clip))) = self.play_clip() { let MidiClip { ref name, color, .. } = *clip.read().unwrap(); (name.clone(), color) @@ -389,10 +389,10 @@ pub trait HasPlayClip: HasClock { let time: String = self.pulses_since_start_looped() .map(|(times, time)|format!("{:>3}x {:>}", times+1.0, self.clock().timebase.format_beats_1(time))) .unwrap_or_else(||String::from(" ")).into(); - FieldV(color, "Now:", format!("{} {}", time, name)) + field_v(color, "Now:", format!("{} {}", time, name)) } - fn next_status (&self) -> impl Content { + fn next_status (&self) -> impl Draw { let mut time: Arc = String::from("--.-.--").into(); let mut name: Arc = String::from("").into(); let mut color = ItemTheme::G[64]; @@ -425,7 +425,7 @@ pub trait HasPlayClip: HasClock { name = "Stop".to_string().into(); } }; - FieldV(color, "Next:", format!("{} {}", time, name)) + field_v(color, "Next:", format!("{} {}", time, name)) } } @@ -489,7 +489,7 @@ pub trait MidiRecord: MidiMonitor + HasClock + HasPlayClip { } } -pub trait MidiViewer: Measured + MidiRange + MidiPoint + Debug + Send + Sync { +pub trait MidiViewer: Measured + MidiRange + MidiPoint + Debug + Send + Sync { fn buffer_size (&self, clip: &MidiClip) -> (usize, usize); fn redraw (&self); fn clip (&self) -> &Option>>; @@ -557,20 +557,20 @@ pub trait MidiViewer: Measured + MidiRange + MidiPoint + Debug + Send + pub trait ClipsView: TracksView + ScenesView { fn view_scenes_clips <'a> (&'a self) - -> impl Content + 'a + -> impl Draw + 'a { - self.clips_size().of(Fill::XY(Bsp::a( - Fill::XY(Align::se(Tui::fg(Green, format!("{}x{}", self.clips_size().w(), self.clips_size().h())))), - Thunk::new(|to: &mut TuiOut|for ( + self.clips_size().of(wh_full(above( + wh_full(origin_se(Tui::fg(Green, format!("{}x{}", self.clips_size().w(), self.clips_size().h())))), + Thunk::new(|to: &mut Tui|for ( track_index, track, _, _ ) in self.tracks_with_sizes() { - to.place(&Fixed::X(track.width as u16, - Fill::Y(self.view_track_clips(track_index, track)))) + to.place(&w_exact(track.width as u16, + h_full(self.view_track_clips(track_index, track)))) })))) } - fn view_track_clips <'a> (&'a self, track_index: usize, track: &'a Track) -> impl Content + 'a { - Thunk::new(move|to: &mut TuiOut|for ( + fn view_track_clips <'a> (&'a self, track_index: usize, track: &'a Track) -> impl Draw + 'a { + Thunk::new(move|to: &mut Tui|for ( scene_index, scene, .. ) in self.scenes_with_sizes() { let (name, theme): (Arc, ItemTheme) = if let Some(Some(clip)) = &scene.clips.get(track_index) { @@ -609,14 +609,14 @@ pub trait ClipsView: TracksView + ScenesView { Self::H_SCENE as usize } as u16; - to.place(&Fixed::XY(w, y, Bsp::b( - Fill::XY(Outer(true, Style::default().fg(outline))), - Fill::XY(Bsp::b( - Bsp::b( - Tui::fg_bg(outline, bg, Fill::XY("")), - Fill::XY(Align::nw(Tui::fg_bg(fg, bg, Tui::bold(true, name)))), + to.place(&wh_exact(w, y, below( + wh_full(Outer(true, Style::default().fg(outline))), + wh_full(below( + below( + Tui::fg_bg(outline, bg, wh_full("")), + wh_full(origin_nw(Tui::fg_bg(fg, bg, Tui::bold(true, name)))), ), - Fill::XY(When::new(self.selection().track() == Some(track_index) + wh_full(when(self.selection().track() == Some(track_index) && self.selection().scene() == Some(scene_index) && self.is_editing(), self.editor()))))))); }) @@ -624,7 +624,7 @@ pub trait ClipsView: TracksView + ScenesView { } -pub trait TracksView: ScenesView + HasMidiIns + HasMidiOuts + HasTrackScroll + Measured { +pub trait TracksView: ScenesView + HasMidiIns + HasMidiOuts + HasTrackScroll + Measured { fn tracks_width_available (&self) -> u16 { (self.measure_width() as u16).saturating_sub(40) @@ -647,68 +647,68 @@ pub trait TracksView: ScenesView + HasMidiIns + HasMidiOuts + HasTrackScroll + M }) } - fn view_track_names (&self, theme: ItemTheme) -> impl Content { + fn view_track_names (&self, theme: ItemTheme) -> impl Draw { let track_count = self.tracks().len(); let scene_count = self.scenes().len(); let selected = self.selection(); - let button = Bsp::s( + let button = south( button_3("t", "rack ", format!("{}{track_count}", selected.track() .map(|track|format!("{track}/")).unwrap_or_default()), false), button_3("s", "cene ", format!("{}{scene_count}", selected.scene() .map(|scene|format!("{scene}/")).unwrap_or_default()), false)); - let button_2 = Bsp::s( + let button_2 = south( button_2("T", "+", false), button_2("S", "+", false)); view_track_row_section(theme, button, button_2, Tui::bg(theme.darker.rgb, - Fixed::Y(2, Thunk::new(|to: &mut TuiOut|{ + h_exact(2, Thunk::new(|to: &mut Tui|{ for (index, track, x1, _x2) in self.tracks_with_sizes() { - to.place(&Push::X(x1 as u16, Fixed::X(track_width(index, track), + to.place(&x_push(x1 as u16, w_exact(track_width(index, track), Tui::bg(if selected.track() == Some(index) { track.color.light.rgb } else { track.color.base.rgb - }, Bsp::s(Fill::X(Align::nw(Bsp::e( + }, south(w_full(origin_nw(east( format!("·t{index:02} "), Tui::fg(Rgb(255, 255, 255), Tui::bold(true, &track.name)) ))), ""))) ));}})))) } - fn view_track_outputs <'a> (&'a self, theme: ItemTheme, _h: u16) -> impl Content { + fn view_track_outputs <'a> (&'a self, theme: ItemTheme, _h: u16) -> impl Draw { view_track_row_section(theme, - Bsp::s(Fill::X(Align::w(button_2("o", "utput", false))), - Thunk::new(|to: &mut TuiOut|for port in self.midi_outs().iter() { - to.place(&Fill::X(Align::w(port.port_name()))); + south(w_full(origin_w(button_2("o", "utput", false))), + Thunk::new(|to: &mut Tui|for port in self.midi_outs().iter() { + to.place(&w_full(origin_w(port.port_name()))); })), button_2("O", "+", false), - Tui::bg(theme.darker.rgb, Align::w(Thunk::new(|to: &mut TuiOut|{ + Tui::bg(theme.darker.rgb, origin_w(Thunk::new(|to: &mut Tui|{ for (index, track, _x1, _x2) in self.tracks_with_sizes() { - to.place(&Fixed::X(track_width(index, track), - Align::nw(Fill::Y(Map::south(1, ||track.sequencer.midi_outs.iter(), + to.place(&w_exact(track_width(index, track), + origin_nw(h_full(iter_south(1, ||track.sequencer.midi_outs.iter(), |port, index|Tui::fg(Rgb(255, 255, 255), - Fixed::Y(1, Tui::bg(track.color.dark.rgb, Fill::X(Align::w( + h_exact(1, Tui::bg(track.color.dark.rgb, w_full(origin_w( format!("·o{index:02} {}", port.port_name())))))))))));}})))) } - fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Content { + fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Draw { let mut h = 0u16; for track in self.tracks().iter() { h = h.max(track.sequencer.midi_ins.len() as u16); } - let content = Thunk::new(move|to: &mut TuiOut|for (index, track, _x1, _x2) in self.tracks_with_sizes() { - to.place(&Fixed::XY(track_width(index, track), h + 1, - Align::nw(Bsp::s( + let content = Thunk::new(move|to: &mut Tui|for (index, track, _x1, _x2) in self.tracks_with_sizes() { + to.place(&wh_exact(track_width(index, track), h + 1, + origin_nw(south( Tui::bg(track.color.base.rgb, - Fill::X(Align::w(row!( - Either::new(track.sequencer.monitoring, Tui::fg(Green, "●mon "), "·mon "), - Either::new(track.sequencer.recording, Tui::fg(Red, "●rec "), "·rec "), - Either::new(track.sequencer.overdub, Tui::fg(Yellow, "●dub "), "·dub "), + w_full(origin_w(east!( + either(track.sequencer.monitoring, Tui::fg(Green, "●mon "), "·mon "), + either(track.sequencer.recording, Tui::fg(Red, "●rec "), "·rec "), + either(track.sequencer.overdub, Tui::fg(Yellow, "●dub "), "·dub "), )))), - Map::south(1, ||track.sequencer.midi_ins.iter(), + iter_south(1, ||track.sequencer.midi_ins.iter(), |port, index|Tui::fg_bg(Rgb(255, 255, 255), track.color.dark.rgb, - Fill::X(Align::w(format!("·i{index:02} {}", port.port_name()))))))))); + w_full(origin_w(format!("·i{index:02} {}", port.port_name()))))))))); }); view_track_row_section(theme, button_2("i", "nput", false), button_2("I", "+", false), - Tui::bg(theme.darker.rgb, Align::w(content))) + Tui::bg(theme.darker.rgb, origin_w(content))) } } @@ -739,13 +739,13 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize + }) } - fn view_scenes_names (&self) -> impl Content { - Fixed::X(20, Thunk::new(|to: &mut TuiOut|for (index, scene, ..) in self.scenes_with_sizes() { + fn view_scenes_names (&self) -> impl Draw { + w_exact(20, Thunk::new(|to: &mut Tui|for (index, scene, ..) in self.scenes_with_sizes() { to.place(&self.view_scene_name(index, scene)); })) } - fn view_scene_name <'a> (&'a self, index: usize, scene: &'a Scene) -> impl Content + 'a { + fn view_scene_name <'a> (&'a self, index: usize, scene: &'a Scene) -> impl Draw + 'a { let h = if self.selection().scene() == Some(index) && let Some(_editor) = self.editor() { 7 } else { @@ -756,13 +756,13 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize + } else { scene.color.base.rgb }; - let a = Fill::X(Align::w(Bsp::e(format!("·s{index:02} "), + let a = w_full(origin_w(east(format!("·s{index:02} "), Tui::fg(Tui::g(255), Tui::bold(true, &scene.name))))); - let b = When::new(self.selection().scene() == Some(index) && self.is_editing(), - Fill::XY(Align::nw(Bsp::s( + let b = when(self.selection().scene() == Some(index) && self.is_editing(), + wh_full(origin_nw(south( self.editor().as_ref().map(|e|e.clip_status()), self.editor().as_ref().map(|e|e.edit_status()))))); - Fixed::XY(20, h, Tui::bg(bg, Align::nw(Bsp::s(a, b)))) + wh_exact(20, h, Tui::bg(bg, origin_nw(south(a, b)))) } } diff --git a/tengri b/tengri index 9dbf4fca..cf57f449 160000 --- a/tengri +++ b/tengri @@ -1 +1 @@ -Subproject commit 9dbf4fcab5f31a68e3d24c8f8f7fc866159e89f1 +Subproject commit cf57f44933c45507e8de072e32c284f20f12ac7a