From 6755f972f369833dfc5a054be31726b3b4db26dc Mon Sep 17 00:00:00 2001 From: unspeaker Date: Wed, 29 Jan 2025 06:53:34 +0100 Subject: [PATCH] implement scrollbar components --- tek/src/view.rs | 1 - tek/src/view_clock.rs | 23 ++------- tek/src/view_scene.rs | 22 +++++---- tui/src/tui_content.rs | 105 ++++++++++++++++++++++++++++------------- 4 files changed, 88 insertions(+), 63 deletions(-) diff --git a/tek/src/view.rs b/tek/src/view.rs index 33dd0e71..7f3efe06 100644 --- a/tek/src/view.rs +++ b/tek/src/view.rs @@ -20,7 +20,6 @@ view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); { ":sampler" => ().boxed(),//self.view_sampler(self.is_editing(), &self.editor).boxed(), ":scene-add" => self.view_scene_add().boxed(), ":scenes" => self.view_scenes().boxed(), - ":toolbar" => self.view_clock().boxed(), ":transport" => self.view_transport().boxed(), ":status" => self.view_status().boxed(), ":tracks" => self.view_tracks().boxed(), diff --git a/tek/src/view_clock.rs b/tek/src/view_clock.rs index 202bd0cb..f51c9127 100644 --- a/tek/src/view_clock.rs +++ b/tek/src/view_clock.rs @@ -29,23 +29,6 @@ impl Tek { fmtd.bpm.update(None, rewrite!(buf, "---.---")); } } - pub(crate) fn view_clock (&self) -> impl Content + use<'_> { - self.update_clock(); - let theme = ItemPalette::G[96]; - let fmtd = self.fmtd.read().unwrap(); - Bsp::a( - Fill::xy(Align::w(self.view_play_pause())), - Fill::xy(Align::e(row!( - FieldH(theme, "Sel", self.selected.describe(&self.tracks, &self.scenes)), - FieldH(theme, "SR", fmtd.sr.view.clone()), - FieldH(theme, "Buf", fmtd.buf.view.clone()), - FieldH(theme, "Lat", fmtd.lat.view.clone()), - FieldH(theme, "BPM", fmtd.bpm.view.clone()), - FieldH(theme, "Beat", fmtd.beat.view.clone()), - FieldH(theme, "Time", fmtd.time.view.clone()) - ))) - ) - } pub(crate) fn view_status (&self) -> impl Content + use<'_> { self.update_clock(); let theme = ItemPalette::G[96]; @@ -66,9 +49,9 @@ impl Tek { Tui::bg(Black, row!(Bsp::a( Fill::xy(Align::w(FieldH(theme, "Selected", self.selected.describe(&self.tracks, &self.scenes)))), Fill::xy(Align::e(row!( - FieldH(theme, "SR", fmtd.sr.view.clone()), - FieldH(theme, "Buf", fmtd.buf.view.clone()), - FieldH(theme, "Lat", fmtd.lat.view.clone()), + FieldH(theme, "SR", fmtd.sr.view.clone()), + FieldH(theme, "Buf", fmtd.buf.view.clone()), + FieldH(theme, "Lat", fmtd.lat.view.clone()), ))) ))) } diff --git a/tek/src/view_scene.rs b/tek/src/view_scene.rs index 5bf04e7c..9f54dfb9 100644 --- a/tek/src/view_scene.rs +++ b/tek/src/view_scene.rs @@ -19,17 +19,19 @@ impl Tek { y += height; data})} fn scene_scrollbar (&self) -> impl Content + use<'_> { - Bsp::s(Tui::fg_bg(Rgb(255,255,255), Rgb(0,0,0),"▲"), - Bsp::n(Tui::fg_bg(Rgb(255,255,255), Rgb(0,0,0),"▼"), Bsp::a( - Fill::y(Align::nw(Fixed::xy(1, 1, Tui::fg(Rgb(255,255,255), "┃")))), - Fill::y(Fixed::x(1, Tui::fg(Rgb(0,0,0), RepeatV("┊")))), - ))) } + Fill::y(Fixed::x(1, ScrollbarV { + offset: self.scene_scroll, + length: 0, + total: self.tracks.len() + })) + } fn track_scrollbar (&self) -> impl Content + use<'_> { - Bsp::e(Tui::fg_bg(Rgb(255,255,255), Rgb(0,0,0), "🞀"), - Bsp::w(Tui::fg_bg(Rgb(255,255,255), Rgb(0,0,0), "🞂"), Bsp::a( - Fill::x(Align::nw(Fixed::xy(1, 1, Tui::fg(Rgb(255,255,255), "━")))), - Fill::x(Fixed::y(1, Tui::fg(Rgb(0,0,0), RepeatH("┈")))), - ))) } + Fill::x(Fixed::y(1, ScrollbarH { + offset: self.track_scroll, + length: 0,// self.view_track_count(), + total: self.tracks.len() + })) + } pub fn view_scenes (&self) -> impl Content + use<'_> { let w_full = self.w(); let w = self.w_tracks_area(); diff --git a/tui/src/tui_content.rs b/tui/src/tui_content.rs index b87bd6d5..7339b250 100644 --- a/tui/src/tui_content.rs +++ b/tui/src/tui_content.rs @@ -1,5 +1,6 @@ use crate::*; use crate::Color::*; +use ratatui::prelude::Position; macro_rules! impl_content_layout_render { ($Output:ty: |$self:ident: $Struct:ty, $to:ident| layout = $layout:expr; render = $render:expr) => { impl Content<$Output> for $Struct { @@ -34,7 +35,6 @@ impl> Content for std::sync::Arc { } } - pub struct FieldH(pub ItemPalette, pub T, pub U); impl, U: Content> Content for FieldH { fn content (&self) -> impl Render { @@ -62,15 +62,13 @@ impl, U: Content> Content for FieldV { pub struct Repeat<'a>(pub &'a str); impl Content for Repeat<'_> { - fn layout (&self, to: [u16;4]) -> [u16;4] { - to - } + fn layout (&self, to: [u16;4]) -> [u16;4] { to } fn render (&self, to: &mut TuiOut) { let [x, y, w, h] = to.area().xywh(); let a = self.0.len(); for (_v, y) in (y..y+h).enumerate() { for (u, x) in (x..x+w).enumerate() { - if let Some(cell) = to.buffer.cell_mut(ratatui::prelude::Position::from((x, y))) { + if let Some(cell) = to.buffer.cell_mut(Position::from((x, y))) { let u = u % a; cell.set_symbol(&self.0[u..u+1]); } @@ -81,13 +79,11 @@ impl Content for Repeat<'_> { pub struct RepeatV<'a>(pub &'a str); impl Content for RepeatV<'_> { - fn layout (&self, to: [u16;4]) -> [u16;4] { - to - } + fn layout (&self, to: [u16;4]) -> [u16;4] { to } fn render (&self, to: &mut TuiOut) { let [x, y, _w, h] = to.area().xywh(); for y in y..y+h { - if let Some(cell) = to.buffer.cell_mut(ratatui::prelude::Position::from((x, y))) { + if let Some(cell) = to.buffer.cell_mut(Position::from((x, y))) { cell.set_symbol(&self.0); } } @@ -96,19 +92,83 @@ impl Content for RepeatV<'_> { pub struct RepeatH<'a>(pub &'a str); impl Content for RepeatH<'_> { - fn layout (&self, to: [u16;4]) -> [u16;4] { - to - } + fn layout (&self, to: [u16;4]) -> [u16;4] { to } fn render (&self, to: &mut TuiOut) { let [x, y, w, _h] = to.area().xywh(); for x in x..x+w { - if let Some(cell) = to.buffer.cell_mut(ratatui::prelude::Position::from((x, y))) { + if let Some(cell) = to.buffer.cell_mut(Position::from((x, y))) { cell.set_symbol(&self.0); } } } } +pub struct ScrollbarV { + pub offset: usize, + pub length: usize, + pub total: usize, +} +impl ScrollbarV { + const ICON_DEC: &[char] = &['▲']; + const ICON_INC: &[char] = &['▼']; +} +impl Content for ScrollbarV { + fn render (&self, to: &mut TuiOut) { + let [x, y1, _w, h] = to.area().xywh(); + let y2 = y1 + h; + for (i, y) in (y1..=y2).enumerate() { + if let Some(cell) = to.buffer.cell_mut(Position::from((x, y))) { + if (i as usize) < (Self::ICON_DEC.len()) { + cell.set_fg(Rgb(255, 255, 255)); + cell.set_bg(Rgb(0, 0, 0)); + cell.set_char(Self::ICON_DEC[i as usize]); + } else if (i as usize) > (h as usize - Self::ICON_INC.len()) { + cell.set_fg(Rgb(255, 255, 255)); + cell.set_bg(Rgb(0, 0, 0)); + cell.set_char(Self::ICON_INC[h as usize - i]); + } else { + cell.set_fg(Rgb(0, 0, 0)); + cell.set_bg(Reset); + cell.set_char('╎'); // ━ + } + } + } + } +} + +pub struct ScrollbarH { + pub offset: usize, + pub length: usize, + pub total: usize, +} +impl ScrollbarH { + const ICON_DEC: &[char] = &[' ', '🞀', ' ']; + const ICON_INC: &[char] = &[' ', '🞂', ' ']; +} +impl Content for ScrollbarH { + fn render (&self, to: &mut TuiOut) { + let [x1, y, w, _h] = to.area().xywh(); + let x2 = x1 + w; + for (i, x) in (x1..=x2).enumerate() { + if let Some(cell) = to.buffer.cell_mut(Position::from((x, y))) { + if i < (Self::ICON_DEC.len()) { + cell.set_fg(Rgb(255, 255, 255)); + cell.set_bg(Rgb(0, 0, 0)); + cell.set_char(Self::ICON_DEC[x as usize]); + } else if i > (w as usize - Self::ICON_INC.len()) { + cell.set_fg(Rgb(255, 255, 255)); + cell.set_bg(Rgb(0, 0, 0)); + cell.set_char(Self::ICON_INC[w as usize - i]); + } else { + cell.set_fg(Rgb(0, 0, 0)); + cell.set_bg(Reset); + cell.set_char('╌'); // ━ + } + } + } + } +} + /// A cell that takes up 3 rows on its own, /// but stacks, giving (N+1)*2 rows per N cells. pub struct Phat { @@ -445,25 +505,6 @@ border! { } } -//pub trait TuiStyle: Render + Sized { - //fn fg (self, color: Color) -> impl Render { - //Layers::new(move |add|{ add(&Foreground(color))?; add(&self) }) - //} - //fn bg (self, color: Color) -> impl Render { - //Layers::new(move |add|{ add(&Background(color))?; add(&self) }) - //} - //fn bold (self, on: bool) -> impl Render { - //Layers::new(move |add|{ add(&Bold(on))?; add(&self) }) - //} - //fn border (self, style: S) -> impl Render { - //Bordered(style, self) - //} -//} - -//impl> TuiStyle for R {} - -//impl Content for Border { -//} //impl> Content for Bordered { //fn content (&self) -> impl Render {