well, 55 errors until up to date

and then we fix
This commit is contained in:
okay stopped screaming 2026-03-21 20:33:51 +02:00
parent 0577309b19
commit 7ff1d989a9
5 changed files with 333 additions and 344 deletions

View file

@ -22,10 +22,13 @@ pub(crate) use tengri::{
*, *,
lang::*, lang::*,
play::*, play::*,
keys::*,
sing::*, sing::*,
time::*, time::*,
draw::*, draw::*,
tui::*, term::*,
color::*,
space::*,
crossterm::event::{Event, KeyEvent}, crossterm::event::{Event, KeyEvent},
ratatui::{ ratatui::{
self, self,
@ -953,13 +956,13 @@ mod view {
/// ``` /// ```
/// let _ = tek::view_logo(); /// let _ = tek::view_logo();
/// ``` /// ```
pub fn view_logo () -> impl Content<TuiOut> { pub fn view_logo () -> impl Draw<Tui> {
Fixed::XY(32, 7, Tui::bold(true, Tui::fg(Rgb(240,200,180), col!{ wh_exact(32, 7, Tui::bold(true, Tui::fg(Rgb(240,200,180), south!{
Fixed::Y(1, ""), h_exact(1, ""),
Fixed::Y(1, ""), h_exact(1, ""),
Fixed::Y(1, "~~ ╓─╥─╖ ╓──╖ ╥ ╖ ~~~~~~~~~~~~"), h_exact(1, "~~ ╓─╥─╖ ╓──╖ ╥ ╖ ~~~~~~~~~~~~"),
Fixed::Y(1, Bsp::e("~~~~ ║ ~ ╟─╌ ~╟─< ~~ ", Bsp::e(Tui::fg(Rgb(230,100,40), "v0.3.0"), " ~~"))), h_exact(1, east("~~~~ ║ ~ ╟─╌ ~╟─< ~~ ", east(Tui::fg(Rgb(230,100,40), "v0.3.0"), " ~~"))),
Fixed::Y(1, "~~~~ ╨ ~ ╙──╜ ╨ ╜ ~~~~~~~~~~~~"), h_exact(1, "~~~~ ╨ ~ ╙──╜ ╨ ╜ ~~~~~~~~~~~~"),
}))) })))
} }
@ -973,14 +976,14 @@ mod view {
bpm: Arc<RwLock<String>>, bpm: Arc<RwLock<String>>,
beat: Arc<RwLock<String>>, beat: Arc<RwLock<String>>,
time: Arc<RwLock<String>>, time: Arc<RwLock<String>>,
) -> impl Content<TuiOut> { ) -> impl Draw<Tui> {
let theme = ItemTheme::G[96]; let theme = ItemTheme::G[96];
Tui::bg(Black, row!(Bsp::a( Tui::bg(Black, east!(above(
Fill::XY(Align::w(button_play_pause(play))), wh_full(origin_w(button_play_pause(play))),
Fill::XY(Align::e(row!( wh_full(origin_e(east!(
FieldH(theme, "BPM", bpm), field_h(theme, "BPM", bpm),
FieldH(theme, "Beat", beat), field_h(theme, "Beat", beat),
FieldH(theme, "Time", time), field_h(theme, "Time", time),
))) )))
))) )))
} }
@ -995,14 +998,14 @@ mod view {
sr: Arc<RwLock<String>>, sr: Arc<RwLock<String>>,
buf: Arc<RwLock<String>>, buf: Arc<RwLock<String>>,
lat: Arc<RwLock<String>>, lat: Arc<RwLock<String>>,
) -> impl Content<TuiOut> { ) -> impl Draw<Tui> {
let theme = ItemTheme::G[96]; let theme = ItemTheme::G[96];
Tui::bg(Black, row!(Bsp::a( Tui::bg(Black, east!(above(
Fill::XY(Align::w(sel.map(|sel|FieldH(theme, "Selected", sel)))), wh_full(origin_w(sel.map(|sel|field_h(theme, "Selected", sel)))),
Fill::XY(Align::e(row!( wh_full(origin_e(east!(
FieldH(theme, "SR", sr), field_h(theme, "SR", sr),
FieldH(theme, "Buf", buf), field_h(theme, "Buf", buf),
FieldH(theme, "Lat", lat), field_h(theme, "Lat", lat),
))) )))
))) )))
} }
@ -1010,17 +1013,17 @@ mod view {
/// ``` /// ```
/// let _ = tek::button_play_pause(true); /// let _ = tek::button_play_pause(true);
/// ``` /// ```
pub fn button_play_pause (playing: bool) -> impl Content<TuiOut> { pub fn button_play_pause (playing: bool) -> impl Draw<Tui> {
let compact = true;//self.is_editing(); let compact = true;//self.is_editing();
Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) },
Either::new(compact, either(compact,
Thunk::new(move|to: &mut TuiOut|to.place(&Fixed::X(9, Either::new(playing, Thunk::new(move|to: &mut Tui|to.place(&w_exact(9, either(playing,
Tui::fg(Rgb(0, 255, 0), " PLAYING "), Tui::fg(Rgb(0, 255, 0), " PLAYING "),
Tui::fg(Rgb(255, 128, 0), " STOPPED "))) Tui::fg(Rgb(255, 128, 0), " STOPPED ")))
)), )),
Thunk::new(move|to: &mut TuiOut|to.place(&Fixed::X(5, Either::new(playing, Thunk::new(move|to: &mut Tui|to.place(&w_exact(5, either(playing,
Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), Tui::fg(Rgb(0, 255, 0), south(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)),
Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))) Tui::fg(Rgb(255, 128, 0), south(" ▗▄▖ ", " ▝▀▘ ",))))
)) ))
) )
) )
@ -1028,12 +1031,12 @@ mod view {
#[cfg(feature = "track")] pub fn view_track_row_section ( #[cfg(feature = "track")] pub fn view_track_row_section (
_theme: ItemTheme, _theme: ItemTheme,
button: impl Content<TuiOut>, button: impl Draw<Tui>,
button_add: impl Content<TuiOut>, button_add: impl Draw<Tui>,
content: impl Content<TuiOut>, content: impl Draw<Tui>,
) -> impl Content<TuiOut> { ) -> impl Draw<Tui> {
Bsp::w(Fill::Y(Fixed::X(4, Align::nw(button_add))), west(h_full(w_exact(4, origin_nw(button_add))),
Bsp::e(Fixed::X(20, Fill::Y(Align::nw(button))), Fill::XY(Align::c(content)))) 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 fg = tengri::ratatui::style::Color::Green;
/// let _ = tek::view_wrap(bg, fg, "and then blue, too!"); /// let _ = tek::view_wrap(bg, fg, "and then blue, too!");
/// ``` /// ```
pub fn view_wrap (bg: Color, fg: Color, content: impl Content<TuiOut>) -> impl Content<TuiOut> { pub fn view_wrap (bg: Color, fg: Color, content: impl Draw<Tui>) -> impl Draw<Tui> {
let left = Tui::fg_bg(bg, Reset, Fixed::X(1, Repeat::Y(""))); let left = Tui::fg_bg(bg, Reset, w_exact(1, y_repeat("")));
let right = Tui::fg_bg(bg, Reset, Fixed::X(1, Repeat::Y(""))); let right = Tui::fg_bg(bg, Reset, w_exact(1, y_repeat("")));
Bsp::e(left, Bsp::w(right, Tui::fg_bg(fg, bg, content))) east(left, west(right, Tui::fg_bg(fg, bg, content)))
} }
/// ``` /// ```
/// let _ = tek::view_meter("", 0.0); /// let _ = tek::view_meter("", 0.0);
/// let _ = tek::view_meters(&[0.0, 0.0]); /// let _ = tek::view_meters(&[0.0, 0.0]);
/// ``` /// ```
pub fn view_meter <'a> (label: &'a str, value: f32) -> impl Content<TuiOut> + 'a { pub fn view_meter <'a> (label: &'a str, value: f32) -> impl Draw<Tui> + 'a {
col!( south!(
FieldH(ItemTheme::G[128], label, format!("{:>+9.3}", value)), field_h(ItemTheme::G[128], label, format!("{:>+9.3}", value)),
Fixed::XY(if value >= 0.0 { 13 } wh_exact(if value >= 0.0 { 13 }
else if value >= -1.0 { 12 } else if value >= -1.0 { 12 }
else if value >= -2.0 { 11 } else if value >= -2.0 { 11 }
else if value >= -3.0 { 10 } else if value >= -3.0 { 10 }
@ -1072,47 +1075,47 @@ mod view {
else { Green }, ()))) else { Green }, ())))
} }
pub fn view_meters (values: &[f32;2]) -> impl Content<TuiOut> + use<'_> { pub fn view_meters (values: &[f32;2]) -> impl Draw<Tui> + use<'_> {
let left = format!("L/{:>+9.3}", values[0]); let left = format!("L/{:>+9.3}", values[0]);
let right = format!("R/{:>+9.3}", values[1]); let right = format!("R/{:>+9.3}", values[1]);
Bsp::s(left, right) south(left, right)
} }
pub fn draw_info (sample: Option<&Arc<RwLock<Sample>>>) -> impl Content<TuiOut> + use<'_> { pub fn draw_info (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> + use<'_> {
When::new(sample.is_some(), Thunk::new(move|to: &mut TuiOut|{ when(sample.is_some(), Thunk::new(move|to: &mut Tui|{
let sample = sample.unwrap().read().unwrap(); let sample = sample.unwrap().read().unwrap();
let theme = sample.color; let theme = sample.color;
to.place(&row!( to.place(&east!(
FieldH(theme, "Name", format!("{:<10}", sample.name.clone())), field_h(theme, "Name", format!("{:<10}", sample.name.clone())),
FieldH(theme, "Length", format!("{:<8}", sample.channels[0].len())), field_h(theme, "Length", format!("{:<8}", sample.channels[0].len())),
FieldH(theme, "Start", format!("{:<8}", sample.start)), field_h(theme, "Start", format!("{:<8}", sample.start)),
FieldH(theme, "End", format!("{:<8}", sample.end)), field_h(theme, "End", format!("{:<8}", sample.end)),
FieldH(theme, "Trans", "0"), field_h(theme, "Trans", "0"),
FieldH(theme, "Gain", format!("{}", sample.gain)), field_h(theme, "Gain", format!("{}", sample.gain)),
)) ))
})) }))
} }
pub fn draw_info_v (sample: Option<&Arc<RwLock<Sample>>>) -> impl Content<TuiOut> + use<'_> { pub fn draw_info_v (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> + use<'_> {
Either::new(sample.is_some(), Thunk::new(move|to: &mut TuiOut|{ either(sample.is_some(), Thunk::new(move|to: &mut Tui|{
let sample = sample.unwrap().read().unwrap(); let sample = sample.unwrap().read().unwrap();
let theme = sample.color; let theme = sample.color;
to.place(&Fixed::X(20, col!( to.place(&w_exact(20, south!(
Fill::X(Align::w(FieldH(theme, "Name ", format!("{:<10}", sample.name.clone())))), w_full(origin_w(field_h(theme, "Name ", format!("{:<10}", sample.name.clone())))),
Fill::X(Align::w(FieldH(theme, "Length", format!("{:<8}", sample.channels[0].len())))), w_full(origin_w(field_h(theme, "Length", format!("{:<8}", sample.channels[0].len())))),
Fill::X(Align::w(FieldH(theme, "Start ", format!("{:<8}", sample.start)))), w_full(origin_w(field_h(theme, "Start ", format!("{:<8}", sample.start)))),
Fill::X(Align::w(FieldH(theme, "End ", format!("{:<8}", sample.end)))), w_full(origin_w(field_h(theme, "End ", format!("{:<8}", sample.end)))),
Fill::X(Align::w(FieldH(theme, "Trans ", "0"))), w_full(origin_w(field_h(theme, "Trans ", "0"))),
Fill::X(Align::w(FieldH(theme, "Gain ", format!("{}", sample.gain)))), 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."), Tui::bold(true, "× No sample."),
"[r] record", "[r] record",
"[Shift-F9] import", "[Shift-F9] import",
))))) )))))
} }
pub fn draw_status (sample: Option<&Arc<RwLock<Sample>>>) -> impl Content<TuiOut> { pub fn draw_status (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> {
Tui::bold(true, Tui::fg(Tui::g(224), sample Tui::bold(true, Tui::fg(Tui::g(224), sample
.map(|sample|{ .map(|sample|{
let sample = sample.read().unwrap(); let sample = sample.read().unwrap();
@ -1121,31 +1124,31 @@ mod view {
.unwrap_or_else(||"No sample".to_string()))) .unwrap_or_else(||"No sample".to_string())))
} }
pub fn view_track_header (theme: ItemTheme, content: impl Content<TuiOut>) -> impl Content<TuiOut> { pub fn view_track_header (theme: ItemTheme, content: impl Draw<Tui>) -> impl Draw<Tui> {
Fixed::X(12, Tui::bg(theme.darker.rgb, Fill::X(Align::e(content)))) 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]) pub fn view_ports_status <'a, T: JackPort> (theme: ItemTheme, title: &'a str, ports: &'a [T])
-> impl Content<TuiOut> + use<'a, T> -> impl Draw<Tui> + use<'a, T>
{ {
let ins = ports.len() as u16; let ins = ports.len() as u16;
let frame = Outer(true, Style::default().fg(Tui::g(96))); let frame = Outer(true, Style::default().fg(Tui::g(96)));
let iter = move||ports.iter(); let iter = move||ports.iter();
let names = Map::south(1, iter, move|port, index|Fill::Y(Align::w(format!(" {index} {}", port.port_name())))); let names = iter_south(1, iter, move|port, index|h_full(origin_w(format!(" {index} {}", port.port_name()))));
let field = FieldV(theme, title, names); let field = field_v(theme, title, names);
Fixed::XY(20, 1 + ins, frame.enclose(Fixed::XY(20, 1 + ins, field))) wh_exact(20, 1 + ins, frame.enclose(wh_exact(20, 1 + ins, field)))
} }
pub fn io_ports <'a, T: PortsSizes<'a>> ( pub fn io_ports <'a, T: PortsSizes<'a>> (
fg: Color, bg: Color, iter: impl Fn()->T + Send + Sync + 'a fg: Color, bg: Color, items: impl Fn()->T + Send + Sync + 'a
) -> impl Content<TuiOut> + 'a { ) -> impl Draw<Tui> + 'a {
Map::new(iter, move|( iter(items, move|(
_index, name, connections, y, y2 _index, name, connections, y, y2
): (usize, &'a Arc<str>, &'a [Connect], usize, usize), _| ): (usize, &'a Arc<str>, &'a [Connect], usize, usize), _|
map_south(y as u16, (y2-y) as u16, Bsp::s( iter_south(y as u16, (y2-y) as u16, south(
Fill::Y(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(Bsp::e(&" 󰣲 ", name))))), h_full(Tui::bold(true, Tui::fg_bg(fg, bg, origin_w(east(&" 󰣲 ", name))))),
Map::new(||connections.iter(), move|connect: &'a Connect, index|map_south(index as u16, 1, iter(||connections.iter(), move|connect: &'a Connect, index|iter_south(index as u16, 1,
Fill::Y(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, h_full(origin_w(Tui::bold(false, Tui::fg_bg(fg, bg,
&connect.info))))))))) &connect.info)))))))))
} }
} }
@ -1210,5 +1213,5 @@ pub const NOTE_NAMES: [&str; 128] = [
pub(crate) const HEADER: &'static str = r#" pub(crate) const HEADER: &'static str = r#"
~ ~~~ ~ ~ ~~ ~ ~ ~ ~~ ~ ~ ~ ~ ~ ~~~ ~ ~ ~~ ~ ~ ~ ~~ ~ ~ ~ ~
~ v0.4.0, 2026 winter (or is it) ~ term ~ v0.4.0, 2026 winter (or is it) ~
~ ~ ~~~ ~ ~ ~ ~ ~~~ ~~~ ~ ~~ "#; ~ ~ ~~~ ~ ~ ~ ~ ~~~ ~~~ ~ ~~ "#;

View file

@ -73,7 +73,7 @@ impl <T: AsRefOpt<MidiEditor>+AsMutOpt<MidiEditor>> HasE
impl <T: AsRefOpt<Scene>+AsMutOpt<Scene>+Send+Sync> HasScene for T {} impl <T: AsRefOpt<Scene>+AsMutOpt<Scene>+Send+Sync> HasScene for T {}
impl <T: AsRefOpt<Track>+AsMutOpt<Track>+Send+Sync> HasTrack for T {} impl <T: AsRefOpt<Track>+AsMutOpt<Track>+Send+Sync> HasTrack for T {}
impl <T: NotePoint+TimePoint> MidiPoint for T {} impl <T: NotePoint+TimePoint> MidiPoint for T {}
impl <T: ScenesView+HasMidiIns+HasMidiOuts+HasTrackScroll+Measured<TuiOut>> TracksView for T {} impl <T: ScenesView+HasMidiIns+HasMidiOuts+HasTrackScroll+Measured<Tui>> TracksView for T {}
impl <T: TimeRange+NoteRange> MidiRange for T {} impl <T: TimeRange+NoteRange> MidiRange for T {}
impl <T: TracksView+ScenesView+Send+Sync> ClipsView for T {} impl <T: TracksView+ScenesView+Send+Sync> ClipsView for T {}
@ -84,24 +84,24 @@ mod app {
impl_has!(Vec<MidiOutput>: |self: App|self.project.midi_outs); impl_has!(Vec<MidiOutput>: |self: App|self.project.midi_outs);
impl_has!(Dialog: |self: App|self.dialog); impl_has!(Dialog: |self: App|self.dialog);
impl_has!(Jack<'static>: |self: App|self.jack); impl_has!(Jack<'static>: |self: App|self.jack);
impl_has!(Measure<TuiOut>: |self: App|self.size); impl_has!(Measure<Tui>: |self: App|self.size);
impl_has!(Pool: |self: App|self.pool); impl_has!(Pool: |self: App|self.pool);
impl_has!(Selection: |self: App|self.project.selection); impl_has!(Selection: |self: App|self.project.selection);
impl_as_ref!(Vec<Scene>: |self: App|self.project.as_ref()); impl_as_ref!(Vec<Scene>: |self: App|self.project.as_ref());
impl_as_mut!(Vec<Scene>: |self: App|self.project.as_mut()); impl_as_mut!(Vec<Scene>: |self: App|self.project.as_mut());
impl_as_ref_opt!(MidiEditor: |self: App|self.project.as_ref_opt()); impl_as_ref_opt!(MidiEditor: |self: App|self.project.as_ref_opt());
impl_as_mut_opt!(MidiEditor: |self: App|self.project.as_mut_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); 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 commands = collect_commands(self, input)?;
let history = execute_commands(self, commands)?; let history = execute_commands(self, commands)?;
self.history.extend(history.into_iter()); self.history.extend(history.into_iter());
Ok(None) Ok(None)
}); });
impl Draw<TuiOut> for App { impl Draw<Tui> for App {
fn draw (&self, to: &mut TuiOut) { fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
if let Some(e) = self.error.read().unwrap().as_ref() { if let Some(e) = self.error.read().unwrap().as_ref() {
to.show(to.area(), e); to.show(to.area(), e);
} }
@ -125,16 +125,16 @@ mod app {
}); });
} }
impl Understand<TuiOut, ()> for App { impl Understand<Tui, ()> for App {
fn understand_expr <'a> (&'a self, to: &mut TuiOut, lang: &'a impl Expression) -> Usually<()> { fn understand_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression) -> Usually<()> {
app_understand_expr(self, to, lang) 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) 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)? if evaluate_output_expression(state, to, lang)?
|| evaluate_output_expression_tui(state, to, lang)? { || evaluate_output_expression_tui(state, to, lang)? {
Ok(()) 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("/"); let mut frags = dsl.src()?.unwrap().split("/");
match frags.next() { match frags.next() {
Some(":logo") => to.place(&view_logo()), 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(":meters") => match frags.next() {
Some("input") => to.place(&Tui::bg(Rgb(30, 30, 30), Fill::Y(Align::s("Input 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), Fill::Y(Align::s("Output Meters")))), Some("output") => to.place(&Tui::bg(Rgb(30, 30, 30), h_full(origin_s("Output Meters")))),
_ => panic!() _ => panic!()
}, },
Some(":tracks") => match frags.next() { Some(":tracks") => match frags.next() {
None => to.place(&"TODO tracks"), 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("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), Fill::X(Align::w("Track Inputs")))), 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), Fill::X(Align::w("Track Devices")))), 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), Fill::X(Align::w("Track Outputs")))), Some("outputs") => to.place(&Tui::bg(Rgb(40, 40, 40), w_full(origin_w("Track Outputs")))),
_ => panic!() _ => panic!()
}, },
Some(":scenes") => match frags.next() { Some(":scenes") => match frags.next() {
@ -171,13 +171,13 @@ mod app {
Some("menu") => to.place(&if let Dialog::Menu(selected, items) = &state.dialog { Some("menu") => to.place(&if let Dialog::Menu(selected, items) = &state.dialog {
let items = items.clone(); let items = items.clone();
let selected = selected; 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() { 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( Tui::fg_bg(
if *selected == index { Rgb(240,200,180) } else { Rgb(200, 200, 200) }, if *selected == index { Rgb(240,200,180) } else { Rgb(200, 200, 200) },
if *selected == index { Rgb(80, 80, 50) } else { Rgb(30, 30, 30) }, 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(&{ Some(":templates") => to.place(&{
let modes = state.config.modes.clone(); let modes = state.config.modes.clone();
let height = (modes.read().unwrap().len() * 2) as u16; 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() { 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 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("<no name>"); let name = profile.name.get(0).map(|x|x.as_ref()).unwrap_or("<no name>");
let info = profile.info.get(0).map(|x|x.as_ref()).unwrap_or("<no info>"); let info = profile.info.get(0).map(|x|x.as_ref()).unwrap_or("<no info>");
let fg1 = Rgb(224, 192, 128); let fg1 = Rgb(224, 192, 128);
let fg2 = Rgb(224, 128, 32); let fg2 = Rgb(224, 128, 32);
let field_name = Fill::X(Align::w(Tui::fg(fg1, name))); let field_name = w_full(origin_w(Tui::fg(fg1, name)));
let field_id = Fill::X(Align::e(Tui::fg(fg2, id))); let field_id = w_full(origin_e(Tui::fg(fg2, id)));
let field_info = Fill::X(Align::w(info)); let field_info = w_full(origin_w(info));
to.place(&Push::Y((2 * index) as u16, to.place(&y_push((2 * index) as u16,
Fixed::Y(2, Fill::X(Tui::bg(bg, Bsp::s( h_exact(2, w_full(Tui::bg(bg, south(
Bsp::a(field_name, field_id), field_info)))))); 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); let fg = Rgb(224, 192, 128);
for (index, name) in ["session1", "session2", "session3"].iter().enumerate() { for (index, name) in ["session1", "session2", "session3"].iter().enumerate() {
let bg = if index == 0 { Rgb(50,50,50) } else { Rgb(40,40,40) }; let bg = if index == 0 { Rgb(50,50,50) } else { Rgb(40,40,40) };
to.place(&Push::Y((2 * index) as u16, to.place(&y_push((2 * index) as u16,
&Fixed::Y(2, Fill::X(Tui::bg(bg, Align::w(Tui::fg(fg, name))))))); &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() { match state.dialog.browser_target().unwrap() {
BrowseTarget::SaveProject => "Save project:", BrowseTarget::SaveProject => "Save project:",
BrowseTarget::LoadProject => "Load project:", BrowseTarget::LoadProject => "Load project:",
@ -221,18 +221,18 @@ mod app {
BrowseTarget::ExportSample(_) => "Export sample:", BrowseTarget::ExportSample(_) => "Export sample:",
BrowseTarget::ImportClip(_) => "Import clip:", BrowseTarget::ImportClip(_) => "Import clip:",
BrowseTarget::ExportClip(_) => "Export 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") => { Some(":device") => {
let selected = state.dialog.device_kind().unwrap(); 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||device_kinds().iter(),
move|_label: &&'static str, i|{ move|_label: &&'static str, i|{
let bg = if i == selected { Rgb(64,128,32) } else { Rgb(0,0,0) }; let bg = if i == selected { Rgb(64,128,32) } else { Rgb(0,0,0) };
let lb = if i == selected { "[ " } else { " " }; let lb = if i == selected { "[ " } else { " " };
let rb = 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(_) => { Some(_) => {
let views = state.config.views.read().unwrap(); let views = state.config.views.read().unwrap();
if let Some(dsl) = views.get(dsl.src()?.unwrap()) { if let Some(dsl) = views.get(dsl.src()?.unwrap()) {
@ -375,7 +375,7 @@ mod app {
mod arrange { mod arrange {
use crate::*; use crate::*;
impl_has!(Jack<'static>: |self: Arrangement| self.jack); impl_has!(Jack<'static>: |self: Arrangement| self.jack);
impl_has!(Measure<TuiOut>: |self: Arrangement| self.size); impl_has!(Measure<Tui>: |self: Arrangement| self.size);
impl_has!(Vec<Track>: |self: Arrangement| self.tracks); impl_has!(Vec<Track>: |self: Arrangement| self.tracks);
impl_has!(Vec<Scene>: |self: Arrangement| self.scenes); impl_has!(Vec<Scene>: |self: Arrangement| self.scenes);
impl_has!(Vec<MidiInput>: |self: Arrangement| self.midi_ins); impl_has!(Vec<MidiInput>: |self: Arrangement| self.midi_ins);
@ -479,79 +479,79 @@ mod arrange {
} }
Ok((index, &mut self.tracks_mut()[index])) Ok((index, &mut self.tracks_mut()[index]))
} }
#[cfg(feature = "track")] pub fn view_inputs (&self, _theme: ItemTheme) -> impl Content<TuiOut> + '_ { #[cfg(feature = "track")] pub fn view_inputs (&self, _theme: ItemTheme) -> impl Draw<Tui> + '_ {
Bsp::s( south(
Fixed::Y(1, self.view_inputs_header()), h_exact(1, self.view_inputs_header()),
Thunk::new(|to: &mut TuiOut|{ Thunk::new(|to: &mut Tui|{
for (index, port) in self.midi_ins().iter().enumerate() { 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<TuiOut> + '_ { #[cfg(feature = "track")] fn view_inputs_header (&self) -> impl Draw<Tui> + '_ {
Bsp::e(Fixed::X(20, Align::w(button_3("i", "nput ", format!("{}", self.midi_ins.len()), false))), east(w_exact(20, origin_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() { 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")] #[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!( to.place(&x_push(x1 as u16, Tui::bg(track.color.dark.rgb, origin_w(w_exact(track.width as u16, east!(
Either::new(track.sequencer.monitoring, Tui::fg(Green, "mon "), "mon "), either(track.sequencer.monitoring, Tui::fg(Green, "mon "), "mon "),
Either::new(track.sequencer.recording, Tui::fg(Red, "rec "), "rec "), either(track.sequencer.recording, Tui::fg(Red, "rec "), "rec "),
Either::new(track.sequencer.overdub, Tui::fg(Yellow, "dub "), "dub "), either(track.sequencer.overdub, Tui::fg(Yellow, "dub "), "dub "),
)))))) ))))))
}))) })))
} }
#[cfg(feature = "track")] fn view_inputs_row (&self, port: &MidiInput) -> impl Content<TuiOut> { #[cfg(feature = "track")] fn view_inputs_row (&self, port: &MidiInput) -> impl Draw<Tui> {
Bsp::e(Fixed::X(20, Align::w(Bsp::e("", Tui::bold(true, Tui::fg(Rgb(255,255,255), port.port_name()))))), east(w_exact(20, origin_w(east("", 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() { west(w_exact(4, ()), Thunk::new(move|to: &mut Tui|for (_index, track, _x1, _x2) in self.tracks_with_sizes() {
#[cfg(feature = "track")] #[cfg(feature = "track")]
to.place(&Tui::bg(track.color.darker.rgb, Align::w(Fixed::X(track.width as u16, row!( to.place(&Tui::bg(track.color.darker.rgb, origin_w(w_exact(track.width as u16, east!(
Either::new(track.sequencer.monitoring, Tui::fg(Green, ""), " · "), either(track.sequencer.monitoring, Tui::fg(Green, ""), " · "),
Either::new(track.sequencer.recording, Tui::fg(Red, ""), " · "), either(track.sequencer.recording, Tui::fg(Red, ""), " · "),
Either::new(track.sequencer.overdub, Tui::fg(Yellow, ""), " · "), either(track.sequencer.overdub, Tui::fg(Yellow, ""), " · "),
))))) )))))
}))) })))
} }
#[cfg(feature = "track")] pub fn view_outputs (&self, theme: ItemTheme) -> impl Content<TuiOut> { #[cfg(feature = "track")] pub fn view_outputs (&self, theme: ItemTheme) -> impl Draw<Tui> {
let mut h = 1; let mut h = 1;
for output in self.midi_outs().iter() { for output in self.midi_outs().iter() {
h += 1 + output.connections.len(); h += 1 + output.connections.len();
} }
let h = h as u16; let h = h as u16;
let list = Bsp::s( let list = south(
Fixed::Y(1, Fill::X(Align::w(button_3("o", "utput", format!("{}", self.midi_outs.len()), false)))), h_exact(1, w_full(origin_w(button_3("o", "utput", format!("{}", self.midi_outs.len()), false)))),
Fixed::Y(h - 1, Fill::XY(Align::nw(Thunk::new(|to: &mut TuiOut|{ h_exact(h - 1, wh_full(origin_nw(Thunk::new(|to: &mut Tui|{
for (_index, port) in self.midi_outs().iter().enumerate() { for (_index, port) in self.midi_outs().iter().enumerate() {
to.place(&Fixed::Y(1,Fill::X(Bsp::e( to.place(&h_exact(1,w_full(east(
Align::w(Bsp::e("", Tui::fg(Rgb(255,255,255),Tui::bold(true, port.port_name())))), origin_w(east("", Tui::fg(Rgb(255,255,255),Tui::bold(true, port.port_name())))),
Fill::X(Align::e(format!("{}/{} ", w_full(origin_e(format!("{}/{} ",
port.port().get_connections().len(), port.port().get_connections().len(),
port.connections.len()))))))); port.connections.len())))))));
for (index, conn) in port.connections.iter().enumerate() { 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), h_exact(h, view_track_row_section(theme, list, button_2("O", "+", false),
Tui::bg(theme.darker.rgb, Align::w(Fill::X( Tui::bg(theme.darker.rgb, origin_w(w_full(
Thunk::new(|to: &mut TuiOut|{ Thunk::new(|to: &mut Tui|{
for (index, track, _x1, _x2) in self.tracks_with_sizes() { for (index, track, _x1, _x2) in self.tracks_with_sizes() {
to.place(&Fixed::X(track_width(index, track), to.place(&w_exact(track_width(index, track),
Thunk::new(|to: &mut TuiOut|{ Thunk::new(|to: &mut Tui|{
to.place(&Fixed::Y(1, Align::w(Bsp::e( to.place(&h_exact(1, origin_w(east(
Either::new(true, Tui::fg(Green, "play "), "play "), either(true, Tui::fg(Green, "play "), "play "),
Either::new(false, Tui::fg(Yellow, "solo "), "solo "), either(false, Tui::fg(Yellow, "solo "), "solo "),
)))); ))));
for (_index, port) in self.midi_outs().iter().enumerate() { for (_index, port) in self.midi_outs().iter().enumerate() {
to.place(&Fixed::Y(1, Align::w(Bsp::e( to.place(&h_exact(1, origin_w(east(
Either::new(true, Tui::fg(Green, ""), " · "), either(true, Tui::fg(Green, ""), " · "),
Either::new(false, Tui::fg(Yellow, ""), " · "), either(false, Tui::fg(Yellow, ""), " · "),
)))); ))));
for (_index, _conn) in port.connections.iter().enumerate() { 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<TuiOut> { #[cfg(feature = "track")] pub fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<Tui> {
let mut h = 2u16; let mut h = 2u16;
for track in self.tracks().iter() { for track in self.tracks().iter() {
h = h.max(track.devices.len() as u16 * 2); h = h.max(track.devices.len() as u16 * 2);
@ -559,14 +559,14 @@ mod arrange {
view_track_row_section(theme, view_track_row_section(theme,
button_3("d", "evice", format!("{}", self.track().map(|t|t.devices.len()).unwrap_or(0)), false), button_3("d", "evice", format!("{}", self.track().map(|t|t.devices.len()).unwrap_or(0)), false),
button_2("D", "+", false), button_2("D", "+", false),
Thunk::new(move|to: &mut TuiOut|for (index, track, _x1, _x2) in self.tracks_with_sizes() { Thunk::new(move|to: &mut Tui|for (index, track, _x1, _x2) in self.tracks_with_sizes() {
to.place(&Fixed::XY(track_width(index, track), h + 1, to.place(&wh_exact(track_width(index, track), h + 1,
Tui::bg(track.color.dark.rgb, Align::nw(Map::south(2, move||0..h, Tui::bg(track.color.dark.rgb, origin_nw(iter_south(2, move||0..h,
|_, _index|Fixed::XY(track.width as u16, 2, |_, _index|wh_exact(track.width as u16, 2,
Tui::fg_bg( Tui::fg_bg(
ItemTheme::G[32].lightest.rgb, ItemTheme::G[32].lightest.rgb,
ItemTheme::G[32].dark.rgb, ItemTheme::G[32].dark.rgb,
Align::nw(format!(" · {}", "--"))))))))); origin_nw(format!(" · {}", "--")))))))));
})) }))
} }
/// Put a clip in a slot /// Put a clip in a slot
@ -616,7 +616,7 @@ mod arrange {
} }
} }
impl HasClipsSize for Arrangement { impl HasClipsSize for Arrangement {
fn clips_size (&self) -> &Measure<TuiOut> { &self.size_inner } fn clips_size (&self) -> &Measure<Tui> { &self.size_inner }
} }
} }
@ -657,14 +657,14 @@ mod browse {
fn _todo_stub_usize (&self) -> usize { todo!() } fn _todo_stub_usize (&self) -> usize { todo!() }
fn _todo_stub_arc_str (&self) -> Arc<str> { todo!() } fn _todo_stub_arc_str (&self) -> Arc<str> { todo!() }
} }
impl HasContent<TuiOut> for Browse { impl Browse {
fn content (&self) -> impl Content<TuiOut> { fn tui (&self) -> impl Draw<Tui> {
Map::south(1, ||EntriesIterator { iter_south(1, ||EntriesIterator {
offset: 0, offset: 0,
index: 0, index: 0,
length: self.dirs.len() + self.files.len(), length: self.dirs.len() + self.files.len(),
browser: self, browser: self,
}, |entry, _index|Fill::X(Align::w(entry))) }, |entry, _index|w_full(origin_w(entry)))
} }
} }
impl<'a> Iterator for EntriesIterator<'a> { impl<'a> Iterator for EntriesIterator<'a> {
@ -1117,7 +1117,7 @@ impl InteriorMutable<usize> for AtomicUsize { fn set (&self, value: usize) -> us
impl PartialEq for MenuItem { fn eq (&self, other: &Self) -> bool { self.0 == other.0 } } impl PartialEq for MenuItem { fn eq (&self, other: &Self) -> bool { self.0 == other.0 } }
impl AsRef<Arc<[MenuItem]>> for MenuItems { fn as_ref (&self) -> &Arc<[MenuItem]> { &self.0 } } impl AsRef<Arc<[MenuItem]>> for MenuItems { fn as_ref (&self) -> &Arc<[MenuItem]> { &self.0 } }
impl HasClipsSize for App { fn clips_size (&self) -> &Measure<TuiOut> { &self.project.size_inner } } impl HasClipsSize for App { fn clips_size (&self) -> &Measure<Tui> { &self.project.size_inner } }
impl HasJack<'static> for MidiInput { fn jack (&self) -> &Jack<'static> { &self.jack } } 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 MidiOutput { fn jack (&self) -> &Jack<'static> { &self.jack } }
impl HasJack<'static> for AudioInput { 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 { mod meter {
use crate::*; use crate::*;
impl Layout<TuiOut> for RmsMeter {} impl Draw<Tui> for RmsMeter {
impl Layout<TuiOut> for Log10Meter {} fn draw(self, to: &mut Tui) -> Usually<XYWH<u16>> {
impl Draw<TuiOut> for RmsMeter {
fn draw (&self, to: &mut TuiOut) {
let XYWH(x, y, w, h) = to.area(); let XYWH(x, y, w, h) = to.area();
let signal = f32::max(0.0, f32::min(100.0, self.0.abs())); let signal = f32::max(0.0, f32::min(100.0, self.0.abs()));
let v = (signal * h as f32).ceil() as u16; let v = (signal * h as f32).ceil() as u16;
@ -1813,8 +1810,8 @@ mod audio {
} }
} }
impl Draw<TuiOut> for Log10Meter { impl Draw<Tui> for Log10Meter {
fn draw (&self, to: &mut TuiOut) { fn draw(self, to: &mut Tui) -> Usually<XYWH<u16>> {
let XYWH(x, y, w, h) = to.area(); let XYWH(x, y, w, h) = to.area();
let signal = 100.0 - f32::max(0.0, f32::min(100.0, self.0.abs())); 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; let v = (signal * h as f32 / 100.0).ceil() as u16;
@ -1828,9 +1825,9 @@ mod audio {
} }
} }
fn draw_meters (meters: &[f32]) -> impl Content<TuiOut> + use<'_> { fn draw_meters (meters: &[f32]) -> impl Draw<Tui> + use<'_> {
Tui::bg(Black, Fixed::X(2, Map::east(1, ||meters.iter(), |value, _index|{ Tui::bg(Black, w_exact(2, iter_east(1, ||meters.iter(), |value, _index|{
Fill::Y(RmsMeter(*value)) h_full(RmsMeter(*value))
}))) })))
} }
} }
@ -1879,14 +1876,14 @@ mod audio {
fn _todo_usize_stub_ (&self) -> usize { todo!() } fn _todo_usize_stub_ (&self) -> usize { todo!() }
fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() } fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() }
fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() } fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() }
pub fn per <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> ( pub fn per <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
tracks: impl Fn() -> U + Send + Sync + 'a, tracks: impl Fn() -> U + Send + Sync + 'a,
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
) -> impl Content<TuiOut> + 'a { ) -> impl Draw<Tui> + 'a {
Map::new(tracks, iter(tracks,
move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _|{ move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _|{
let width = (x2 - x1) as u16; 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.lightest.rgb,
track.color.base.rgb, track.color.base.rgb,
callback(index, track))))}) callback(index, track))))})
@ -1932,21 +1929,21 @@ mod audio {
} }
} }
pub fn per_track <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> ( pub fn per_track <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
tracks: impl Fn() -> U + Send + Sync + 'a, tracks: impl Fn() -> U + Send + Sync + 'a,
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
) -> impl Content<TuiOut> + 'a { ) -> impl Draw<Tui> + 'a {
per_track_top(tracks, move|index, track|Fill::Y(Align::y(callback(index, track)))) per_track_top(tracks, move|index, track|h_full(origin_y(callback(index, track))))
} }
pub fn per_track_top <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> ( pub fn per_track_top <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
tracks: impl Fn() -> U + Send + Sync + 'a, tracks: impl Fn() -> U + Send + Sync + 'a,
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
) -> impl Content<TuiOut> + 'a { ) -> impl Draw<Tui> + 'a {
Align::x(Tui::bg(Reset, Map::new(tracks, origin_x(Tui::bg(Reset, iter(tracks,
move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _|{ move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _|{
let width = (x2 - x1) as u16; 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.lightest.rgb,
track.color.base.rgb, track.color.base.rgb,
callback(index, track))))}))) callback(index, track))))})))
@ -2007,8 +2004,8 @@ mod audio {
impl_has!(Clock: |self: Sequencer| self.clock); impl_has!(Clock: |self: Sequencer| self.clock);
impl_has!(Vec<MidiInput>: |self: Sequencer| self.midi_ins); impl_has!(Vec<MidiInput>: |self: Sequencer| self.midi_ins);
impl_has!(Vec<MidiOutput>: |self: Sequencer| self.midi_outs); impl_has!(Vec<MidiOutput>: |self: Sequencer| self.midi_outs);
impl_has!(Measure<TuiOut>: |self: MidiEditor| self.size); impl_has!(Measure<Tui>: |self: MidiEditor| self.size);
impl_has!(Measure<TuiOut>: |self: PianoHorizontal| self.size); impl_has!(Measure<Tui>: |self: PianoHorizontal| self.size);
impl_default!(Sequencer: Self { impl_default!(Sequencer: Self {
clock: Clock::default(), clock: Clock::default(),
play_clip: None, play_clip: None,
@ -2224,11 +2221,15 @@ mod audio {
} }
} }
} }
impl Draw<TuiOut> for MidiEditor { impl Draw<Tui> for MidiEditor {
fn draw (&self, to: &mut TuiOut) { self.content().draw(to) } fn draw(self, to: &mut Tui) -> Usually<XYWH<u16>> {
self.tui().draw(to)
}
}
impl Draw<Tui> for PianoHorizontal {
fn draw(self, to: &mut Tui) -> Usually<XYWH<u16>> {
self.tui().draw(to)
} }
impl Draw<TuiOut> for PianoHorizontal {
fn draw (&self, to: &mut TuiOut) { self.content().draw(to) }
} }
} }
@ -2320,23 +2321,23 @@ mod audio {
self.get_time_pos().overflowing_sub(1) self.get_time_pos().overflowing_sub(1)
.0.min(self.clip_length().saturating_sub(1)) .0.min(self.clip_length().saturating_sub(1))
} }
pub fn clip_status (&self) -> impl Content<TuiOut> + '_ { pub fn clip_status (&self) -> impl Draw<Tui> + '_ {
let (_color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { 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) (clip.color, clip.name.clone(), clip.length, clip.looped)
} else { (ItemTheme::G[64], String::new().into(), 0, false) }; } else { (ItemTheme::G[64], String::new().into(), 0, false) };
Fixed::X(20, col!( w_exact(20, south!(
Fill::X(Align::w(Bsp::e( w_full(origin_w(east(
button_2("f2", "name ", false), button_2("f2", "name ", false),
Fill::X(Align::e(Tui::fg(Rgb(255, 255, 255), format!("{name} "))))))), w_full(origin_e(Tui::fg(Rgb(255, 255, 255), format!("{name} "))))))),
Fill::X(Align::w(Bsp::e( w_full(origin_w(east(
button_2("l", "ength ", false), button_2("l", "ength ", false),
Fill::X(Align::e(Tui::fg(Rgb(255, 255, 255), format!("{length} "))))))), w_full(origin_e(Tui::fg(Rgb(255, 255, 255), format!("{length} "))))))),
Fill::X(Align::w(Bsp::e( w_full(origin_w(east(
button_2("r", "epeat ", false), 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<TuiOut> + '_ { pub fn edit_status (&self) -> impl Draw<Tui> + '_ {
let (_color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { let (_color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.length) (clip.color, clip.length)
} else { (ItemTheme::G[64], 0) }; } else { (ItemTheme::G[64], 0) };
@ -2347,18 +2348,18 @@ mod audio {
let note_name = format!("{:4}", note_pitch_to_name(note_pos)); let note_name = format!("{:4}", note_pitch_to_name(note_pos));
let note_pos = format!("{:>3}", note_pos); let note_pos = format!("{:>3}", note_pos);
let note_len = format!("{:>4}", self.get_note_len()); let note_len = format!("{:>4}", self.get_note_len());
Fixed::X(20, col!( w_exact(20, south!(
Fill::X(Align::w(Bsp::e( w_full(origin_w(east(
button_2("t", "ime ", false), 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} "))))))), format!("{length} /{time_zoom} +{time_pos} "))))))),
Fill::X(Align::w(Bsp::e( w_full(origin_w(east(
button_2("z", "lock ", false), 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}"))))))), format!("{time_lock}"))))))),
Fill::X(Align::w(Bsp::e( w_full(origin_w(east(
button_2("x", "note ", false), 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}"))))))), format!("{note_name} {note_pos} {note_len}"))))))),
)) ))
} }
@ -2394,12 +2395,8 @@ mod audio {
fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) } fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) }
} }
impl Layout<TuiOut> for MidiEditor { impl MidiEditor {
fn layout (&self, to: XYWH<u16>) -> XYWH<u16> { self.content().layout(to) } fn tui (&self) -> impl Draw<Tui> { self.autoscroll(); /*self.autozoom();*/ self.size.of(&self.mode) }
}
impl HasContent<TuiOut> for MidiEditor {
fn content (&self) -> impl Content<TuiOut> { self.autoscroll(); /*self.autozoom();*/ self.size.of(&self.mode) }
} }
@ -2423,14 +2420,11 @@ mod audio {
} }
} }
impl Layout<TuiOut> for PianoHorizontal { impl PianoHorizontal {
fn layout (&self, to: XYWH<u16>) -> XYWH<u16> { self.content().layout(to) } fn tui (&self) -> impl Draw<Tui> {
} south(
impl HasContent<TuiOut> for PianoHorizontal { east(w_exact(5, format!("{}x{}", self.size.w(), self.size.h())), self.timeline()),
fn content (&self) -> impl Content<TuiOut> { east(self.keys(), self.size.of(below(wh_full(self.notes()), wh_full(self.cursor())))),
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())))),
) )
} }
} }
@ -2507,12 +2501,12 @@ mod audio {
} }
} }
fn notes (&self) -> impl Content<TuiOut> { fn notes (&self) -> impl Draw<Tui> {
let time_start = self.get_time_start(); let time_start = self.get_time_start();
let note_lo = self.get_note_lo(); let note_lo = self.get_note_lo();
let note_hi = self.get_note_hi(); let note_hi = self.get_note_hi();
let buffer = self.buffer.clone(); let buffer = self.buffer.clone();
Thunk::new(move|to: &mut TuiOut|{ Thunk::new(move|to: &mut Tui|{
let source = buffer.read().unwrap(); let source = buffer.read().unwrap();
let XYWH(x0, y0, w, _h) = to.area(); let XYWH(x0, y0, w, _h) = to.area();
//if h as usize != note_axis { //if h as usize != note_axis {
@ -2538,7 +2532,7 @@ mod audio {
} }
}) })
} }
fn cursor (&self) -> impl Content<TuiOut> { fn cursor (&self) -> impl Draw<Tui> {
let note_hi = self.get_note_hi(); let note_hi = self.get_note_hi();
let note_lo = self.get_note_lo(); let note_lo = self.get_note_lo();
let note_pos = self.get_note_pos(); let note_pos = self.get_note_pos();
@ -2547,7 +2541,7 @@ mod audio {
let time_start = self.get_time_start(); let time_start = self.get_time_start();
let time_zoom = self.get_time_zoom(); let time_zoom = self.get_time_zoom();
let style = Some(Style::default().fg(self.color.lightest.rgb)); 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(); let XYWH(x0, y0, w, _) = to.area();
for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) { for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
if note == note_pos { if note == note_pos {
@ -2569,7 +2563,7 @@ mod audio {
} }
}) })
} }
fn keys (&self) -> impl Content<TuiOut> { fn keys (&self) -> impl Draw<Tui> {
let state = self; let state = self;
let color = state.color; let color = state.color;
let note_lo = state.get_note_lo(); 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 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 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()); 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(); let XYWH(x, y0, _w, _h) = to.area();
for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) { 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); to.blit(&to_key(note), x, screen_y, key_style);
@ -2593,8 +2587,8 @@ mod audio {
} }
}))) })))
} }
fn timeline (&self) -> impl Content<TuiOut> + '_ { fn timeline (&self) -> impl Draw<Tui> + '_ {
Fill::X(Fixed::Y(1, Thunk::new(move|to: &mut TuiOut|{ w_full(h_exact(1, Thunk::new(move|to: &mut Tui|{
let XYWH(x, y, w, _h) = to.area(); let XYWH(x, y, w, _h) = to.area();
let style = Some(Style::default().dim()); let style = Some(Style::default().dim());
let length = self.clip.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1); 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<Arc<RwLock<MidiClip>>> { &mut self.clip } fn clip_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> { &mut self.clip }
/// Determine the required space to render the 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 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() { *self.buffer.write().unwrap() = if let Some(clip) = self.clip.as_ref() {
let clip = clip.read().unwrap(); let clip = clip.read().unwrap();
let buf_size = self.buffer_size(&clip); 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 } }] self.colors[if self.on[pitch] { 2 } else { match pitch { 0 | 2 | 4 | 5 | 6 | 8 | 10 => 0, _ => 1 } }]
} }
} }
impl HasContent<TuiOut> for OctaveVertical { impl OctaveVertical {
fn content (&self) -> impl Content<TuiOut> { fn tui (&self) -> impl Draw<Tui> {
row!( east!(
Tui::fg_bg(self.color(0), self.color(1), ""), Tui::fg_bg(self.color(0), self.color(1), ""),
Tui::fg_bg(self.color(2), self.color(3), ""), Tui::fg_bg(self.color(2), self.color(3), ""),
Tui::fg_bg(self.color(4), self.color(5), ""), Tui::fg_bg(self.color(4), self.color(5), ""),
@ -3107,8 +3101,8 @@ mod audio {
Ok(()) Ok(())
} }
} }
impl Draw<TuiOut> for SampleAdd { impl Draw<Tui> for SampleAdd {
fn draw (&self, _to: &mut TuiOut) { fn draw (self, _to: &mut Tui) -> Usually<XYWH<u16>> {
todo!() todo!()
} }
} }
@ -3129,9 +3123,9 @@ mod audio {
} }
} }
fn draw_viewer (sample: Option<&Arc<RwLock<Sample>>>) -> impl Content<TuiOut> + use<'_> { fn draw_viewer (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> + use<'_> {
let min_db = -64.0; 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 XYWH(x, y, width, height) = to.area();
let area = Rect { x, y, width, height }; let area = Rect { x, y, width, height };
if let Some(sample) = &sample { if let Some(sample) = &sample {
@ -3216,7 +3210,7 @@ mod audio {
} }
fn draw_sample ( 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<usize> { ) -> Usually<usize> {
let style = if focus { Style::default().green() } else { Style::default() }; let style = if focus { Style::default().green() } else { Style::default() };
if focus { if focus {
@ -3340,8 +3334,8 @@ mod audio {
} }
} }
impl Draw<TuiOut> for Lv2 { impl Draw<Tui> for Lv2 {
fn draw (&self, to: &mut TuiOut) { fn draw(self, to: &mut Tui) {
let area = to.area(); let area = to.area();
let XYWH(x, y, _, height) = area; let XYWH(x, y, _, height) = area;
let mut width = 20u16; 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 style = Style::default().gray();
let label1 = format!(" {}", state.name); let label1 = format!(" {}", state.name);
to.blit(&label1, x + 1, y, Some(style.white().bold())); to.blit(&label1, x + 1, y, Some(style.white().bold()));
@ -3387,8 +3381,8 @@ mod audio {
mod pool { mod pool {
use crate::*; use crate::*;
has_clips!(|self: Pool|self.clips);
has_clip!(|self: Pool|self.clips().get(self.clip_index()).map(|c|c.clone())); 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<RwLock<MidiClip>>|{ impl_from!(Pool: |clip:&Arc<RwLock<MidiClip>>|{
let model = Self::default(); let model = Self::default();
model.clips.write().unwrap().push(clip.clone()); model.clips.write().unwrap().push(clip.clone());
@ -3523,14 +3517,14 @@ mod pool {
fn _color_random (&self) -> ItemColor { ItemColor::random() } fn _color_random (&self) -> ItemColor { ItemColor::random() }
} }
impl<'a> HasContent<TuiOut> for PoolView<'a> { impl<'a> PoolView<'a> {
fn content (&self) -> impl Content<TuiOut> { fn tui (&self) -> impl Draw<Tui> {
let Self(pool) = self; let Self(pool) = self;
//let color = self.1.clip().map(|c|c.read().unwrap().color).unwrap_or_else(||Tui::g(32).into()); //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 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; //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(), ||pool.clips().clone().into_iter(),
move|clip: Arc<RwLock<MidiClip>>, i: usize|{ move|clip: Arc<RwLock<MidiClip>>, i: usize|{
let item_height = 1; let item_height = 1;
@ -3541,26 +3535,26 @@ mod pool {
let fg = color.lightest.rgb; let fg = color.lightest.rgb;
let name = if false { format!(" {i:>3}") } else { format!(" {i:>3} {name}") }; let name = if false { format!(" {i:>3}") } else { format!(" {i:>3} {name}") };
let length = if false { String::default() } else { format!("{length} ") }; let length = if false { String::default() } else { format!("{length} ") };
Fixed::Y(1, map_south(item_offset, item_height, Tui::bg(bg, lay!( h_exact(1, iter_south(item_offset, item_height, Tui::bg(bg, below!(
Fill::X(Align::w(Tui::fg(fg, Tui::bold(selected, name)))), w_full(origin_w(Tui::fg(fg, Tui::bold(selected, name)))),
Fill::X(Align::e(Tui::fg(fg, Tui::bold(selected, length)))), w_full(origin_e(Tui::fg(fg, Tui::bold(selected, length)))),
Fill::X(Align::w(When::new(selected, Tui::bold(true, Tui::fg(Tui::g(255), ""))))), w_full(origin_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), ""))))), w_full(origin_e(When::new(selected, Tui::bold(true, Tui::fg(Tui::g(255), ""))))),
)))) ))))
})))) }))))
} }
} }
impl HasContent<TuiOut> for ClipLength { impl ClipLength {
fn content (&self) -> impl Content<TuiOut> { fn tui (&self) -> impl Draw<Tui> {
use ClipLengthFocus::*; use ClipLengthFocus::*;
let bars = ||self.bars_string(); let bars = ||self.bars_string();
let beats = ||self.beats_string(); let beats = ||self.beats_string();
let ticks = ||self.ticks_string(); let ticks = ||self.ticks_string();
match self.focus { match self.focus {
None => row!(" ", bars(), ".", beats(), ".", ticks()), None => east!(" ", bars(), ".", beats(), ".", ticks()),
Some(Bar) => row!("[", bars(), "]", beats(), ".", ticks()), Some(Bar) => east!("[", bars(), "]", beats(), ".", ticks()),
Some(Beat) => row!(" ", bars(), "[", beats(), "]", ticks()), Some(Beat) => east!(" ", bars(), "[", beats(), "]", ticks()),
Some(Tick) => row!(" ", bars(), ".", beats(), "[", ticks()), Some(Tick) => east!(" ", bars(), ".", beats(), "[", ticks()),
} }
} }
} }
@ -3848,14 +3842,6 @@ mod config {
Ok(map) 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<D: Language + Ord> Draw<TuiOut> for Mode<D> {
fn draw (&self, _to: &mut TuiOut) {
//self.content().draw(to)
}
}
} }
mod dialog { mod dialog {

View file

@ -31,7 +31,7 @@ use builder_pattern::Builder;
/// Must not be dropped for the duration of the process /// Must not be dropped for the duration of the process
pub jack: Jack<'static>, pub jack: Jack<'static>,
/// Display size /// Display size
pub size: Measure<TuiOut>, pub size: Measure<Tui>,
/// Performance counter /// Performance counter
pub perf: PerfModel, pub perf: PerfModel,
/// Available view modes and input bindings /// Available view modes and input bindings
@ -468,9 +468,9 @@ use builder_pattern::Builder;
/// TODO rename to "render_cache" or smth /// TODO rename to "render_cache" or smth
pub arranger: Arc<RwLock<Buffer>>, pub arranger: Arc<RwLock<Buffer>>,
/// Display size /// Display size
pub size: Measure<TuiOut>, pub size: Measure<Tui>,
/// Display size of clips area /// Display size of clips area
pub size_inner: Measure<TuiOut>, pub size_inner: Measure<Tui>,
/// Source of time /// Source of time
#[cfg(feature = "clock")] pub clock: Clock, #[cfg(feature = "clock")] pub clock: Clock,
/// Allows one MIDI clip to be edited /// Allows one MIDI clip to be edited
@ -509,7 +509,7 @@ use builder_pattern::Builder;
pub filter: String, pub filter: String,
pub index: usize, pub index: usize,
pub scroll: usize, pub scroll: usize,
pub size: Measure<TuiOut>, pub size: Measure<Tui>,
} }
pub(crate) struct EntriesIterator<'a> { pub(crate) struct EntriesIterator<'a> {
@ -598,7 +598,7 @@ pub struct DeviceAudio<'a>(pub &'a mut Device);
/// ``` /// ```
pub struct MidiEditor { pub struct MidiEditor {
/// Size of editor on screen /// Size of editor on screen
pub size: Measure<TuiOut>, pub size: Measure<Tui>,
/// View mode and state of editor /// View mode and state of editor
pub mode: PianoHorizontal, pub mode: PianoHorizontal,
} }
@ -613,7 +613,7 @@ pub struct MidiEditor {
/// Buffer where the whole clip is rerendered on change /// Buffer where the whole clip is rerendered on change
pub buffer: Arc<RwLock<BigBuffer>>, pub buffer: Arc<RwLock<BigBuffer>>,
/// Size of actual notes area /// Size of actual notes area
pub size: Measure<TuiOut>, pub size: Measure<Tui>,
/// The display window /// The display window
pub range: MidiSelection, pub range: MidiSelection,
/// The note cursor /// The note cursor
@ -808,7 +808,7 @@ pub struct PoolView<'a>(pub &'a Pool);
/// Currently active modal, if any. /// Currently active modal, if any.
pub mode: Option<SamplerMode>, pub mode: Option<SamplerMode>,
/// Size of rendered sampler. /// Size of rendered sampler.
pub size: Measure<TuiOut>, pub size: Measure<Tui>,
/// Lowest note displayed. /// Lowest note displayed.
pub note_lo: AtomicUsize, pub note_lo: AtomicUsize,
/// Currently selected note. /// Currently selected note.

View file

@ -119,7 +119,7 @@ pub trait MidiRange: TimeRange + NoteRange {}
/// can be clocked in microseconds with f64 without losing precision. /// can be clocked in microseconds with f64 without losing precision.
pub trait TimeUnit: InteriorMutable<f64> {} pub trait TimeUnit: InteriorMutable<f64> {}
pub trait HasClipsSize { fn clips_size (&self) -> &Measure<TuiOut>; } pub trait HasClipsSize { fn clips_size (&self) -> &Measure<Tui>; }
pub trait HasMidiClip { pub trait HasMidiClip {
fn clip (&self) -> Option<Arc<RwLock<MidiClip>>>; fn clip (&self) -> Option<Arc<RwLock<MidiClip>>>;
@ -327,16 +327,16 @@ pub trait HasTracks: AsRef<Vec<Track>> + AsMut<Vec<Track>> {
pub trait HasTrack: AsRefOpt<Track> + AsMutOpt<Track> { pub trait HasTrack: AsRefOpt<Track> + AsMutOpt<Track> {
fn track (&self) -> Option<&Track> { self.as_ref_opt() } fn track (&self) -> Option<&Track> { self.as_ref_opt() }
fn track_mut (&mut self) -> Option<&mut Track> { self.as_mut_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<TuiOut> + 'a { #[cfg(feature = "port")] fn view_midi_ins_status <'a> (&'a self, theme: ItemTheme) -> impl Draw<Tui> + 'a {
self.track().map(move|track|view_ports_status(theme, "MIDI ins: ", &track.sequencer.midi_ins)) 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<TuiOut> + '_ { #[cfg(feature = "port")] fn view_midi_outs_status (&self, theme: ItemTheme) -> impl Draw<Tui> + '_ {
self.track().map(move|track|view_ports_status(theme, "MIDI outs: ", &track.sequencer.midi_outs)) 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<TuiOut> { #[cfg(feature = "port")] fn view_audio_ins_status (&self, theme: ItemTheme) -> impl Draw<Tui> {
self.track().map(move|track|view_ports_status(theme, "Audio ins: ", &track.audio_ins())) 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<TuiOut> { #[cfg(feature = "port")] fn view_audio_outs_status (&self, theme: ItemTheme) -> impl Draw<Tui> {
self.track().map(move|track|view_ports_status(theme, "Audio outs:", &track.audio_outs())) 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; *self.reset_mut() = true;
} }
fn play_status (&self) -> impl Content<TuiOut> { fn play_status (&self) -> impl Draw<Tui> {
let (name, color): (Arc<str>, ItemTheme) = if let Some((_, Some(clip))) = self.play_clip() { let (name, color): (Arc<str>, ItemTheme) = if let Some((_, Some(clip))) = self.play_clip() {
let MidiClip { ref name, color, .. } = *clip.read().unwrap(); let MidiClip { ref name, color, .. } = *clip.read().unwrap();
(name.clone(), color) (name.clone(), color)
@ -389,10 +389,10 @@ pub trait HasPlayClip: HasClock {
let time: String = self.pulses_since_start_looped() let time: String = self.pulses_since_start_looped()
.map(|(times, time)|format!("{:>3}x {:>}", times+1.0, self.clock().timebase.format_beats_1(time))) .map(|(times, time)|format!("{:>3}x {:>}", times+1.0, self.clock().timebase.format_beats_1(time)))
.unwrap_or_else(||String::from(" ")).into(); .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<TuiOut> { fn next_status (&self) -> impl Draw<Tui> {
let mut time: Arc<str> = String::from("--.-.--").into(); let mut time: Arc<str> = String::from("--.-.--").into();
let mut name: Arc<str> = String::from("").into(); let mut name: Arc<str> = String::from("").into();
let mut color = ItemTheme::G[64]; let mut color = ItemTheme::G[64];
@ -425,7 +425,7 @@ pub trait HasPlayClip: HasClock {
name = "Stop".to_string().into(); 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<TuiOut> + MidiRange + MidiPoint + Debug + Send + Sync { pub trait MidiViewer: Measured<Tui> + MidiRange + MidiPoint + Debug + Send + Sync {
fn buffer_size (&self, clip: &MidiClip) -> (usize, usize); fn buffer_size (&self, clip: &MidiClip) -> (usize, usize);
fn redraw (&self); fn redraw (&self);
fn clip (&self) -> &Option<Arc<RwLock<MidiClip>>>; fn clip (&self) -> &Option<Arc<RwLock<MidiClip>>>;
@ -557,20 +557,20 @@ pub trait MidiViewer: Measured<TuiOut> + MidiRange + MidiPoint + Debug + Send +
pub trait ClipsView: TracksView + ScenesView { pub trait ClipsView: TracksView + ScenesView {
fn view_scenes_clips <'a> (&'a self) fn view_scenes_clips <'a> (&'a self)
-> impl Content<TuiOut> + 'a -> impl Draw<Tui> + 'a
{ {
self.clips_size().of(Fill::XY(Bsp::a( self.clips_size().of(wh_full(above(
Fill::XY(Align::se(Tui::fg(Green, format!("{}x{}", self.clips_size().w(), self.clips_size().h())))), wh_full(origin_se(Tui::fg(Green, format!("{}x{}", self.clips_size().w(), self.clips_size().h())))),
Thunk::new(|to: &mut TuiOut|for ( Thunk::new(|to: &mut Tui|for (
track_index, track, _, _ track_index, track, _, _
) in self.tracks_with_sizes() { ) in self.tracks_with_sizes() {
to.place(&Fixed::X(track.width as u16, to.place(&w_exact(track.width as u16,
Fill::Y(self.view_track_clips(track_index, track)))) h_full(self.view_track_clips(track_index, track))))
})))) }))))
} }
fn view_track_clips <'a> (&'a self, track_index: usize, track: &'a Track) -> impl Content<TuiOut> + 'a { fn view_track_clips <'a> (&'a self, track_index: usize, track: &'a Track) -> impl Draw<Tui> + 'a {
Thunk::new(move|to: &mut TuiOut|for ( Thunk::new(move|to: &mut Tui|for (
scene_index, scene, .. scene_index, scene, ..
) in self.scenes_with_sizes() { ) in self.scenes_with_sizes() {
let (name, theme): (Arc<str>, ItemTheme) = if let Some(Some(clip)) = &scene.clips.get(track_index) { let (name, theme): (Arc<str>, 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 Self::H_SCENE as usize
} as u16; } as u16;
to.place(&Fixed::XY(w, y, Bsp::b( to.place(&wh_exact(w, y, below(
Fill::XY(Outer(true, Style::default().fg(outline))), wh_full(Outer(true, Style::default().fg(outline))),
Fill::XY(Bsp::b( wh_full(below(
Bsp::b( below(
Tui::fg_bg(outline, bg, Fill::XY("")), Tui::fg_bg(outline, bg, wh_full("")),
Fill::XY(Align::nw(Tui::fg_bg(fg, bg, Tui::bold(true, name)))), 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.selection().scene() == Some(scene_index)
&& self.is_editing(), self.editor()))))))); && self.is_editing(), self.editor())))))));
}) })
@ -624,7 +624,7 @@ pub trait ClipsView: TracksView + ScenesView {
} }
pub trait TracksView: ScenesView + HasMidiIns + HasMidiOuts + HasTrackScroll + Measured<TuiOut> { pub trait TracksView: ScenesView + HasMidiIns + HasMidiOuts + HasTrackScroll + Measured<Tui> {
fn tracks_width_available (&self) -> u16 { fn tracks_width_available (&self) -> u16 {
(self.measure_width() as u16).saturating_sub(40) (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<TuiOut> { fn view_track_names (&self, theme: ItemTheme) -> impl Draw<Tui> {
let track_count = self.tracks().len(); let track_count = self.tracks().len();
let scene_count = self.scenes().len(); let scene_count = self.scenes().len();
let selected = self.selection(); let selected = self.selection();
let button = Bsp::s( let button = south(
button_3("t", "rack ", format!("{}{track_count}", selected.track() button_3("t", "rack ", format!("{}{track_count}", selected.track()
.map(|track|format!("{track}/")).unwrap_or_default()), false), .map(|track|format!("{track}/")).unwrap_or_default()), false),
button_3("s", "cene ", format!("{}{scene_count}", selected.scene() button_3("s", "cene ", format!("{}{scene_count}", selected.scene()
.map(|scene|format!("{scene}/")).unwrap_or_default()), false)); .map(|scene|format!("{scene}/")).unwrap_or_default()), false));
let button_2 = Bsp::s( let button_2 = south(
button_2("T", "+", false), button_2("T", "+", false),
button_2("S", "+", false)); button_2("S", "+", false));
view_track_row_section(theme, button, button_2, Tui::bg(theme.darker.rgb, 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() { 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) { Tui::bg(if selected.track() == Some(index) {
track.color.light.rgb track.color.light.rgb
} else { } else {
track.color.base.rgb track.color.base.rgb
}, Bsp::s(Fill::X(Align::nw(Bsp::e( }, south(w_full(origin_nw(east(
format!("·t{index:02} "), format!("·t{index:02} "),
Tui::fg(Rgb(255, 255, 255), Tui::bold(true, &track.name)) Tui::fg(Rgb(255, 255, 255), Tui::bold(true, &track.name))
))), ""))) ));}})))) ))), ""))) ));}}))))
} }
fn view_track_outputs <'a> (&'a self, theme: ItemTheme, _h: u16) -> impl Content<TuiOut> { fn view_track_outputs <'a> (&'a self, theme: ItemTheme, _h: u16) -> impl Draw<Tui> {
view_track_row_section(theme, view_track_row_section(theme,
Bsp::s(Fill::X(Align::w(button_2("o", "utput", false))), south(w_full(origin_w(button_2("o", "utput", false))),
Thunk::new(|to: &mut TuiOut|for port in self.midi_outs().iter() { Thunk::new(|to: &mut Tui|for port in self.midi_outs().iter() {
to.place(&Fill::X(Align::w(port.port_name()))); to.place(&w_full(origin_w(port.port_name())));
})), })),
button_2("O", "+", false), 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() { for (index, track, _x1, _x2) in self.tracks_with_sizes() {
to.place(&Fixed::X(track_width(index, track), to.place(&w_exact(track_width(index, track),
Align::nw(Fill::Y(Map::south(1, ||track.sequencer.midi_outs.iter(), origin_nw(h_full(iter_south(1, ||track.sequencer.midi_outs.iter(),
|port, index|Tui::fg(Rgb(255, 255, 255), |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())))))))))));}})))) format!("·o{index:02} {}", port.port_name())))))))))));}}))))
} }
fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Content<TuiOut> { fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Draw<Tui> {
let mut h = 0u16; let mut h = 0u16;
for track in self.tracks().iter() { for track in self.tracks().iter() {
h = h.max(track.sequencer.midi_ins.len() as u16); 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() { let content = Thunk::new(move|to: &mut Tui|for (index, track, _x1, _x2) in self.tracks_with_sizes() {
to.place(&Fixed::XY(track_width(index, track), h + 1, to.place(&wh_exact(track_width(index, track), h + 1,
Align::nw(Bsp::s( origin_nw(south(
Tui::bg(track.color.base.rgb, Tui::bg(track.color.base.rgb,
Fill::X(Align::w(row!( w_full(origin_w(east!(
Either::new(track.sequencer.monitoring, Tui::fg(Green, "●mon "), "·mon "), either(track.sequencer.monitoring, Tui::fg(Green, "●mon "), "·mon "),
Either::new(track.sequencer.recording, Tui::fg(Red, "●rec "), "·rec "), either(track.sequencer.recording, Tui::fg(Red, "●rec "), "·rec "),
Either::new(track.sequencer.overdub, Tui::fg(Yellow, "●dub "), "·dub "), 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, |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), 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<TuiOut> { fn view_scenes_names (&self) -> impl Draw<Tui> {
Fixed::X(20, Thunk::new(|to: &mut TuiOut|for (index, scene, ..) in self.scenes_with_sizes() { w_exact(20, Thunk::new(|to: &mut Tui|for (index, scene, ..) in self.scenes_with_sizes() {
to.place(&self.view_scene_name(index, scene)); to.place(&self.view_scene_name(index, scene));
})) }))
} }
fn view_scene_name <'a> (&'a self, index: usize, scene: &'a Scene) -> impl Content<TuiOut> + 'a { fn view_scene_name <'a> (&'a self, index: usize, scene: &'a Scene) -> impl Draw<Tui> + 'a {
let h = if self.selection().scene() == Some(index) && let Some(_editor) = self.editor() { let h = if self.selection().scene() == Some(index) && let Some(_editor) = self.editor() {
7 7
} else { } else {
@ -756,13 +756,13 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize +
} else { } else {
scene.color.base.rgb 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))))); Tui::fg(Tui::g(255), Tui::bold(true, &scene.name)))));
let b = When::new(self.selection().scene() == Some(index) && self.is_editing(), let b = when(self.selection().scene() == Some(index) && self.is_editing(),
Fill::XY(Align::nw(Bsp::s( wh_full(origin_nw(south(
self.editor().as_ref().map(|e|e.clip_status()), self.editor().as_ref().map(|e|e.clip_status()),
self.editor().as_ref().map(|e|e.edit_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))))
} }
} }

2
tengri

@ -1 +1 @@
Subproject commit 9dbf4fcab5f31a68e3d24c8f8f7fc866159e89f1 Subproject commit cf57f44933c45507e8de072e32c284f20f12ac7a