impl TuiTheme on Tui; need to to reduce number of ItemPalette invocations

This commit is contained in:
🪞👃🪞 2025-01-19 22:09:37 +01:00
parent 9d250daa04
commit cfa3cad5cb
9 changed files with 157 additions and 177 deletions

View file

@ -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/

View file

@ -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();

View file

@ -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<TuiOut> {
let mut time: Arc<str> = String::from("--.-.--").into();
let mut name: Arc<str> = 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();

View file

@ -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), ""))))),
))))
})))))
});

View file

@ -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<Item=(usize, u16, usize)> {
(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<RwLock<MidiClip>>>) {
*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();
}
}

View file

@ -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<TuiOut> {
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,

View file

@ -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<RwLock<JackConnection>>,
/// 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<RwLock<MidiClip>>: |self: Tek| {});
provide!(Option<Arc<RwLock<MidiClip>>>: |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<TuiOut> + 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<TuiOut> + 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<TuiOut> + '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<TuiOut> {
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<str>,
pub name: Arc<str>,
/// 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<Box<dyn Device>>,
pub devices: Vec<Box<dyn Device>>,
/// Inputs of 1st device
audio_ins: Vec<JackPort<AudioIn>>,
pub audio_ins: Vec<JackPort<AudioIn>>,
/// Outputs of last device
audio_outs: Vec<JackPort<AudioOut>>,
pub audio_outs: Vec<JackPort<AudioOut>>,
}
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<str>,
pub name: Arc<str>,
/// Clips in scene, one per track
clips: Vec<Option<Arc<RwLock<MidiClip>>>>,
pub clips: Vec<Option<Arc<RwLock<MidiClip>>>>,
/// 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<TuiOut> {
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 () {

View file

@ -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<f32>,
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<f32>|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<f32>) -> Color {
let Srgb { red, green, blue, .. }: Srgb<f32> = Srgb::from_color_unclamped(color);
Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,)

View file

@ -64,66 +64,6 @@ impl<T, U> Content<TuiOut> for FieldV<T, U>
}
}
#[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<TuiOut> for Repeat<'_> {