From d401870b2d918d2edf9ce26a2960b030a7025ede Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 16 Dec 2024 04:18:40 +0100 Subject: [PATCH] refactor bsp, rebalance color, BIG PLAY BUTTON --- crates/tek/src/api/note.rs | 2 +- crates/tek/src/core/color.rs | 6 +- crates/tek/src/layout/bsp.rs | 219 +++++++++++++++++----------- crates/tek/src/tui.rs | 16 +- crates/tek/src/tui/app_sequencer.rs | 47 +++--- crates/tek/src/tui/app_transport.rs | 57 ++++---- crates/tek/src/tui/phrase_editor.rs | 10 +- crates/tek/src/tui/status_bar.rs | 7 +- crates/tek/src/tui/tui_style.rs | 3 + 9 files changed, 215 insertions(+), 152 deletions(-) diff --git a/crates/tek/src/api/note.rs b/crates/tek/src/api/note.rs index 3d4e1e49..6267a8d2 100644 --- a/crates/tek/src/api/note.rs +++ b/crates/tek/src/api/note.rs @@ -86,7 +86,7 @@ impl Default for MidiPointModel { fn default () -> Self { Self { time_point: Arc::new(0.into()), - note_point: Arc::new(0.into()), + note_point: Arc::new(36.into()), note_len: Arc::new(24.into()), } } diff --git a/crates/tek/src/core/color.rs b/crates/tek/src/core/color.rs index 99b9a48c..48d10fec 100644 --- a/crates/tek/src/core/color.rs +++ b/crates/tek/src/core/color.rs @@ -61,11 +61,11 @@ impl From for ItemPalette { impl From for ItemPalette { fn from (base: ItemColor) -> Self { let mut light = base.okhsl.clone(); - light.lightness = (light.lightness * 1.33).min(Okhsl::::max_lightness()); + light.lightness = (light.lightness * 4. / 3.).min(Okhsl::::max_lightness()); let mut lighter = light.clone(); - lighter.lightness = (lighter.lightness * 1.33).min(Okhsl::::max_lightness()); + lighter.lightness = (lighter.lightness * 5. / 3.).min(Okhsl::::max_lightness()); let mut lightest = lighter.clone(); - lightest.lightness = (lightest.lightness * 1.33).min(Okhsl::::max_lightness()); + lightest.lightness = (lightest.lightness * 4. / 3.).min(Okhsl::::max_lightness()); let mut dark = base.okhsl.clone(); dark.lightness = (dark.lightness * 0.75).max(Okhsl::::min_lightness()); diff --git a/crates/tek/src/layout/bsp.rs b/crates/tek/src/layout/bsp.rs index a0c3c07c..1cdead54 100644 --- a/crates/tek/src/layout/bsp.rs +++ b/crates/tek/src/layout/bsp.rs @@ -1,105 +1,150 @@ use crate::*; -impl LayoutBspStatic for E {} +pub enum Bsp, Y: Render> { + /// X is north of Y + N(Option, Option), + /// X is south of Y + S(Option, Option), + /// X is east of Y + E(Option, Option), + /// X is west of Y + W(Option, Option), + /// X is above Y + A(Option, Option), + /// X is below Y + B(Option, Option), + /// Should be avoided. + Null(PhantomData), +} -pub trait LayoutBspStatic: { - fn over , B: Render> (a: A, b: B) -> Over { - Over(Default::default(), a, b) - } - fn under , B: Render> (a: A, b: B) -> Under { - Under(Default::default(), a, b) - } - fn to_north , B: Render> (a: A, b: B) -> ToNorth { - ToNorth(None, a, b) - } - fn to_south , B: Render> (a: A, b: B) -> ToSouth { - ToSouth(None, a, b) - } - fn to_east , B: Render> (a: A, b: B) -> ToEast { - ToEast(None, a, b) - } - fn to_west , B: Render> (a: A, b: B) -> ToWest { - ToWest(None, a, b) +render!(|self:Bsp, Y: Render>|match self { + Bsp::Null(_) => { () }, + Bsp::N(a, b) => { todo!("") }, + Bsp::S(a, b) => { todo!("") }, + Bsp::E(a, b) => { todo!("") }, + Bsp::W(a, b) => { todo!("") }, + Bsp::A(a, b) => { todo!("") }, + Bsp::B(a, b) => { todo!("") }, +}); + +impl, Y: Render> Bsp { + pub fn new (x: X) -> Self { Self::A(Some(x), None) } + pub fn n (x: X, y: Y) -> Self { Self::N(Some(x), Some(y)) } + pub fn s (x: X, y: Y) -> Self { Self::S(Some(x), Some(y)) } + pub fn e (x: X, y: Y) -> Self { Self::E(Some(x), Some(y)) } + pub fn w (x: X, y: Y) -> Self { Self::W(Some(x), Some(y)) } + pub fn a (x: X, y: Y) -> Self { Self::A(Some(x), Some(y)) } + pub fn b (x: X, y: Y) -> Self { Self::B(Some(x), Some(y)) } +} + +impl, Y: Render> Default for Bsp { + fn default () -> Self { + Self::Null(Default::default()) } } -pub trait LayoutBspFixedStatic: { - fn to_north , B: Render> (n: E::Unit, a: A, b: B) -> ToNorth { - ToNorth(Some(n), a, b) - } - fn to_south , B: Render> (n: E::Unit, a: A, b: B) -> ToSouth { - ToSouth(Some(n), a, b) - } - fn to_east , B: Render> (n: E::Unit, a: A, b: B) -> ToEast { - ToEast(Some(n), a, b) - } - fn to_west , B: Render> (n: E::Unit, a: A, b: B) -> ToWest { - ToWest(Some(n), a, b) - } -} +/////////////////////////////////////////////////////////////////////////////////////////////////// -pub struct Over, B: Render>(PhantomData, A, B); +//impl LayoutBspStatic for E {} -pub struct Under, B: Render>(PhantomData, A, B); +//pub trait LayoutBspStatic: { + //fn n , B: Render> (a: A, b: B) -> ToNorth { + //ToNorth(None, a, b) + //} + //fn s , B: Render> (a: A, b: B) -> ToSouth { + //ToSouth(None, a, b) + //} + //fn e , B: Render> (a: A, b: B) -> ToEast { + //ToEast(None, a, b) + //} + //fn w , B: Render> (a: A, b: B) -> ToWest { + //ToWest(None, a, b) + //} + //fn i , B: Render> (a: A, b: B) -> Over { + //Over(Default::default(), a, b) + //} + //fn o , B: Render> (a: A, b: B) -> Under { + //Under(Default::default(), a, b) + //} +//} -pub struct ToNorth, B: Render>(Option, A, B); +//pub trait LayoutBspFixedStatic: { + //fn to_north , B: Render> (n: E::Unit, a: A, b: B) -> ToNorth { + //ToNorth(Some(n), a, b) + //} + //fn to_south , B: Render> (n: E::Unit, a: A, b: B) -> ToSouth { + //ToSouth(Some(n), a, b) + //} + //fn to_east , B: Render> (n: E::Unit, a: A, b: B) -> ToEast { + //ToEast(Some(n), a, b) + //} + //fn to_west , B: Render> (n: E::Unit, a: A, b: B) -> ToWest { + //ToWest(Some(n), a, b) + //} +//} -pub struct ToSouth, B: Render>(Option, A, B); +//pub struct Over, B: Render>(PhantomData, A, B); -pub struct ToEast(Option, A, B); +//pub struct Under, B: Render>(PhantomData, A, B); -pub struct ToWest, B: Render>(Option, A, B); +//pub struct ToNorth, B: Render>(Option, A, B); -impl, B: Render> Render for Over { - fn min_size (&self, _: E::Size) -> Perhaps { - todo!(); - } - fn render (&self, _: &mut E::Output) -> Usually<()> { - Ok(()) - } -} +//pub struct ToSouth, B: Render>(Option, A, B); -impl, B: Render> Render for Under { - fn min_size (&self, _: E::Size) -> Perhaps { - todo!(); - } - fn render (&self, _: &mut E::Output) -> Usually<()> { - Ok(()) - } -} +//pub struct ToEast(Option, A, B); -impl, B: Render> Render for ToNorth { - fn min_size (&self, _: E::Size) -> Perhaps { - todo!(); - } - fn render (&self, _: &mut E::Output) -> Usually<()> { - Ok(()) - } -} +//pub struct ToWest, B: Render>(Option, A, B); -impl, B: Render> Render for ToSouth { - fn min_size (&self, _: E::Size) -> Perhaps { - todo!(); - } - fn render (&self, _: &mut E::Output) -> Usually<()> { - Ok(()) - } -} +//impl, B: Render> Render for Over { + //fn min_size (&self, _: E::Size) -> Perhaps { + //todo!(); + //} + //fn render (&self, _: &mut E::Output) -> Usually<()> { + //Ok(()) + //} +//} -impl, B: Render> Render for ToWest { - fn min_size (&self, _: E::Size) -> Perhaps { - todo!(); - } - fn render (&self, _: &mut E::Output) -> Usually<()> { - Ok(()) - } -} +//impl, B: Render> Render for Under { + //fn min_size (&self, _: E::Size) -> Perhaps { + //todo!(); + //} + //fn render (&self, _: &mut E::Output) -> Usually<()> { + //Ok(()) + //} +//} -impl, B: Render> Render for ToEast { - fn min_size (&self, _: E::Size) -> Perhaps { - todo!(); - } - fn render (&self, _: &mut E::Output) -> Usually<()> { - Ok(()) - } -} +//impl, B: Render> Render for ToNorth { + //fn min_size (&self, _: E::Size) -> Perhaps { + //todo!(); + //} + //fn render (&self, _: &mut E::Output) -> Usually<()> { + //Ok(()) + //} +//} + +//impl, B: Render> Render for ToSouth { + //fn min_size (&self, _: E::Size) -> Perhaps { + //todo!(); + //} + //fn render (&self, _: &mut E::Output) -> Usually<()> { + //Ok(()) + //} +//} + +//impl, B: Render> Render for ToWest { + //fn min_size (&self, _: E::Size) -> Perhaps { + //todo!(); + //} + //fn render (&self, _: &mut E::Output) -> Usually<()> { + //Ok(()) + //} +//} + +//impl, B: Render> Render for ToEast { + //fn min_size (&self, _: E::Size) -> Perhaps { + //todo!(); + //} + //fn render (&self, _: &mut E::Output) -> Usually<()> { + //Ok(()) + //} +//} diff --git a/crates/tek/src/tui.rs b/crates/tek/src/tui.rs index 0fdb9d50..e5628203 100644 --- a/crates/tek/src/tui.rs +++ b/crates/tek/src/tui.rs @@ -173,14 +173,16 @@ impl Tui { } } -struct Field(&'static str, String); +/////////////////////////////////////////////////////////////////////////////////////////////////// -render!(|self: Field|{ - Tui::to_east("│", Tui::to_east( - Tui::bold(true, self.0), - Tui::bg(Color::Rgb(0, 0, 0), self.1.as_str()), - )) -}); +//struct Field(&'static str, String); + +//render!(|self: Field|{ + //Tui::to_east("│", Tui::to_east( + //Tui::bold(true, self.0), + //Tui::bg(Color::Rgb(0, 0, 0), self.1.as_str()), + //)) +//}); //pub struct TransportView { //pub(crate) state: Option, diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index f0625a0a..7703d467 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -85,6 +85,10 @@ impl Command for SequencerCommand { impl InputToCommand for SequencerCommand { fn input_to_command (state: &SequencerTui, input: &TuiInput) -> Option { Some(match input.event() { + key_pat!(Char('u')) => { todo!("undo") }, + key_pat!(Char('U')) => { todo!("redo") }, + key_pat!(Ctrl-Char('k')) => { todo!("keyboard") }, + // Toggle visibility of phrase pool column key_pat!(Tab) => ShowPool(!state.show_pool), // Enqueue currently edited phrase @@ -156,14 +160,16 @@ render!(|self: SequencerTui|{ let with_size = |x|lay!([self.size, x]); let editor = with_bar(with_pool(Tui::fill_xy(&self.editor))); let color = self.player.play_phrase().as_ref().map(|(_,p)|p.as_ref().map(|p|p.read().unwrap().color)).flatten().clone(); - let play = Tui::fixed_xy(11, 2, PlayPause(self.clock.is_rolling())); - let playing = Tui::fixed_xy(14, 2, PhraseSelector::play_phrase(&self.player)); - let next = Tui::fixed_xy(14, 2, PhraseSelector::next_phrase(&self.player)); + let play = Tui::fixed_xy(5, 2, PlayPause(self.clock.is_rolling())); let transport = Tui::fixed_y(2, TransportView::from((self, color, true))); - let toolbar = row!([play, playing, next, transport]); + let toolbar = row!([play, col!([ + PhraseSelector::play_phrase(&self.player), + PhraseSelector::next_phrase(&self.player), + ]), transport]); with_size(with_status(col!([ toolbar, editor, ]))) }); +has_size!(|self:SequencerTui|&self.size); has_clock!(|self:SequencerTui|&self.clock); has_phrases!(|self:SequencerTui|self.phrases.phrases); has_editor!(|self:SequencerTui|self.editor); @@ -176,16 +182,12 @@ pub struct PhraseSelector { } // TODO: Display phrases always in order of appearance -render!(|self: PhraseSelector|Tui::fixed_y(2, col!([ - lay!(move|add|{ - add(&Tui::push_x(1, Tui::fg(TuiTheme::g(240), self.title)))?; - add(&Tui::bg(self.color.base.rgb, Tui::fill_x(Tui::inset_x(1, Tui::fill_x(Tui::at_e( - Tui::fg(self.color.lightest.rgb, &self.time)))))))?; - Ok(()) - }), - Tui::bg(self.color.base.rgb, - Tui::fg(self.color.lightest.rgb, - Tui::bold(true, self.name.clone()))), +render!(|self: PhraseSelector|Tui::fixed_xy(24, 1, row!([ + Tui::fg(self.color.lighter.rgb, Tui::bold(true, &self.title)), + Tui::bg(self.color.base.rgb, Tui::fg(self.color.lighter.rgb, row!([ + format!("{:8}", &self.name[0..8.min(self.name.len())]), + Tui::bg(self.color.dark.rgb, &self.time), + ]))), ]))); impl PhraseSelector { @@ -200,9 +202,9 @@ impl PhraseSelector { let time = if let Some(elapsed) = state.pulses_since_start_looped() { format!("+{:>}", state.clock().timebase.format_beats_0(elapsed)) } else { - String::from("") + String::from(" ") }; - Self { title: "Now:", time, name, color, } + Self { title: " Now|", time, name, color, } } // beats until switchover pub fn next_phrase (state: &T) -> Self { @@ -219,17 +221,24 @@ impl PhraseSelector { } }; (time, name.clone(), color) + } else if let Some((_, Some(phrase))) = state.play_phrase() { + let phrase = phrase.read().unwrap(); + if phrase.loop_on { + (" ".into(), phrase.name.clone(), phrase.color.clone()) + } else { + (" ".into(), " ".into(), TuiTheme::g(64).into()) + } } else { - ("".into(), "".into(), TuiTheme::g(64).into()) + (" ".into(), " ".into(), TuiTheme::g(64).into()) }; - Self { title: "Next:", time, name, color, } + Self { title: " Next|", time, name, color, } } pub fn edit_phrase (phrase: &Option>>) -> Self { let (time, name, color) = if let Some(phrase) = phrase { let phrase = phrase.read().unwrap(); (format!("{}", phrase.length), phrase.name.clone(), phrase.color) } else { - ("".to_string(), "".to_string(), ItemPalette::from(TuiTheme::g(64))) + ("".to_string(), " ".to_string(), ItemPalette::from(TuiTheme::g(64))) }; Self { title: "Editing:", time, name, color } } diff --git a/crates/tek/src/tui/app_transport.rs b/crates/tek/src/tui/app_transport.rs index d6da53e9..af4660d7 100644 --- a/crates/tek/src/tui/app_transport.rs +++ b/crates/tek/src/tui/app_transport.rs @@ -42,18 +42,18 @@ audio!(|self:TransportTui,client,scope|ClockAudio(self).process(client, scope)); render!(|self: TransportTui|TransportView::from((self, None, true))); pub struct TransportView { - bg: Color, - focused: bool, + color: ItemPalette, + focused: bool, - sr: String, - bpm: String, - ppq: String, - beat: String, + sr: String, + bpm: String, + ppq: String, + beat: String, - global_sample: String, - global_second: String, + global_sample: String, + global_second: String, - started: bool, + started: bool, current_sample: f64, current_second: f64, @@ -66,13 +66,12 @@ impl From<(&T, Option, bool)> for TransportView { let bpm = format!("{:.3}", clock.timebase.bpm.get()); let ppq = format!("{:.0}", clock.timebase.ppq.get()); let color = color.unwrap_or(ItemPalette::from(TuiTheme::g(32))); - let bg = color.dark.rgb; if let Some(started) = clock.started.read().unwrap().as_ref() { let current_sample = (clock.global.sample.get() - started.sample.get())/1000.; let current_usec = clock.global.usec.get() - started.usec.get(); let current_second = current_usec/1000000.; Self { - bg, focused, sr, bpm, ppq, + color, focused, sr, bpm, ppq, started: true, global_sample: format!("{:.0}k", started.sample.get()/1000.), global_second: format!("{:.1}s", started.usec.get()/1000.), @@ -84,7 +83,7 @@ impl From<(&T, Option, bool)> for TransportView { } } else { Self { - bg, focused, sr, bpm, ppq, + color, focused, sr, bpm, ppq, started: false, global_sample: format!("{:.0}k", clock.global.sample.get()/1000.), global_second: format!("{:.1}s", clock.global.usec.get()/1000000.), @@ -99,23 +98,25 @@ impl From<(&T, Option, bool)> for TransportView { render!(|self: TransportView|{ - struct Field<'a>(&'a str, &'a str); + let color = self.color; + + struct Field<'a>(&'a str, &'a str, &'a ItemPalette); render!(|self: Field<'a>|row!([ - Tui::fg(Color::Rgb(200, 200, 200), self.0), - " ", - Tui::bold(true, Tui::fg(Color::Rgb(220, 220, 220), self.1)), + Tui::bg(Color::Reset, Tui::bold(true, + Tui::fg(self.2.lighter.rgb, self.0))), + Tui::fg(self.2.lighter.rgb, format!("{:>10}", self.1)), ])); - Tui::bg(self.bg, Tui::fill_x(row!([ + Tui::bg(color.base.rgb, Tui::fill_x(row!([ //PlayPause(self.started), " ", col!([ - Field(" Beat", self.beat.as_str()), - Field(" BPM ", self.bpm.as_str()), + Field(" Beat|", self.beat.as_str(), &color), + Field(" BPM|", self.bpm.as_str(), &color), ]), " ", col!([ - Field("Time ", format!("{:.1}s", self.current_second).as_str()), - Field("Sample", format!("{:.0}k", self.current_sample).as_str()), + Field(" Time|", format!("{:.1}s", self.current_second).as_str(), &color), + Field(" Smpl|", format!("{:.1}k", self.current_sample).as_str(), &color), ]), ]))) @@ -124,11 +125,17 @@ render!(|self: TransportView|{ pub struct PlayPause(pub bool); render!(|self: PlayPause|Tui::bg( if self.0{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)}, - Tui::outset_x(1, Tui::fixed_x(9, col!(|add|if self.0 { - add(&Tui::fg(Color::Rgb(0, 255, 0), col!(["▶ PLAYING", "▒ ▒ ▒ ▒ ▒"]))) + Tui::fixed_x(5, col!(|add|if self.0 { + add(&Tui::fg(Color::Rgb(0, 255, 0), col!([ + " 🭍🭑🬽 ", + " 🭞🭜🭘 " + ]))) } else { - add(&Tui::fg(Color::Rgb(255, 128, 0), col!(["▒ ▒ ▒ ▒ ▒", "⏹ STOPPED"]))) - }))) + add(&Tui::fg(Color::Rgb(255, 128, 0), col!([ + " ▗▄▖ ", + " ▝▀▘ " + ]))) + })) )); impl HasFocus for TransportTui { diff --git a/crates/tek/src/tui/phrase_editor.rs b/crates/tek/src/tui/phrase_editor.rs index f254541a..17f146b4 100644 --- a/crates/tek/src/tui/phrase_editor.rs +++ b/crates/tek/src/tui/phrase_editor.rs @@ -107,15 +107,19 @@ impl Command for PhraseCommand { pub struct PhraseEditorModel { /// Renders the phrase pub mode: Box, + + pub size: Measure } impl Default for PhraseEditorModel { fn default () -> Self { - Self { mode: Box::new(PianoHorizontal::new(None)) } + Self { mode: Box::new(PianoHorizontal::new(None)), size: Measure::new() } } } -render!(|self: PhraseEditorModel|self.mode); +has_size!(|self:PhraseEditorModel|&self.size); +render!(|self: PhraseEditorModel|&self.mode); +//render!(|self: PhraseEditorModel|lay!(|add|{add(&self.size)?;add(self.mode)}));//bollocks pub trait PhraseViewMode: Render + HasSize + MidiRange + MidiPoint + Debug + Send + Sync { fn buffer_size (&self, phrase: &Phrase) -> (usize, usize); @@ -130,8 +134,6 @@ pub trait PhraseViewMode: Render + HasSize + MidiRange + MidiPoint + D impl MidiViewport for PhraseEditorModel {} -has_size!(|self:PhraseEditorModel|self.mode.size()); - impl MidiRange for PhraseEditorModel { fn time_zoom (&self) -> usize { self.mode.time_zoom() } fn set_time_zoom (&self, x: usize) { self.mode.set_time_zoom(x); } diff --git a/crates/tek/src/tui/status_bar.rs b/crates/tek/src/tui/status_bar.rs index b3af3c5b..926abf72 100644 --- a/crates/tek/src/tui/status_bar.rs +++ b/crates/tek/src/tui/status_bar.rs @@ -13,16 +13,11 @@ pub trait StatusBar: Render { row!([a, b, c] in commands.iter() => { row!([a, Tui::fg(hotkey_fg, Tui::bold(true, b)), c]) }) - //Tui::reduce(commands.iter(), |prev, [a, b, c]| - //Tui::to_east(prev, - //Tui::to_east(a, - //Tui::to_east(Tui::fg(hotkey_fg, Tui::bold(true, b)), - //c)))) } fn with <'a> (state: &'a Self::State, content: impl Render) -> impl Render where Self: Sized, &'a Self::State: Into { - Tui::to_north(state.into(), content) + Bsp::n(state.into(), content) } } diff --git a/crates/tek/src/tui/tui_style.rs b/crates/tek/src/tui/tui_style.rs index 8c7eb0ca..28c6075a 100644 --- a/crates/tek/src/tui/tui_style.rs +++ b/crates/tek/src/tui/tui_style.rs @@ -7,6 +7,9 @@ impl Tui { pub(crate) fn bg > (color: Color, w: W) -> Background { Background(color, w) } + pub(crate) fn fg_bg > (fg: Color, bg: Color, w: W) -> Background> { + Background(bg, Foreground(fg, w)) + } pub(crate) fn bold > (on: bool, w: W) -> Bold { Bold(on, w) }