fixed Map operator!

This commit is contained in:
🪞👃🪞 2025-01-06 23:12:25 +01:00
parent 7ff731133c
commit 38e2e64751
11 changed files with 362 additions and 436 deletions

View file

@ -4,13 +4,11 @@ mod arranger_command; pub(crate) use self::arranger_command::*;
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_tui; pub(crate) use self::arranger_tui::*;
mod arranger_mode; pub(crate) use self::arranger_mode::*;
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_sep; pub(crate) use self::arranger_v_sep::*;
pub(crate) const HEADER_H: u16 = 5;
pub(crate) const HEADER_H: u16 = 0; // 5
pub(crate) const SCENES_W_OFFSET: u16 = 3;
/// Root view for standalone `tek_arranger`
@ -98,160 +96,6 @@ from_jack!(|jack| Arranger {
compact: false,
}
});
//impl Arranger {
//fn render_mode (state: &Self) -> impl Content<TuiOut> + 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;
//let layout = Bsp::a(Fill::xy(ArrangerStatus::from(self)),
//Bsp::n(Fixed::x(pool_w, PoolView(self.pool.visible, &self.pool)),
//Bsp::n(TransportView::new(true, &self.clock),
//Bsp::s(Fixed::y(1, MidiEditStatus(&self.editor)),
//Bsp::n(Fill::x(Fixed::y(20,
//Bsp::a(Fill::xy(Tui::bg(color.darkest.rgb, "background")),
//Bsp::a(
//Fill::xy(Outer(Style::default().fg(color.dark.rgb).bg(color.darkest.rgb))),
//Self::render_mode(self))))), Fill::y(&"fixme: self.editor"))))));
//self.size.of(layout)
//});
render!(TuiOut: (self: Arranger) => self.size.of(
Bsp::s(self.toolbar_view(),
Bsp::n(self.status_view(),
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<TuiOut> + use<'_> {
Fill::x(Fixed::y(2, Align::x(TransportView::new(true, &self.clock))))
}
fn status_view (&self) -> impl Content<TuiOut> + use<'_> {
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<TuiOut> + use<'_> {
row!(
//ClipSelected::play_phrase(&self.player),
//ClipSelected::next_phrase(&self.player),
MidiEditClip(&self.editor),
MidiEditStatus(&self.editor),
)
}
fn pool_view (&self) -> impl Content<TuiOut> + use<'_> {
let w = self.size.w();
let phrase_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
let pool_w = if self.pool.visible { phrase_w } else { 0 };
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<TuiOut> + use<'_> {
let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16;
fn row <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
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<TuiOut> {
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<Timebase>) -> impl Content<TuiOut> {
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<Moment>)
-> Option<impl Content<TuiOut>>
{
let timebase = &current.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<TuiOut> + use<'_> {
let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16;
fn row <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
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<impl Content<TuiOut>> {
Ok(format!(">{}", track.player.midi_ins().first().map(|port|port.short_name())
.transpose()?.unwrap_or("?".into())))
}
fn outs (&self) -> impl Content<TuiOut> + use<'_> {
let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16;
fn row <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
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<impl Content<TuiOut>> {
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 };
//let color = self.color;