mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
impl TuiTheme on Tui; need to to reduce number of ItemPalette invocations
This commit is contained in:
parent
9d250daa04
commit
cfa3cad5cb
9 changed files with 157 additions and 177 deletions
10
Justfile
10
Justfile
|
|
@ -1,15 +1,13 @@
|
||||||
default:
|
default:
|
||||||
bacon -sj test
|
bacon -sj test
|
||||||
|
|
||||||
tui:
|
tui:
|
||||||
reset
|
|
||||||
cargo run --example tui
|
cargo run --example tui
|
||||||
|
|
||||||
test:
|
|
||||||
cargo test --workspace --exclude jack
|
|
||||||
cloc:
|
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
|
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:
|
status:
|
||||||
cargo c
|
cargo c
|
||||||
cloc --by-file src/
|
cloc --by-file src/
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ impl MidiEditor {
|
||||||
let (color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
|
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)
|
(clip.color, clip.name.clone(), clip.length, clip.looped)
|
||||||
} else {
|
} else {
|
||||||
(ItemPalette::from(TuiTheme::g(64)), String::new().into(), 0, false)
|
(ItemPalette::from(Tui::g(64)), String::new().into(), 0, false)
|
||||||
};
|
};
|
||||||
row!(
|
row!(
|
||||||
FieldV(color, "Edit", format!("{name} ({length})")),
|
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()) {
|
let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
|
||||||
(clip.color, clip.length)
|
(clip.color, clip.length)
|
||||||
} else {
|
} else {
|
||||||
(ItemPalette::from(TuiTheme::g(64)), 0)
|
(ItemPalette::from(Tui::g(64)), 0)
|
||||||
};
|
};
|
||||||
let time_pos = self.time_pos();
|
let time_pos = self.time_pos();
|
||||||
let time_zoom = self.time_zoom().get();
|
let time_zoom = self.time_zoom().get();
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ pub trait HasPlayClip: HasClock {
|
||||||
let MidiClip { ref name, color, .. } = *clip.read().unwrap();
|
let MidiClip { ref name, color, .. } = *clip.read().unwrap();
|
||||||
(name.clone(), color)
|
(name.clone(), color)
|
||||||
} else {
|
} else {
|
||||||
("".into(), TuiTheme::g(64).into())
|
("".into(), Tui::g(64).into())
|
||||||
};
|
};
|
||||||
let time: String = self.pulses_since_start_looped()
|
let time: String = self.pulses_since_start_looped()
|
||||||
.map(|(times, time)|format!("{:>3}x {:>}", times+1.0, self.clock().timebase.format_beats_1(time)))
|
.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> {
|
fn next_status (&self) -> impl Content<TuiOut> {
|
||||||
let mut time: Arc<str> = String::from("--.-.--").into();
|
let mut time: Arc<str> = String::from("--.-.--").into();
|
||||||
let mut name: 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();
|
let clock = self.clock();
|
||||||
if let Some((t, Some(clip))) = self.next_clip() {
|
if let Some((t, Some(clip))) = self.next_clip() {
|
||||||
let clip = clip.read().unwrap();
|
let clip = clip.read().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ pub struct PoolView<'a>(pub bool, pub &'a MidiPool);
|
||||||
content!(TuiOut: |self: PoolView<'a>| {
|
content!(TuiOut: |self: PoolView<'a>| {
|
||||||
let Self(compact, model) = self;
|
let Self(compact, model) = self;
|
||||||
let MidiPool { clips, .. } = self.1;
|
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 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 border = |x|x;//Outer(Style::default().fg(color.dark.rgb).bg(color.darkest.rgb)).enclose(x);
|
||||||
let iter = | |model.clips().clone().into_iter();
|
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!(
|
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::w(Tui::fg(fg, Tui::bold(selected, name)))),
|
||||||
Fill::x(Align::e(Tui::fg(fg, Tui::bold(selected, length)))),
|
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::w(When::new(selected, Tui::bold(true, Tui::fg(Tui::g(255), "▶"))))),
|
||||||
Fill::x(Align::e(When::new(selected, Tui::bold(true, Tui::fg(TuiTheme::g(255), "◀"))))),
|
Fill::x(Align::e(When::new(selected, Tui::bold(true, Tui::fg(Tui::g(255), "◀"))))),
|
||||||
))))
|
))))
|
||||||
})))))
|
})))))
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ impl PianoHorizontal {
|
||||||
clip: clip.cloned(),
|
clip: clip.cloned(),
|
||||||
color: clip.as_ref()
|
color: clip.as_ref()
|
||||||
.map(|p|p.read().unwrap().color)
|
.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.redraw();
|
||||||
piano
|
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)> {
|
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))
|
(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(
|
Bsp::e(
|
||||||
Fixed::x(5, format!("{}x{}", self.size.w(), self.size.h())),
|
Fixed::x(5, format!("{}x{}", self.size.w(), self.size.h())),
|
||||||
self.timeline()
|
self.timeline()
|
||||||
),
|
),
|
||||||
Bsp::e(
|
Bsp::e(
|
||||||
self.keys(),
|
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.notes()),
|
||||||
Fill::xy(self.cursor()),
|
Fill::xy(self.cursor()),
|
||||||
)))
|
)))
|
||||||
|
|
@ -185,8 +185,8 @@ impl PianoHorizontal {
|
||||||
let note_hi = state.note_hi();
|
let note_hi = state.note_hi();
|
||||||
let note_pos = state.note_pos();
|
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 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 off_style = Some(Style::default().fg(Tui::g(160)));
|
||||||
let on_style = Some(Style::default().fg(TuiTheme::g(255)).bg(color.base.rgb).bold());
|
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|{
|
Fill::y(Fixed::x(self.keys_width, ThunkRender::new(move|to: &mut TuiOut|{
|
||||||
let [x, y0, _w, _h] = to.area().xywh();
|
let [x, y0, _w, _h] = to.area().xywh();
|
||||||
for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
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>>>) {
|
fn set_clip (&mut self, clip: Option<&Arc<RwLock<MidiClip>>>) {
|
||||||
*self.clip_mut() = clip.cloned();
|
*self.clip_mut() = clip.cloned();
|
||||||
self.color = clip.map(|p|p.read().unwrap().color)
|
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();
|
self.redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -842,12 +842,12 @@ impl Sampler {
|
||||||
let note_lo = editor.note_lo().load(Relaxed);
|
let note_lo = editor.note_lo().load(Relaxed);
|
||||||
let note_pt = editor.note_pos();
|
let note_pt = editor.note_pos();
|
||||||
let note_hi = editor.note_hi();
|
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 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 bg = if note == note_pt { Tui::g(64) } else { Color::Reset };
|
||||||
let mut fg = TuiTheme::g(160);
|
let mut fg = Tui::g(160);
|
||||||
if self.mapped[note].is_some() {
|
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);
|
bg = Color::Rgb(0, if note == note_pt { 96 } else { 64 }, 0);
|
||||||
}
|
}
|
||||||
if let Some((index, _)) = self.recording {
|
if let Some((index, _)) = self.recording {
|
||||||
|
|
@ -929,7 +929,7 @@ impl Sampler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn status (&self, index: usize) -> impl Content<TuiOut> {
|
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 {}-{}",
|
||||||
sample.read().unwrap().start,
|
sample.read().unwrap().start,
|
||||||
sample.read().unwrap().end,
|
sample.read().unwrap().end,
|
||||||
|
|
|
||||||
|
|
@ -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
|
/// Must not be dropped for the duration of the process
|
||||||
pub jack: Arc<RwLock<JackConnection>>,
|
pub jack: Arc<RwLock<JackConnection>>,
|
||||||
/// Source of time
|
/// 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(),
|
":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(),
|
":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(),
|
":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.w(), self.size.h().saturating_sub(12) as u16,
|
||||||
self.scene_header(), self.clip_columns()
|
self.scene_header(), self.clip_columns()
|
||||||
)).boxed()
|
)).boxed()
|
||||||
});
|
});
|
||||||
provide_bool!(bool: |self: Tek| {});
|
|
||||||
provide_num!(isize: |self: Tek| {});
|
|
||||||
provide!(Color: |self: Tek| {});
|
provide!(Color: |self: Tek| {});
|
||||||
provide!(Selection: |self: Tek| {});
|
provide!(Selection: |self: Tek| {});
|
||||||
provide!(Arc<RwLock<MidiClip>>: |self: Tek| {});
|
provide!(Arc<RwLock<MidiClip>>: |self: Tek| {});
|
||||||
provide!(Option<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| {
|
provide_num!(u16: |self: Tek| {
|
||||||
":sidebar-w" => self.sidebar_w(),
|
":sidebar-w" => self.sidebar_w(),
|
||||||
":sample-h" => if self.is_editing() { 0 } else { 5 },
|
":sample-h" => if self.is_editing() { 0 } else { 5 },
|
||||||
|
|
@ -365,7 +365,7 @@ impl Tek {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn view_clock (&self) -> impl Content<TuiOut> + use<'_> {
|
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_engine_stats(), " ",
|
||||||
self.view_play_pause(), " ",
|
self.view_play_pause(), " ",
|
||||||
self.view_beat_stats(),
|
self.view_beat_stats(),
|
||||||
|
|
@ -381,12 +381,12 @@ impl Tek {
|
||||||
.unwrap_or("-.---s".into());
|
.unwrap_or("-.---s".into());
|
||||||
let bpm = ||format!("{:.3}", clock.timebase.bpm.get());
|
let bpm = ||format!("{:.3}", clock.timebase.bpm.get());
|
||||||
Either::new(compact,
|
Either::new(compact,
|
||||||
row!(Field(TuiTheme::g(128).into(), "BPM", bpm()),
|
row!(Field(Tui::g(128).into(), "BPM", bpm()),
|
||||||
Field(TuiTheme::g(128).into(), "Beat", beat()),
|
Field(Tui::g(128).into(), "Beat", beat()),
|
||||||
Field(TuiTheme::g(128).into(), "Time", time())),
|
Field(Tui::g(128).into(), "Time", time())),
|
||||||
row!(FieldV(TuiTheme::g(128).into(), "BPM", bpm()),
|
row!(FieldV(Tui::g(128).into(), "BPM", bpm()),
|
||||||
FieldV(TuiTheme::g(128).into(), "Beat", beat()),
|
FieldV(Tui::g(128).into(), "Beat", beat()),
|
||||||
FieldV(TuiTheme::g(128).into(), "Time", time())))
|
FieldV(Tui::g(128).into(), "Time", time())))
|
||||||
}
|
}
|
||||||
fn view_engine_stats (&self) -> impl Content<TuiOut> + use<'_> {
|
fn view_engine_stats (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let compact = self.size.w() > 80;
|
let compact = self.size.w() > 80;
|
||||||
|
|
@ -397,16 +397,16 @@ impl Tek {
|
||||||
let buf = move||format!("{chunk}");
|
let buf = move||format!("{chunk}");
|
||||||
let latency = move||format!("{:.1}ms", chunk as f64 / rate * 1000.);
|
let latency = move||format!("{:.1}ms", chunk as f64 / rate * 1000.);
|
||||||
Either::new(compact,
|
Either::new(compact,
|
||||||
row!(Field(TuiTheme::g(128).into(), "SR", sr()),
|
row!(Field(Tui::g(128).into(), "SR", sr()),
|
||||||
Field(TuiTheme::g(128).into(), "Buf", buf()),
|
Field(Tui::g(128).into(), "Buf", buf()),
|
||||||
Field(TuiTheme::g(128).into(), "Lat", latency())),
|
Field(Tui::g(128).into(), "Lat", latency())),
|
||||||
row!(FieldV(TuiTheme::g(128).into(), "SR", sr()),
|
row!(FieldV(Tui::g(128).into(), "SR", sr()),
|
||||||
FieldV(TuiTheme::g(128).into(), "Buf", buf()),
|
FieldV(Tui::g(128).into(), "Buf", buf()),
|
||||||
FieldV(TuiTheme::g(128).into(), "Lat", latency())))
|
FieldV(Tui::g(128).into(), "Lat", latency())))
|
||||||
}
|
}
|
||||||
fn view_meter <'a> (&'a self, label: &'a str, value: f32) -> impl Content<TuiOut> + 'a {
|
fn view_meter <'a> (&'a self, label: &'a str, value: f32) -> impl Content<TuiOut> + 'a {
|
||||||
col!(
|
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 }
|
Fixed::xy(if value >= 0.0 { 13 }
|
||||||
else if value >= -1.0 { 12 }
|
else if value >= -1.0 { 12 }
|
||||||
else if value >= -2.0 { 11 }
|
else if value >= -2.0 { 11 }
|
||||||
|
|
@ -493,11 +493,12 @@ impl Tek {
|
||||||
let scenes = move||self.scenes_sizes(editing, 2, 15);
|
let scenes = move||self.scenes_sizes(editing, 2, 15);
|
||||||
let selected_track = self.selected().track();
|
let selected_track = self.selected().track();
|
||||||
let selected_scene = self.selected().scene();
|
let selected_scene = self.selected().scene();
|
||||||
let border = |x|Outer(false, Style::default().fg(TuiTheme::g(0))).enclose(x);
|
let border = |x|Outer(false, Style::default().fg(Tui::g(0))).enclose(x);
|
||||||
(move||Align::c(Map::new(tracks, move|(_, track, x1, x2), t| {
|
(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 w = (x2 - x1) as u16;
|
||||||
let color: ItemPalette = track.color.dark.into();
|
let color: ItemPalette = track.color;
|
||||||
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| {
|
map_east(x1 as u16, w, border(Map::new(scenes, move|(_, scene, y1, y2), s| {
|
||||||
let last_color = last_color.clone();
|
let last_color = last_color.clone();
|
||||||
let h = (1 + y2 - y1) as u16;
|
let h = (1 + y2 - y1) as u16;
|
||||||
|
|
@ -506,7 +507,7 @@ impl Tek {
|
||||||
let c = c.read().unwrap();
|
let c = c.read().unwrap();
|
||||||
(c.name.to_string(), c.color.lightest.rgb, c.color)
|
(c.name.to_string(), c.color.lightest.rgb, c.color)
|
||||||
} else {
|
} else {
|
||||||
("⏹ ".to_string(), TuiTheme::g(64), TuiTheme::g(32).into())
|
("⏹ ".to_string(), Tui::g(64), Tui::g(32).into())
|
||||||
};
|
};
|
||||||
let same_track = selected_track == Some(t+1);
|
let same_track = selected_track == Some(t+1);
|
||||||
let selected = same_track && Some(s+1) == selected_scene;
|
let selected = same_track && Some(s+1) == selected_scene;
|
||||||
|
|
@ -528,6 +529,7 @@ impl Tek {
|
||||||
)),
|
)),
|
||||||
))))
|
))))
|
||||||
}))).boxed()
|
}))).boxed()
|
||||||
|
}
|
||||||
})).boxed()).into()
|
})).boxed()).into()
|
||||||
}
|
}
|
||||||
fn activate (&mut self) -> Usually<()> {
|
fn activate (&mut self) -> Usually<()> {
|
||||||
|
|
@ -556,8 +558,8 @@ impl Tek {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn input_header <'a> (&'a self) -> ThunkBox<'a, TuiOut> {
|
fn input_header <'a> (&'a self) -> ThunkBox<'a, TuiOut> {
|
||||||
let fg = TuiTheme::g(224);
|
let fg = Tui::g(224);
|
||||||
let bg = TuiTheme::g(64);
|
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(
|
(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())))),
|
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,
|
inp.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false,
|
||||||
|
|
@ -565,8 +567,8 @@ impl Tek {
|
||||||
))).boxed()).into()
|
))).boxed()).into()
|
||||||
}
|
}
|
||||||
fn output_header <'a> (&'a self) -> ThunkBox<'a, TuiOut> {
|
fn output_header <'a> (&'a self) -> ThunkBox<'a, TuiOut> {
|
||||||
let fg = TuiTheme::g(224);
|
let fg = Tui::g(224);
|
||||||
let bg = TuiTheme::g(64);
|
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(
|
(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())))),
|
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,
|
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 ({}/{})",
|
let add_track = ||self.button(" C-t ".to_string(), format!(" add track ({}/{})",
|
||||||
self.selected.track().unwrap_or(0),
|
self.selected.track().unwrap_or(0),
|
||||||
self.tracks().len()));
|
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_scene())),
|
||||||
Fill::x(Align::w(add_track())),
|
Fill::x(Align::w(add_track())),
|
||||||
)).boxed()).into()
|
)).boxed()).into()
|
||||||
|
|
@ -588,8 +590,8 @@ impl Tek {
|
||||||
fn button (&self, key: String, label: String) -> impl Content<TuiOut> {
|
fn button (&self, key: String, label: String) -> impl Content<TuiOut> {
|
||||||
let compact = !self.is_editing();
|
let compact = !self.is_editing();
|
||||||
Tui::bold(true, Bsp::e(
|
Tui::bold(true, Bsp::e(
|
||||||
Margin::x(1, Tui::fg_bg(TuiTheme::g(0), TuiTheme::orange(), key)),
|
Margin::x(1, Tui::fg_bg(Tui::g(0), Tui::orange(), key)),
|
||||||
When::new(compact, Margin::x(1, Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(96), label))),
|
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 (&self) -> &Selection;
|
||||||
fn selected_mut (&mut self) -> &mut Selection;
|
fn selected_mut (&mut self) -> &mut Selection;
|
||||||
}
|
}
|
||||||
#[derive(Debug, Default)] struct Track {
|
#[derive(Debug, Default)] pub struct Track {
|
||||||
/// Name of track
|
/// Name of track
|
||||||
name: Arc<str>,
|
pub name: Arc<str>,
|
||||||
/// Preferred width of track column
|
/// Preferred width of track column
|
||||||
width: usize,
|
pub width: usize,
|
||||||
/// Identifying color of track
|
/// Identifying color of track
|
||||||
color: ItemPalette,
|
pub color: ItemPalette,
|
||||||
/// MIDI player state
|
/// MIDI player state
|
||||||
player: MidiPlayer,
|
pub player: MidiPlayer,
|
||||||
/// Device chain
|
/// Device chain
|
||||||
devices: Vec<Box<dyn Device>>,
|
pub devices: Vec<Box<dyn Device>>,
|
||||||
/// Inputs of 1st device
|
/// Inputs of 1st device
|
||||||
audio_ins: Vec<JackPort<AudioIn>>,
|
pub audio_ins: Vec<JackPort<AudioIn>>,
|
||||||
/// Outputs of last device
|
/// Outputs of last device
|
||||||
audio_outs: Vec<JackPort<AudioOut>>,
|
pub audio_outs: Vec<JackPort<AudioOut>>,
|
||||||
}
|
}
|
||||||
has_clock!(|self: Track|self.player.clock);
|
has_clock!(|self: Track|self.player.clock);
|
||||||
has_player!(|self: Track|self.player);
|
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> {
|
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| {
|
(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 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(
|
map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, Bsp::n(
|
||||||
Self::rec_mon(color.base.rgb, false, false),
|
Self::rec_mon(color.base.rgb, false, false),
|
||||||
phat_hi(color.base.rgb, color.dark.rgb)
|
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> {
|
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| {
|
(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 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(
|
map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, Bsp::n(
|
||||||
Self::mute_solo(color.base.rgb, false, false),
|
Self::mute_solo(color.base.rgb, false, false),
|
||||||
phat_hi(color.dark.rgb, color.darker.rgb)
|
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 {}
|
trait Device: Send + Sync + std::fmt::Debug {}
|
||||||
impl Device for Sampler {}
|
impl Device for Sampler {}
|
||||||
impl Device for Plugin {}
|
impl Device for Plugin {}
|
||||||
#[derive(Debug, Default)] struct Scene {
|
#[derive(Debug, Default)] pub struct Scene {
|
||||||
/// Name of scene
|
/// Name of scene
|
||||||
name: Arc<str>,
|
pub name: Arc<str>,
|
||||||
/// Clips in scene, one per track
|
/// Clips in scene, one per track
|
||||||
clips: Vec<Option<Arc<RwLock<MidiClip>>>>,
|
pub clips: Vec<Option<Arc<RwLock<MidiClip>>>>,
|
||||||
/// Identifying color of scene
|
/// Identifying color of scene
|
||||||
color: ItemPalette,
|
pub color: ItemPalette,
|
||||||
}
|
}
|
||||||
impl Scene {
|
impl Scene {
|
||||||
/// Returns the pulse length of the longest clip in the 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;
|
*last_color.write().unwrap() = color;
|
||||||
map_south(y1 as u16, h, Push::y(1, Fixed::y(h,
|
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()
|
}).boxed()
|
||||||
}).into()
|
}).into()
|
||||||
}
|
}
|
||||||
|
|
@ -1237,8 +1239,8 @@ audio!(|self: Tek, client, scope|{
|
||||||
});
|
});
|
||||||
fn button (key: String, label: String) -> impl Content<TuiOut> {
|
fn button (key: String, label: String) -> impl Content<TuiOut> {
|
||||||
Tui::bold(true, Bsp::e(
|
Tui::bold(true, Bsp::e(
|
||||||
Margin::x(1, Tui::fg_bg(TuiTheme::g(0), TuiTheme::orange(), key)),
|
Margin::x(1, Tui::fg_bg(Tui::g(0), Tui::orange(), key)),
|
||||||
Margin::x(1, Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(96), label)),
|
Margin::x(1, Tui::fg_bg(Tui::g(255), Tui::g(96), label)),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
#[cfg(test)] fn test_tek () {
|
#[cfg(test)] fn test_tek () {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,30 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use rand::{thread_rng, distributions::uniform::UniformSampler};
|
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; }
|
pub trait HasColor { fn color (&self) -> ItemColor; }
|
||||||
#[macro_export] macro_rules! has_color {
|
#[macro_export] macro_rules! has_color {
|
||||||
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
(|$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 okhsl: Okhsl<f32>,
|
||||||
pub rgb: Color,
|
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!(|okhsl: Okhsl<f32>|ItemColor = Self { okhsl, rgb: okhsl_to_rgb(okhsl) });
|
||||||
from!(|rgb: Color|ItemColor = Self { rgb, okhsl: rgb_to_okhsl(rgb) });
|
from!(|rgb: Color|ItemColor = Self { rgb, okhsl: rgb_to_okhsl(rgb) });
|
||||||
// A single color within item theme parameters, in OKHSL and RGB representations.
|
// 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()
|
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: Color|ItemPalette = Self::from(ItemColor::from(base)));
|
||||||
from!(|base: ItemColor|ItemPalette = {
|
from!(|base: ItemColor|ItemPalette = {
|
||||||
let mut light = base.okhsl;
|
let mut light = base.okhsl;
|
||||||
|
|
@ -74,12 +120,6 @@ from!(|base: ItemColor|ItemPalette = {
|
||||||
darkest: darkest.into(),
|
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 {
|
pub fn okhsl_to_rgb (color: Okhsl<f32>) -> Color {
|
||||||
let Srgb { red, green, blue, .. }: Srgb<f32> = Srgb::from_color_unclamped(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,)
|
Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
pub struct Repeat<'a>(pub &'a str);
|
||||||
|
|
||||||
impl Content<TuiOut> for Repeat<'_> {
|
impl Content<TuiOut> for Repeat<'_> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue