diff --git a/Justfile b/Justfile index cd529cd9..e114f670 100644 --- a/Justfile +++ b/Justfile @@ -1,15 +1,13 @@ default: bacon -sj test - tui: - reset cargo run --example tui - -test: - cargo test --workspace --exclude jack cloc: for src in {cli,edn/src,input/src,jack/src,midi/src,output/src,plugin/src,sampler/src,tek/src,time/src,tui/src}; do echo; echo $src; cloc --quiet $src; done - +test: + cargo test --workspace --exclude jack +prof: + CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph -- arranger status: cargo c cloc --by-file src/ diff --git a/midi/src/midi_edit.rs b/midi/src/midi_edit.rs index 7dfc27c2..7443753c 100644 --- a/midi/src/midi_edit.rs +++ b/midi/src/midi_edit.rs @@ -133,7 +133,7 @@ impl MidiEditor { 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) } else { - (ItemPalette::from(TuiTheme::g(64)), String::new().into(), 0, false) + (ItemPalette::from(Tui::g(64)), String::new().into(), 0, false) }; row!( FieldV(color, "Edit", format!("{name} ({length})")), @@ -144,7 +144,7 @@ impl MidiEditor { let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { (clip.color, clip.length) } else { - (ItemPalette::from(TuiTheme::g(64)), 0) + (ItemPalette::from(Tui::g(64)), 0) }; let time_pos = self.time_pos(); let time_zoom = self.time_zoom().get(); diff --git a/midi/src/midi_launch.rs b/midi/src/midi_launch.rs index abf647eb..7092efea 100644 --- a/midi/src/midi_launch.rs +++ b/midi/src/midi_launch.rs @@ -37,7 +37,7 @@ pub trait HasPlayClip: HasClock { let MidiClip { ref name, color, .. } = *clip.read().unwrap(); (name.clone(), color) } else { - ("".into(), TuiTheme::g(64).into()) + ("".into(), Tui::g(64).into()) }; let time: String = self.pulses_since_start_looped() .map(|(times, time)|format!("{:>3}x {:>}", times+1.0, self.clock().timebase.format_beats_1(time))) @@ -47,7 +47,7 @@ pub trait HasPlayClip: HasClock { fn next_status (&self) -> impl Content { let mut time: Arc = String::from("--.-.--").into(); let mut name: Arc = String::from("").into(); - let mut color = ItemPalette::from(TuiTheme::g(64)); + let mut color = ItemPalette::from(Tui::g(64)); let clock = self.clock(); if let Some((t, Some(clip))) = self.next_clip() { let clip = clip.read().unwrap(); diff --git a/midi/src/midi_pool.rs b/midi/src/midi_pool.rs index db7edbd2..eae4101c 100644 --- a/midi/src/midi_pool.rs +++ b/midi/src/midi_pool.rs @@ -162,7 +162,7 @@ pub struct PoolView<'a>(pub bool, pub &'a MidiPool); content!(TuiOut: |self: PoolView<'a>| { let Self(compact, model) = self; let MidiPool { clips, .. } = self.1; - //let color = self.1.clip().map(|c|c.read().unwrap().color).unwrap_or_else(||TuiTheme::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 border = |x|x;//Outer(Style::default().fg(color.dark.rgb).bg(color.darkest.rgb)).enclose(x); let iter = | |model.clips().clone().into_iter(); @@ -178,8 +178,8 @@ content!(TuiOut: |self: PoolView<'a>| { Fixed::y(1, map_south(item_offset, item_height, Tui::bg(bg, lay!( Fill::x(Align::w(Tui::fg(fg, Tui::bold(selected, name)))), Fill::x(Align::e(Tui::fg(fg, Tui::bold(selected, length)))), - Fill::x(Align::w(When::new(selected, Tui::bold(true, Tui::fg(TuiTheme::g(255), "▶"))))), - Fill::x(Align::e(When::new(selected, Tui::bold(true, Tui::fg(TuiTheme::g(255), "◀"))))), + Fill::x(Align::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), "◀"))))), )))) }))))) }); diff --git a/midi/src/piano_h.rs b/midi/src/piano_h.rs index dd07cbaa..2168ea81 100644 --- a/midi/src/piano_h.rs +++ b/midi/src/piano_h.rs @@ -30,7 +30,7 @@ impl PianoHorizontal { clip: clip.cloned(), color: clip.as_ref() .map(|p|p.read().unwrap().color) - .unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64)))), + .unwrap_or(ItemPalette::from(ItemColor::from(Tui::g(64)))), }; piano.redraw(); piano @@ -39,14 +39,14 @@ impl PianoHorizontal { pub(crate) fn note_y_iter (note_lo: usize, note_hi: usize, y0: u16) -> impl Iterator { (note_lo..=note_hi).rev().enumerate().map(move|(y, n)|(y, y0 + y as u16, n)) } -content!(TuiOut:|self: PianoHorizontal| Tui::bg(TuiTheme::g(40), Bsp::s( +content!(TuiOut:|self: PianoHorizontal| Tui::bg(Tui::g(40), Bsp::s( Bsp::e( Fixed::x(5, format!("{}x{}", self.size.w(), self.size.h())), self.timeline() ), Bsp::e( self.keys(), - self.size.of(Tui::bg(TuiTheme::g(32), Bsp::b( + self.size.of(Tui::bg(Tui::g(32), Bsp::b( Fill::xy(self.notes()), Fill::xy(self.cursor()), ))) @@ -185,8 +185,8 @@ impl PianoHorizontal { let note_hi = state.note_hi(); let note_pos = state.note_pos(); let key_style = Some(Style::default().fg(Color::Rgb(192, 192, 192)).bg(Color::Rgb(0, 0, 0))); - let off_style = Some(Style::default().fg(TuiTheme::g(160))); - let on_style = Some(Style::default().fg(TuiTheme::g(255)).bg(color.base.rgb).bold()); + let off_style = Some(Style::default().fg(Tui::g(160))); + let on_style = Some(Style::default().fg(Tui::g(255)).bg(color.base.rgb).bold()); Fill::y(Fixed::x(self.keys_width, ThunkRender::new(move|to: &mut TuiOut|{ let [x, y0, _w, _h] = to.area().xywh(); for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) { @@ -270,7 +270,7 @@ impl MidiViewer for PianoHorizontal { fn set_clip (&mut self, clip: Option<&Arc>>) { *self.clip_mut() = clip.cloned(); self.color = clip.map(|p|p.read().unwrap().color) - .unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64)))); + .unwrap_or(ItemPalette::from(ItemColor::from(Tui::g(64)))); self.redraw(); } } diff --git a/sampler/src/sampler.rs b/sampler/src/sampler.rs index 7d4e796c..24c4e53c 100644 --- a/sampler/src/sampler.rs +++ b/sampler/src/sampler.rs @@ -842,12 +842,12 @@ impl Sampler { let note_lo = editor.note_lo().load(Relaxed); let note_pt = editor.note_pos(); let note_hi = editor.note_hi(); - Outer(true, Style::default().fg(TuiTheme::g(96))).enclose(Map::new(move||(note_lo..=note_hi).rev(), move|note, i| { + Outer(true, Style::default().fg(Tui::g(96))).enclose(Map::new(move||(note_lo..=note_hi).rev(), move|note, i| { let offset = |a|Push::y(i as u16, Align::n(Fixed::y(1, Fill::x(a)))); - let mut bg = if note == note_pt { TuiTheme::g(64) } else { Color::Reset }; - let mut fg = TuiTheme::g(160); + let mut bg = if note == note_pt { Tui::g(64) } else { Color::Reset }; + let mut fg = Tui::g(160); if self.mapped[note].is_some() { - fg = TuiTheme::g(224); + fg = Tui::g(224); bg = Color::Rgb(0, if note == note_pt { 96 } else { 64 }, 0); } if let Some((index, _)) = self.recording { @@ -929,7 +929,7 @@ impl Sampler { }) } pub fn status (&self, index: usize) -> impl Content { - Tui::bold(true, Tui::fg(TuiTheme::g(224), self.mapped[index].as_ref().map(|sample|format!( + Tui::bold(true, Tui::fg(Tui::g(224), self.mapped[index].as_ref().map(|sample|format!( "Sample {}-{}", sample.read().unwrap().start, sample.read().unwrap().end, diff --git a/tek/src/lib.rs b/tek/src/lib.rs index de5840ac..341fbef7 100644 --- a/tek/src/lib.rs +++ b/tek/src/lib.rs @@ -115,7 +115,7 @@ impl TekCli { })?) } } -#[derive(Default, Debug)] struct Tek { +#[derive(Default, Debug)] pub struct Tek { /// Must not be dropped for the duration of the process pub jack: Arc>, /// Source of time @@ -185,17 +185,17 @@ view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); { ":tracks" => self.view_row(self.w(), 3, self.track_header(), self.track_cells()).boxed(), ":inputs" => self.view_row(self.w(), 3, self.input_header(), self.input_cells()).boxed(), ":outputs" => self.view_row(self.w(), 3, self.output_header(), self.output_cells()).boxed(), - ":scenes" => Outer(false, Style::default().fg(TuiTheme::g(0))).enclose_bg(self.view_row( + ":scenes" => Outer(false, Style::default().fg(Tui::g(0))).enclose_bg(self.view_row( self.w(), self.size.h().saturating_sub(12) as u16, self.scene_header(), self.clip_columns() )).boxed() }); -provide_bool!(bool: |self: Tek| {}); -provide_num!(isize: |self: Tek| {}); provide!(Color: |self: Tek| {}); provide!(Selection: |self: Tek| {}); provide!(Arc>: |self: Tek| {}); provide!(Option>>: |self: Tek| {}); +provide_bool!(bool: |self: Tek| {}); +provide_num!(isize: |self: Tek| {}); provide_num!(u16: |self: Tek| { ":sidebar-w" => self.sidebar_w(), ":sample-h" => if self.is_editing() { 0 } else { 5 }, @@ -365,7 +365,7 @@ impl Tek { Ok(()) } fn view_clock (&self) -> impl Content + use<'_> { - Outer(false, Style::default().fg(TuiTheme::g(0))).enclose(row!( + Outer(false, Style::default().fg(Tui::g(0))).enclose(row!( self.view_engine_stats(), " ", self.view_play_pause(), " ", self.view_beat_stats(), @@ -381,12 +381,12 @@ impl Tek { .unwrap_or("-.---s".into()); let bpm = ||format!("{:.3}", clock.timebase.bpm.get()); Either::new(compact, - row!(Field(TuiTheme::g(128).into(), "BPM", bpm()), - Field(TuiTheme::g(128).into(), "Beat", beat()), - Field(TuiTheme::g(128).into(), "Time", time())), - row!(FieldV(TuiTheme::g(128).into(), "BPM", bpm()), - FieldV(TuiTheme::g(128).into(), "Beat", beat()), - FieldV(TuiTheme::g(128).into(), "Time", time()))) + row!(Field(Tui::g(128).into(), "BPM", bpm()), + Field(Tui::g(128).into(), "Beat", beat()), + Field(Tui::g(128).into(), "Time", time())), + row!(FieldV(Tui::g(128).into(), "BPM", bpm()), + FieldV(Tui::g(128).into(), "Beat", beat()), + FieldV(Tui::g(128).into(), "Time", time()))) } fn view_engine_stats (&self) -> impl Content + use<'_> { let compact = self.size.w() > 80; @@ -397,16 +397,16 @@ impl Tek { let buf = move||format!("{chunk}"); let latency = move||format!("{:.1}ms", chunk as f64 / rate * 1000.); Either::new(compact, - row!(Field(TuiTheme::g(128).into(), "SR", sr()), - Field(TuiTheme::g(128).into(), "Buf", buf()), - Field(TuiTheme::g(128).into(), "Lat", latency())), - row!(FieldV(TuiTheme::g(128).into(), "SR", sr()), - FieldV(TuiTheme::g(128).into(), "Buf", buf()), - FieldV(TuiTheme::g(128).into(), "Lat", latency()))) + row!(Field(Tui::g(128).into(), "SR", sr()), + Field(Tui::g(128).into(), "Buf", buf()), + Field(Tui::g(128).into(), "Lat", latency())), + row!(FieldV(Tui::g(128).into(), "SR", sr()), + FieldV(Tui::g(128).into(), "Buf", buf()), + FieldV(Tui::g(128).into(), "Lat", latency()))) } fn view_meter <'a> (&'a self, label: &'a str, value: f32) -> impl Content + 'a { col!( - Field(TuiTheme::g(128).into(), label, format!("{:>+9.3}", value)), + Field(Tui::g(128).into(), label, format!("{:>+9.3}", value)), Fixed::xy(if value >= 0.0 { 13 } else if value >= -1.0 { 12 } else if value >= -2.0 { 11 } @@ -493,41 +493,43 @@ impl Tek { let scenes = move||self.scenes_sizes(editing, 2, 15); let selected_track = self.selected().track(); let selected_scene = self.selected().scene(); - let border = |x|Outer(false, Style::default().fg(TuiTheme::g(0))).enclose(x); - (move||Align::c(Map::new(tracks, move|(_, track, x1, x2), t| { - let w = (x2 - x1) as u16; - let color: ItemPalette = track.color.dark.into(); - let last_color = Arc::new(RwLock::new(ItemPalette::from(Color::Rgb(0, 0, 0)))); - map_east(x1 as u16, w, border(Map::new(scenes, move|(_, scene, y1, y2), s| { - let last_color = last_color.clone(); - let h = (1 + y2 - y1) as u16; - let color = scene.color; - let (name, fg, bg) = if let Some(c) = &scene.clips[t] { - let c = c.read().unwrap(); - (c.name.to_string(), c.color.lightest.rgb, c.color) - } else { - ("⏹ ".to_string(), TuiTheme::g(64), TuiTheme::g(32).into()) - }; - let same_track = selected_track == Some(t+1); - let selected = same_track && Some(s+1) == selected_scene; - let neighbor = same_track && Some(s) == selected_scene; - let active = editing && selected; - let label = move||Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string()))); - let mid = if active { bg.light } else { bg.base }; - let top = if neighbor { None } else { Some(last_color.read().unwrap().base.rgb) }; - let mid = mid.rgb; - let low = Color::Rgb(0, 0, 0); - *last_color.write().unwrap() = bg; - map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new(active, - Thunk::new(||Bsp::a( - Fill::xy(Align::nw(button(" Tab ".into(), "".into()))), - &self.editor)), - Thunk::new(move||Bsp::a( - When::new(selected, Fill::y(Align::n(button(" Tab ".into(), "edit".into())))), - phat_sel_3(selected, label(), label(), top, mid, low) - )), - )))) - }))).boxed() + let border = |x|Outer(false, Style::default().fg(Tui::g(0))).enclose(x); + (move||Align::c(Map::new(tracks, { + move|(_, track, x1, x2), t| { + let last_color = Arc::new(RwLock::new(ItemPalette::default())); + let w = (x2 - x1) as u16; + let color: ItemPalette = track.color; + map_east(x1 as u16, w, border(Map::new(scenes, move|(_, scene, y1, y2), s| { + let last_color = last_color.clone(); + let h = (1 + y2 - y1) as u16; + let color = scene.color; + let (name, fg, bg) = if let Some(c) = &scene.clips[t] { + let c = c.read().unwrap(); + (c.name.to_string(), c.color.lightest.rgb, c.color) + } else { + ("⏹ ".to_string(), Tui::g(64), Tui::g(32).into()) + }; + let same_track = selected_track == Some(t+1); + let selected = same_track && Some(s+1) == selected_scene; + let neighbor = same_track && Some(s) == selected_scene; + let active = editing && selected; + let label = move||Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string()))); + let mid = if active { bg.light } else { bg.base }; + let top = if neighbor { None } else { Some(last_color.read().unwrap().base.rgb) }; + let mid = mid.rgb; + let low = Color::Rgb(0, 0, 0); + *last_color.write().unwrap() = bg; + map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new(active, + Thunk::new(||Bsp::a( + Fill::xy(Align::nw(button(" Tab ".into(), "".into()))), + &self.editor)), + Thunk::new(move||Bsp::a( + When::new(selected, Fill::y(Align::n(button(" Tab ".into(), "edit".into())))), + phat_sel_3(selected, label(), label(), top, mid, low) + )), + )))) + }))).boxed() + } })).boxed()).into() } fn activate (&mut self) -> Usually<()> { @@ -556,8 +558,8 @@ impl Tek { Ok(()) } fn input_header <'a> (&'a self) -> ThunkBox<'a, TuiOut> { - let fg = TuiTheme::g(224); - let bg = TuiTheme::g(64); + let fg = Tui::g(224); + let bg = Tui::g(64); (move||Bsp::s(Fill::x(Align::w(self.button(" I ".to_string(), format!(" midi ins ({})", self.midi_ins().len())))), self.midi_ins().get(0).map(|inp|Bsp::s( Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(inp.name.clone())))), inp.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false, @@ -565,8 +567,8 @@ impl Tek { ))).boxed()).into() } fn output_header <'a> (&'a self) -> ThunkBox<'a, TuiOut> { - let fg = TuiTheme::g(224); - let bg = TuiTheme::g(64); + let fg = Tui::g(224); + let bg = Tui::g(64); (move||Bsp::s(Fill::x(Align::w(self.button(" O ".to_string(), format!(" midi outs ({}) ", self.midi_outs().len())))), self.midi_outs().get(0).map(|out|Bsp::s( Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(out.name.clone())))), out.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false, @@ -580,7 +582,7 @@ impl Tek { let add_track = ||self.button(" C-t ".to_string(), format!(" add track ({}/{})", self.selected.track().unwrap_or(0), self.tracks().len())); - (move||Tui::bg(TuiTheme::g(32), Bsp::s( + (move||Tui::bg(Tui::g(32), Bsp::s( Fill::x(Align::w(add_scene())), Fill::x(Align::w(add_track())), )).boxed()).into() @@ -588,8 +590,8 @@ impl Tek { fn button (&self, key: String, label: String) -> impl Content { let compact = !self.is_editing(); Tui::bold(true, Bsp::e( - Margin::x(1, Tui::fg_bg(TuiTheme::g(0), TuiTheme::orange(), key)), - When::new(compact, Margin::x(1, Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(96), label))), + Margin::x(1, Tui::fg_bg(Tui::g(0), Tui::orange(), key)), + When::new(compact, Margin::x(1, Tui::fg_bg(Tui::g(255), Tui::g(96), label))), )) } } @@ -830,21 +832,21 @@ trait HasSelection { fn selected (&self) -> &Selection; fn selected_mut (&mut self) -> &mut Selection; } -#[derive(Debug, Default)] struct Track { +#[derive(Debug, Default)] pub struct Track { /// Name of track - name: Arc, + pub name: Arc, /// Preferred width of track column - width: usize, + pub width: usize, /// Identifying color of track - color: ItemPalette, + pub color: ItemPalette, /// MIDI player state - player: MidiPlayer, + pub player: MidiPlayer, /// Device chain - devices: Vec>, + pub devices: Vec>, /// Inputs of 1st device - audio_ins: Vec>, + pub audio_ins: Vec>, /// Outputs of last device - audio_outs: Vec>, + pub audio_outs: Vec>, } has_clock!(|self: Track|self.player.clock); has_player!(|self: Track|self.player); @@ -949,7 +951,7 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync { fn input_cells <'a> (&'a self) -> ThunkBox<'a, TuiOut> { (move||Align::x(Map::new(||self.tracks_sizes(self.is_editing(), self.editor_w()), move|(_, track, x1, x2), i| { let w = (x2 - x1) as u16; - let color: ItemPalette = track.color.dark.into(); + let color: ItemPalette = track.color; map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, Bsp::n( Self::rec_mon(color.base.rgb, false, false), phat_hi(color.base.rgb, color.dark.rgb) @@ -968,7 +970,7 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync { fn output_cells <'a> (&'a self) -> ThunkBox<'a, TuiOut> { (move||Align::x(Map::new(||self.tracks_sizes(self.is_editing(), self.editor_w()), move|(_, track, x1, x2), i| { let w = (x2 - x1) as u16; - let color: ItemPalette = track.color.dark.into(); + let color: ItemPalette = track.color; map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, Bsp::n( Self::mute_solo(color.base.rgb, false, false), phat_hi(color.dark.rgb, color.darker.rgb) @@ -989,13 +991,13 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync { trait Device: Send + Sync + std::fmt::Debug {} impl Device for Sampler {} impl Device for Plugin {} -#[derive(Debug, Default)] struct Scene { +#[derive(Debug, Default)] pub struct Scene { /// Name of scene - name: Arc, + pub name: Arc, /// Clips in scene, one per track - clips: Vec>>>, + pub clips: Vec>>>, /// Identifying color of scene - color: ItemPalette, + pub color: ItemPalette, } impl Scene { /// Returns the pulse length of the longest clip in the scene @@ -1126,7 +1128,7 @@ trait HasScenes: HasSelection + HasEditor + Send + Sync { ); *last_color.write().unwrap() = color; map_south(y1 as u16, h, Push::y(1, Fixed::y(h, - Outer(false, Style::default().fg(TuiTheme::g(0))).enclose(cell)))) + Outer(false, Style::default().fg(Tui::g(0))).enclose(cell)))) }).boxed() }).into() } @@ -1237,8 +1239,8 @@ audio!(|self: Tek, client, scope|{ }); fn button (key: String, label: String) -> impl Content { Tui::bold(true, Bsp::e( - Margin::x(1, Tui::fg_bg(TuiTheme::g(0), TuiTheme::orange(), key)), - Margin::x(1, Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(96), label)), + Margin::x(1, Tui::fg_bg(Tui::g(0), Tui::orange(), key)), + Margin::x(1, Tui::fg_bg(Tui::g(255), Tui::g(96), label)), )) } #[cfg(test)] fn test_tek () { diff --git a/tui/src/tui_color.rs b/tui/src/tui_color.rs index fadf612f..88017d3c 100644 --- a/tui/src/tui_color.rs +++ b/tui/src/tui_color.rs @@ -1,5 +1,30 @@ use crate::*; use rand::{thread_rng, distributions::uniform::UniformSampler}; +impl Theme for Tui {} +pub trait Theme { + const HOTKEY_FG: Color = Color::Rgb(255, 255, 0); + fn null () -> Color { Color::Reset } + fn g (g: u8) -> Color { Color::Rgb(g, g, g) } + //fn bg0 () -> Color { Color::Rgb(20, 20, 20) } + //fn bg () -> Color { Color::Rgb(28, 35, 25) } + //fn border_bg () -> Color { Color::Rgb(40, 50, 30) } + //fn border_fg (f: bool) -> Color { if f { Self::bo1() } else { Self::bo2() } } + //fn title_fg (f: bool) -> Color { if f { Self::ti1() } else { Self::ti2() } } + //fn separator_fg (_: bool) -> Color { Color::Rgb(0, 0, 0) } + //fn mode_bg () -> Color { Color::Rgb(150, 160, 90) } + //fn mode_fg () -> Color { Color::Rgb(255, 255, 255) } + //fn status_bar_bg () -> Color { Color::Rgb(28, 35, 25) } + //fn bo1 () -> Color { Color::Rgb(100, 110, 40) } + //fn bo2 () -> Color { Color::Rgb(70, 80, 50) } + //fn ti1 () -> Color { Color::Rgb(150, 160, 90) } + //fn ti2 () -> Color { Color::Rgb(120, 130, 100) } + fn red () -> Color { Color::Rgb(255,0, 0) } + fn orange () -> Color { Color::Rgb(255,128,0) } + fn yellow () -> Color { Color::Rgb(255,255,0) } + fn brown () -> Color { Color::Rgb(128,255,0) } + fn green () -> Color { Color::Rgb(0, 255,0) } + fn electric () -> Color { Color::Rgb(0, 255,128) } +} pub trait HasColor { fn color (&self) -> ItemColor; } #[macro_export] macro_rules! has_color { (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { @@ -13,16 +38,6 @@ pub trait HasColor { fn color (&self) -> ItemColor; } pub okhsl: Okhsl, pub rgb: Color, } -/// A color in OKHSL and RGB with lighter and darker variants. -#[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemPalette { - pub base: ItemColor, - pub light: ItemColor, - pub lighter: ItemColor, - pub lightest: ItemColor, - pub dark: ItemColor, - pub darker: ItemColor, - pub darkest: ItemColor, -} from!(|okhsl: Okhsl|ItemColor = Self { okhsl, rgb: okhsl_to_rgb(okhsl) }); from!(|rgb: Color|ItemColor = Self { rgb, okhsl: rgb_to_okhsl(rgb) }); // A single color within item theme parameters, in OKHSL and RGB representations. @@ -47,6 +62,37 @@ impl ItemColor { self.okhsl.mix(other.okhsl, distance).into() } } +/// A color in OKHSL and RGB with lighter and darker variants. +#[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemPalette { + pub base: ItemColor, + pub light: ItemColor, + pub lighter: ItemColor, + pub lightest: ItemColor, + pub dark: ItemColor, + pub darker: ItemColor, + pub darkest: ItemColor, +} +impl ItemPalette { + pub fn random () -> Self { ItemColor::random().into() } + pub fn random_near (color: Self, distance: f32) -> Self { + color.base.mix(ItemColor::random(), distance).into() + } + pub const G00: Self = { + let color: ItemColor = ItemColor { + okhsl: Okhsl { hue: OklabHue::new(0.0), lightness: 0.0, saturation: 0.0 }, + rgb: Color::Rgb(0, 0, 0) + }; + Self { + base: color, + light: color, + lighter: color, + lightest: color, + dark: color, + darker: color, + darkest: color, + } + }; +} from!(|base: Color|ItemPalette = Self::from(ItemColor::from(base))); from!(|base: ItemColor|ItemPalette = { let mut light = base.okhsl; @@ -74,12 +120,6 @@ from!(|base: ItemColor|ItemPalette = { darkest: darkest.into(), } }); -impl ItemPalette { - pub fn random () -> Self { ItemColor::random().into() } - pub fn random_near (color: Self, distance: f32) -> Self { - color.base.mix(ItemColor::random(), distance).into() - } -} pub fn okhsl_to_rgb (color: Okhsl) -> Color { let Srgb { red, green, blue, .. }: Srgb = Srgb::from_color_unclamped(color); Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,) diff --git a/tui/src/tui_content.rs b/tui/src/tui_content.rs index eaca6bf4..72869c3d 100644 --- a/tui/src/tui_content.rs +++ b/tui/src/tui_content.rs @@ -64,66 +64,6 @@ impl Content for FieldV } } -#[derive(Copy,Clone)] -pub struct TuiTheme; - -impl Theme for TuiTheme {} - -pub trait Theme { - const HOTKEY_FG: Color = Color::Rgb(255, 255, 0); - fn null () -> Color { - Color::Reset - } - fn bg0 () -> Color { - Color::Rgb(20, 20, 20) - } - fn bg () -> Color { - Color::Rgb(28, 35, 25) - } - fn border_bg () -> Color { - Color::Rgb(40, 50, 30) - } - fn border_fg (focused: bool) -> Color { - if focused { Self::bo1() } else { Self::bo2() } - } - fn title_fg (focused: bool) -> Color { - if focused { Self::ti1() } else { Self::ti2() } - } - fn separator_fg (_: bool) -> Color { - Color::Rgb(0, 0, 0) - } - fn mode_bg () -> Color { - Color::Rgb(150, 160, 90) - } - fn mode_fg () -> Color { - Color::Rgb(255, 255, 255) - } - fn status_bar_bg () -> Color { - Color::Rgb(28, 35, 25) - } - fn bo1 () -> Color { - Color::Rgb(100, 110, 40) - } - fn bo2 () -> Color { - Color::Rgb(70, 80, 50) - } - fn ti1 () -> Color { - Color::Rgb(150, 160, 90) - } - fn ti2 () -> Color { - Color::Rgb(120, 130, 100) - } - fn orange () -> Color { - Color::Rgb(255,128,0) - } - fn yellow () -> Color { - Color::Rgb(255,255,0) - } - fn g (g: u8) -> Color { - Color::Rgb(g, g, g) - } -} - pub struct Repeat<'a>(pub &'a str); impl Content for Repeat<'_> {