diff --git a/src/arranger.rs b/src/arranger.rs index 4a858692..5f045e70 100644 --- a/src/arranger.rs +++ b/src/arranger.rs @@ -5,14 +5,14 @@ mod arranger_scene; pub(crate) use self::arranger_scene::*; mod arranger_select; pub(crate) use self::arranger_select::*; mod arranger_track; pub(crate) use self::arranger_track::*; mod arranger_mode; pub(crate) use self::arranger_mode::*; -mod arranger_v; #[allow(unused)] pub(crate) use self::arranger_v::*; mod arranger_h; mod arranger_v_clips; pub(crate) use self::arranger_v_clips::*; mod arranger_v_cursor; pub(crate) use self::arranger_v_cursor::*; -mod arranger_v_head; pub(crate) use self::arranger_v_head::*; -mod arranger_v_io; pub(crate) use self::arranger_v_io::*; mod arranger_v_sep; pub(crate) use self::arranger_v_sep::*; +pub(crate) const HEADER_H: u16 = 5; +pub(crate) const SCENES_W_OFFSET: u16 = 3; + /// Root view for standalone `tek_arranger` pub struct Arranger { jack: Arc>, @@ -98,14 +98,14 @@ from_jack!(|jack| Arranger { compact: false, } }); -impl Arranger { - fn render_mode (state: &Self) -> impl Content + use<'_> { - match state.mode { - ArrangerMode::H => todo!("horizontal arranger"), - ArrangerMode::V(factor) => Self::render_mode_v(state, factor), - } - } -} +//impl Arranger { + //fn render_mode (state: &Self) -> impl Content + use<'_> { + //match state.mode { + //ArrangerMode::H => todo!("horizontal arranger"), + //ArrangerMode::V(factor) => Self::render_mode_v(state, factor), + //} + //} +//} //render!(TuiOut: (self: Arranger) => { //let pool_w = if self.pool.visible { self.splits[1] } else { 0 }; //let color = self.color; @@ -122,17 +122,38 @@ impl Arranger { //}); render!(TuiOut: (self: Arranger) => self.size.of( Bsp::s(self.toolbar_view(), - Bsp::n(self.selector_view(), Bsp::n(self.status_view(), - Bsp::w(self.pool_view(), Fill::xy(&self.editor))))))); + Bsp::n(self.selector_view(), + Bsp::s(Align::nw(Fill::x(Fixed::y(3, self.header()))), + Bsp::s(Align::nw(Fill::x(Fixed::y(1, self.ins()))), + Bsp::n(Align::nw(Fill::x(Fixed::y(1, self.outs()))), + Bsp::w(self.pool_view(), Fill::xy(lay!( + Align::nw(Fill::xy(Tui::bg(self.color.darkest.rgb, " "))), + Align::nw(Fill::xy(ArrangerVColSep::from(self))), + Align::nw(Fill::xy(ArrangerVRowSep::from((self, 1)))), + Align::nw(Fill::xy(ArrangerVCursor::from((self, 1)))), + Align::nw(Fill::xy(":"))))))))))))); + //"todo:")))))))); + //Bsp::s( + //Align::nw(Fixed::y(1, Fill::x(ArrangerVIns::from(self)))), + //Bsp::s( + //Fixed::y(20, Align::nw(ArrangerVClips::new(self, 1))), + //Fill::x(Fixed::y(1, ArrangerVOuts::from(self))))))))))))); + //Bsp::s( + //Bsp::s( + //Bsp::s( + //Fill::xy(ArrangerVClips::new(self, 1)), + //Fill::x(ArrangerVOuts::from(self))))) + impl Arranger { fn toolbar_view (&self) -> impl Content + use<'_> { Fill::x(Fixed::y(2, Align::x(TransportView::new(true, &self.clock)))) } fn status_view (&self) -> impl Content + use<'_> { - let edit_clip = MidiEditClip(&self.editor); - //let selectors = When(false, Bsp::e(ClipSelected::play_phrase(&self.player), ClipSelected::next_phrase(&self.player))); - row!(/*selectors,*/ edit_clip, MidiEditStatus(&self.editor)) + ArrangerStatus::from(self) + //let edit_clip = MidiEditClip(&self.editor); + ////let selectors = When(false, Bsp::e(ClipSelected::play_phrase(&self.player), ClipSelected::next_phrase(&self.player))); + //row!([>selectors,<] edit_clip, MidiEditStatus(&self.editor)) } fn selector_view (&self) -> impl Content + use<'_> { row!( @@ -149,6 +170,87 @@ impl Arranger { let pool = Pull::y(1, Fill::y(Align::e(PoolView(self.pool.visible, &self.pool)))); Fixed::x(pool_w, Align::e(Fill::y(PoolView(self.compact, &self.pool)))) } + fn header (&self) -> impl Content + use<'_> { + let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16; + fn row > (color: ItemPalette, field: T) -> impl Content { + row!(Tui::fg(color.light.rgb, "▎"), Tui::fg(color.lightest.rgb, field)) + } + Push::x(scenes_w, Map(||ArrangerTrack::with_widths(self.tracks.as_slice()), |(_, track, x1, x2), i| { + let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H); + let color = track.color(); + Push::x(x1 as u16, Tui::bg(color.base.rgb, Min::xy(w as u16, h, Fixed::xy(w as u16, 5, col!( + row(color, Self::format_name(track, w)), + row(color, Self::format_elapsed(track, self.clock().timebase())), + row(color, Self::format_until_next(track, &self.clock().playhead)), + ))))) + })) + } + /// name and width of track + fn format_name (track: &ArrangerTrack, _w: usize) -> impl Content { + let name = track.name().read().unwrap().clone(); + Tui::bold(true, Tui::fg(track.color.lightest.rgb, name)) + } + /// beats elapsed + fn format_elapsed (track: &ArrangerTrack, timebase: &Arc) -> impl Content { + let mut result = String::new(); + if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() { + let length = phrase.read().unwrap().length; + let elapsed = track.player.pulses_since_start().unwrap(); + let elapsed = timebase.format_beats_1_short( + (elapsed as usize % length) as f64 + ); + result = format!("+{elapsed:>}") + } + result + } + /// beats until switchover + fn format_until_next (track: &ArrangerTrack, current: &Arc) + -> Option> + { + let timebase = ¤t.timebase; + let mut result = String::new(); + if let Some((t, _)) = track.player.next_phrase().as_ref() { + let target = t.pulse.get(); + let current = current.pulse.get(); + if target > current { + let remaining = target - current; + result = format!("-{:>}", timebase.format_beats_0_short(remaining)) + } + } + Some(result) + } + fn ins (&self) -> impl Content + use<'_> { + let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16; + fn row > (color: ItemPalette, field: T) -> impl Content { + row!(Tui::fg(color.light.rgb, "▎"), Tui::fg(color.lightest.rgb, field)) + } + Push::x(scenes_w, Map(||ArrangerTrack::with_widths(self.tracks.as_slice()), |(_, track, x1, x2), i| { + let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H); + let color = track.color(); + let input = Self::format_input(track); + Push::x(x1 as u16, Tui::bg(color.base.rgb, Min::xy(w as u16, h, Fixed::xy(w as u16, 5, row(color, Self::format_input(track).ok()))))) + })) + } + fn format_input (track: &ArrangerTrack) -> Usually> { + Ok(format!(">{}", track.player.midi_ins().first().map(|port|port.short_name()) + .transpose()?.unwrap_or("?".into()))) + } + fn outs (&self) -> impl Content + use<'_> { + let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16; + fn row > (color: ItemPalette, field: T) -> impl Content { + row!(Tui::fg(color.light.rgb, "▎"), Tui::fg(color.lightest.rgb, field)) + } + Push::x(scenes_w, Map(||ArrangerTrack::with_widths(self.tracks.as_slice()), |(_, track, x1, x2), i| { + let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H); + let color = track.color(); + Push::x(x2 as u16, Tui::bg(color.base.rgb, Min::xy(w as u16, h, Fixed::xy(w as u16, 5, row(color, Self::format_output(track).ok()))))) + })) + } + /// output port + fn format_output (track: &ArrangerTrack) -> Usually> { + Ok(format!("<{}", track.player.midi_outs().first().map(|port|port.short_name()) + .transpose()?.unwrap_or("?".into()))) + } } //render!(TuiOut: (self: Arranger) => { //let pool_w = if self.pool.visible { self.splits[1] } else { 0 }; diff --git a/src/arranger/arranger_v.rs b/src/arranger/arranger_v.rs index c88c38d1..3408d004 100644 --- a/src/arranger/arranger_v.rs +++ b/src/arranger/arranger_v.rs @@ -1,14 +1,12 @@ use crate::*; // egyptian snakes den -pub(crate) const HEADER_H: u16 = 5; -pub(crate) const SCENES_W_OFFSET: u16 = 3; impl Arranger { pub fn render_mode_v (state: &Arranger, factor: usize) -> impl Content + use<'_> { lay!( ArrangerVColSep::from(state), ArrangerVRowSep::from((state, factor)), col!( - ArrangerVHead::from(state), + //ArrangerVHead::from(state), ArrangerVIns::from(state), ArrangerVClips::new(state, factor), ArrangerVOuts::from(state), diff --git a/src/arranger/arranger_v_head.rs b/src/arranger/arranger_v_head.rs index 05ec1366..86069a59 100644 --- a/src/arranger/arranger_v_head.rs +++ b/src/arranger/arranger_v_head.rs @@ -1,84 +1,41 @@ use crate::*; use super::*; -pub struct ArrangerVHead<'a> { +pub struct ArrangerVIns<'a> { + size: &'a Measure, + tracks: &'a Vec, scenes_w: u16, - timebase: &'a Arc, - current: &'a Arc, - tracks: &'a [ArrangerTrack], } - -from!(<'a>|state: &'a Arranger|ArrangerVHead<'a> = Self { // A - tracks: &state.tracks, - timebase: state.clock().timebase(), - current: &state.clock().playhead, - scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&state.scenes) as u16, -}); - -render!(TuiOut: (self: ArrangerVHead<'a>) => { - fn row > (color: ItemPalette, field: T) -> impl Content { - row!(Tui::fg(color.light.rgb, "▎"), Tui::fg(color.lightest.rgb, field)) - } - Some(Push::x(self.scenes_w, - Map(||ArrangerTrack::with_widths(self.tracks), |(_, track, x1, x2), i| { - let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H); - let color = track.color(); - let input = Self::format_input(track); - let output = Self::format_output(track); - Tui::bg(color.base.rgb, Min::xy(w as u16, h, Fixed::xy(w as u16, 5, col!( - row(color, Self::format_name(track, w)), - row(color, Self::format_input(track).ok()), - row(color, Self::format_output(track).ok()), - row(color, Self::format_elapsed(track, self.timebase)), - row(color, Self::format_until_next(track, self.current)), - )))) - }) - )) -}); - -impl ArrangerVHead<'_> { - /// name and width of track - fn format_name (track: &ArrangerTrack, _w: usize) -> impl Content { - let name = track.name().read().unwrap().clone(); - Tui::bold(true, Tui::fg(track.color.lightest.rgb, name)) - } +impl ArrangerVIns<'_> { /// input port fn format_input (track: &ArrangerTrack) -> Usually> { Ok(format!(">{}", track.player.midi_ins().first().map(|port|port.short_name()) .transpose()?.unwrap_or("?".into()))) } - /// output port - fn format_output (track: &ArrangerTrack) -> Usually> { - Ok(format!("<{}", track.player.midi_outs().first().map(|port|port.short_name()) - .transpose()?.unwrap_or("?".into()))) - } - /// beats elapsed - fn format_elapsed (track: &ArrangerTrack, timebase: &Arc) -> impl Content { - let mut result = String::new(); - if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() { - let length = phrase.read().unwrap().length; - let elapsed = track.player.pulses_since_start().unwrap(); - let elapsed = timebase.format_beats_1_short( - (elapsed as usize % length) as f64 - ); - result = format!("+{elapsed:>}") - } - result - } - /// beats until switchover - fn format_until_next (track: &ArrangerTrack, current: &Arc) - -> Option> - { - let timebase = ¤t.timebase; - let mut result = String::new(); - if let Some((t, _)) = track.player.next_phrase().as_ref() { - let target = t.pulse.get(); - let current = current.pulse.get(); - if target > current { - let remaining = target - current; - result = format!("-{:>}", timebase.format_beats_0_short(remaining)) - } - } - Some(result) - } } + +from!(<'a>|args: &'a Arranger|ArrangerVIns<'a> = Self { + size: &args.size, + tracks: &args.tracks, + scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&args.scenes) as u16, +}); + +render!(TuiOut: (self: ArrangerVIns<'a>) => { +}); + +pub struct ArrangerVOuts<'a> { + size: &'a Measure, + tracks: &'a Vec, + scenes_w: u16, +} +impl ArrangerVOuts<'_> { +} + +from!(<'a>|args: &'a Arranger|ArrangerVOuts<'a> = Self { + size: &args.size, + tracks: &args.tracks, + scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&args.scenes) as u16, +}); + +render!(TuiOut: (self: ArrangerVOuts<'a>) => { +}); diff --git a/src/arranger/arranger_v_io.rs b/src/arranger/arranger_v_io.rs deleted file mode 100644 index 76cb05b3..00000000 --- a/src/arranger/arranger_v_io.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::*; -use super::*; - -pub struct ArrangerVIns<'a> { - size: &'a Measure, - tracks: &'a Vec, -} - -from!(<'a>|args: &'a Arranger|ArrangerVIns<'a> = Self { - size: &args.size, - tracks: &args.tracks, -}); - -render!(TuiOut: (self: ArrangerVIns<'a>) => ""); - -pub struct ArrangerVOuts<'a> { - size: &'a Measure, - tracks: &'a Vec, -} - -from!(<'a>|args: &'a Arranger|ArrangerVOuts<'a> = Self { - size: &args.size, - tracks: &args.tracks, -}); - -render!(TuiOut: (self: ArrangerVOuts<'a>) => ""); diff --git a/src/arranger/arranger_v_sep.rs b/src/arranger/arranger_v_sep.rs index 2bf2e7df..d522f6cd 100644 --- a/src/arranger/arranger_v_sep.rs +++ b/src/arranger/arranger_v_sep.rs @@ -26,17 +26,18 @@ pub struct ArrangerVRowSep { rows: Vec<(usize, usize)>, } from!(|args:(&Arranger, usize)|ArrangerVRowSep = Self { - fg: TuiTheme::separator_fg(false), + fg: Color::Rgb(255,255,255,), rows: ArrangerScene::ppqs(&args.0.scenes, args.1), }); render!(TuiOut: |self: ArrangerVRowSep, to|for y in self.rows.iter().map(|row|row.1) { let y = to.area().y() + (y / PPQ) as u16 + 1; if y >= to.buffer.area.height { break } for x in to.area().x()..to.area().x2().saturating_sub(2) { - if x < to.buffer.area.x && y < to.buffer.area.y { - let cell = to.buffer.get_mut(x, y); - cell.modifier = Modifier::UNDERLINED; - cell.underline_color = self.fg; - } + //if x < to.buffer.area.x && y < to.buffer.area.y { + if let Some(cell) = to.buffer.cell_mut(ratatui::prelude::Position::from((x, y))) { + cell.modifier = Modifier::UNDERLINED; + cell.underline_color = self.fg; + } + //} } });