why do the borders think they are enabled

This commit is contained in:
🪞👃🪞 2025-01-16 20:26:34 +01:00
parent 1463460c4f
commit 3a6202464c
5 changed files with 135 additions and 167 deletions

View file

@ -32,7 +32,7 @@ content!(TuiOut: |self: SamplerTui| {
let keys = move||"";//SamplerKeys(self);
let fg = self.color.base.rgb;
let bg = self.color.darkest.rgb;
let border = Fill::xy(Outer(Style::default().fg(fg).bg(bg)));
let border = Fill::xy(Outer(true, Style::default().fg(fg).bg(bg)));
let with_border = |x|lay!(border, Fill::xy(x));
let with_size = |x|lay!(self.size.clone(), x);
Tui::bg(bg, Fill::xy(with_border(Bsp::s(
@ -83,7 +83,7 @@ impl Sampler {
let note_lo = editor.note_lo().load(Relaxed);
let note_pt = editor.note_pos();
let note_hi = editor.note_hi();
Outer(Style::default().fg(TuiTheme::g(96))).enclose(Map::new(move||(note_lo..=note_hi).rev(), move|note, i| {
Outer(true, Style::default().fg(TuiTheme::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);

View file

@ -200,23 +200,68 @@ edn_view!(TuiOut: |self: Tek| self.size.of(EdnView::from_source(self, self.edn.a
":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(Style::default().fg(TuiTheme::g(0))).enclose_bg(self.view_row(
":scenes" => Outer(false, Style::default().fg(TuiTheme::g(0))).enclose_bg(self.view_row(
self.w(), self.size.h().saturating_sub(12) as u16,
self.scene_header(), self.clip_columns()
)).boxed() }});
impl Tek {
fn new_clock (
jack: &Arc<RwLock<JackConnection>>,
bpm: Option<f64>, sync_lead: bool, sync_follow: bool,
midi_froms: &[PortConnection], midi_tos: &[PortConnection],
) -> Usually<Self> {
let tek = Self {
edn: include_str!("./view_transport.edn").to_string(),
jack: jack.clone(),
color: ItemPalette::random(),
clock: Clock::new(jack, bpm),
midi_ins: vec![JackPort::<MidiIn>::new(jack, "GlobalI", midi_froms)?],
midi_outs: vec![JackPort::<MidiOut>::new(jack, "GlobalO", midi_tos)?],
..Default::default()
};
tek.sync_lead(sync_lead);
tek.sync_follow(sync_follow);
Ok(tek)
}
fn new_sequencer (
jack: &Arc<RwLock<JackConnection>>,
bpm: Option<f64>, sync_lead: bool, sync_follow: bool,
midi_froms: &[PortConnection], midi_tos: &[PortConnection],
) -> Usually<Self> {
let clip = MidiClip::new("Clip", true, 384usize, None, Some(ItemColor::random().into()));
let clip = Arc::new(RwLock::new(clip));
Ok(Self {
edn: include_str!("./view_sequencer.edn").to_string(),
pool: Some((&clip).into()),
editor: Some((&clip).into()),
editing: false.into(),
midi_buf: vec![vec![];65536],
player: Some(MidiPlayer::new(&jack, "sequencer", Some(&clip), &midi_froms, &midi_tos)?),
..Self::new_clock(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?
})
}
fn new_groovebox (
jack: &Arc<RwLock<JackConnection>>,
bpm: Option<f64>, sync_lead: bool, sync_follow: bool,
midi_froms: &[PortConnection], midi_tos: &[PortConnection],
audio_froms: &[&[PortConnection];2], audio_tos: &[&[PortConnection];2],
) -> Usually<Self> {
let app = Self {
edn: include_str!("./view_groovebox.edn").to_string(),
sampler: Some(Sampler::new(jack, &"sampler", midi_froms, audio_froms, audio_tos)?),
..Self::new_sequencer(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?
};
if let Some(sampler) = app.sampler.as_ref().unwrap().midi_in.as_ref() {
jack.connect_ports(&app.player.as_ref().unwrap().midi_outs[0].port, &sampler.port)?;
}
Ok(app)
}
fn new_arranger (
jack: &Arc<RwLock<JackConnection>>,
bpm: Option<f64>,
sync_lead: bool,
sync_follow: bool,
midi_froms: &[PortConnection],
midi_tos: &[PortConnection],
audio_froms: &[&[PortConnection];2],
audio_tos: &[&[PortConnection];2],
scenes: usize,
tracks: usize,
track_width: usize,
bpm: Option<f64>, sync_lead: bool, sync_follow: bool,
midi_froms: &[PortConnection], midi_tos: &[PortConnection],
audio_froms: &[&[PortConnection];2], audio_tos: &[&[PortConnection];2],
scenes: usize, tracks: usize, track_width: usize,
) -> Usually<Self> {
let mut arranger = Self {
edn: include_str!("./view_arranger.edn").to_string(),
@ -240,6 +285,21 @@ impl Tek {
}
Ok(())
}
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
-> Usually<(usize, &mut Scene)>
{
let scene = Scene {
name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()),
clips: vec![None;self.tracks().len()],
color: color.unwrap_or_else(ItemPalette::random),
};
self.scenes_mut().push(scene);
let index = self.scenes().len() - 1;
Ok((index, &mut self.scenes_mut()[index]))
}
fn scene_default_name (&self) -> Arc<str> {
format!("Sc{:3>}", self.scenes().len() + 1).into()
}
fn tracks_add (
&mut self, count: usize, width: usize,
midi_from: &[PortConnection], midi_to: &[PortConnection],
@ -282,71 +342,6 @@ impl Tek {
}
Ok((index, &mut self.tracks_mut()[index]))
}
fn new_groovebox (
jack: &Arc<RwLock<JackConnection>>,
bpm: Option<f64>,
sync_lead: bool,
sync_follow: bool,
midi_froms: &[PortConnection],
midi_tos: &[PortConnection],
audio_froms: &[&[PortConnection];2],
audio_tos: &[&[PortConnection];2],
) -> Usually<Self> {
let app = Self {
edn: include_str!("./view_groovebox.edn").to_string(),
sampler: Some(Sampler::new(jack, &"sampler", midi_froms, audio_froms, audio_tos)?),
..Self::new_sequencer(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?
};
if let Some(sampler) = app.sampler.as_ref().unwrap().midi_in.as_ref() {
jack.connect_ports(&app.player.as_ref().unwrap().midi_outs[0].port, &sampler.port)?;
}
Ok(app)
}
fn new_sequencer (
jack: &Arc<RwLock<JackConnection>>,
bpm: Option<f64>,
sync_lead: bool,
sync_follow: bool,
midi_froms: &[PortConnection],
midi_tos: &[PortConnection],
) -> Usually<Self> {
let clip = MidiClip::new("Clip", true, 384usize, None, Some(ItemColor::random().into()));
let clip = Arc::new(RwLock::new(clip));
Ok(Self {
edn: include_str!("./view_sequencer.edn").to_string(),
pool: Some((&clip).into()),
editor: Some((&clip).into()),
editing: false.into(),
midi_buf: vec![vec![];65536],
player: Some(MidiPlayer::new(&jack, "sequencer", Some(&clip), &midi_froms, &midi_tos)?),
..Self::new_clock(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?
})
}
fn new_clock (
jack: &Arc<RwLock<JackConnection>>,
bpm: Option<f64>,
sync_lead: bool,
sync_follow: bool,
midi_froms: &[PortConnection],
midi_tos: &[PortConnection],
) -> Usually<Self> {
let tek = Self {
edn: include_str!("./view_transport.edn").to_string(),
jack: jack.clone(),
color: ItemPalette::random(),
clock: Clock::new(jack, bpm),
midi_ins: vec![
JackPort::<MidiIn>::new(jack, "GlobalI", midi_froms)?
],
midi_outs: vec![
JackPort::<MidiOut>::new(jack, "GlobalO", midi_tos)?
],
..Default::default()
};
tek.sync_lead(sync_lead);
tek.sync_follow(sync_follow);
Ok(tek)
}
fn sync_lead (&self, enable: bool) -> Usually<()> {
if enable {
self.jack.read().unwrap().client().register_timebase_callback(false, |mut state|{
@ -363,7 +358,7 @@ impl Tek {
Ok(())
}
fn view_clock (&self) -> impl Content<TuiOut> + use<'_> {
Outer(Style::default().fg(TuiTheme::g(0))).enclose(row!(
Outer(false, Style::default().fg(TuiTheme::g(0))).enclose(row!(
self.view_engine_stats(), " ",
self.view_play_pause(), " ",
self.view_beat_stats(),
@ -485,25 +480,13 @@ impl Tek {
scene.clips.remove(index);
}
}
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
-> Usually<(usize, &mut Scene)>
{
let scene = Scene {
name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()),
clips: vec![None;self.tracks().len()],
color: color.unwrap_or_else(ItemPalette::random),
};
self.scenes_mut().push(scene);
let index = self.scenes().len() - 1;
Ok((index, &mut self.scenes_mut()[index]))
}
fn clip_columns <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
let editing = self.is_editing();
let tracks = move||self.tracks_sizes(editing, self.editor_w());
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(Style::default().fg(TuiTheme::g(0))).enclose(x);
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();
@ -955,7 +938,8 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
let bfg = if active { Color::Rgb(255,255,255) } else { Color::Rgb(0,0,0) };
let border = Style::default().fg(bfg).bg(bg);
Tui::bg(bg, map_east(x1 as u16, (x2 - x1) as u16,
Outer(border).enclose(Tui::fg_bg(fg, bg, Tui::bold(true, Fill::x(Align::x(name)))))
Outer(false, border)
.enclose(Tui::fg_bg(fg, bg, Tui::bold(true, Fill::x(Align::x(name)))))
))
})).boxed()).into()
}
@ -1107,9 +1091,6 @@ trait HasScenes: HasSelection + HasEditor + Send + Sync {
data
})
}
fn scene_default_name (&self) -> Arc<str> {
format!("Sc{:3>}", self.scenes().len() + 1).into()
}
fn scene (&self) -> Option<&Scene> {
self.selected().scene().and_then(|s|self.scenes().get(s))
}
@ -1142,7 +1123,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(Style::default().fg(TuiTheme::g(0))).enclose(cell))))
Outer(false, Style::default().fg(TuiTheme::g(0))).enclose(cell))))
}).boxed()
}).into()
}

View file

@ -1,44 +1,42 @@
use crate::*;
pub struct Bordered<S: BorderStyle, W: Content<TuiOut>>(pub S, pub W);
pub struct Bordered<S: BorderStyle, W: Content<TuiOut>>(pub bool, pub S, pub W);
content!(TuiOut: |self: Bordered<S: BorderStyle, W: Content<TuiOut>>|Fill::xy(
lay!(Border(self.0), Padding::xy(1, 1, &self.1))
lay!(When::new(self.0, Border(self.0, self.1)), Padding::xy(1, 1, &self.2))
));
pub struct Border<S: BorderStyle>(pub S);
pub struct Border<S: BorderStyle>(pub bool, pub S);
render!(TuiOut: |self: Border<S: BorderStyle>, to| {
if self.0 {
let area = to.area();
if area.w() > 0 && area.y() > 0 {
to.blit(&self.0.nw(), area.x(), area.y(), self.0.style());
to.blit(&self.0.ne(), area.x() + area.w() - 1, area.y(), self.0.style());
to.blit(&self.0.sw(), area.x(), area.y() + area.h() - 1, self.0.style());
to.blit(&self.0.se(), area.x() + area.w() - 1, area.y() + area.h() - 1, self.0.style());
to.blit(&self.1.nw(), area.x(), area.y(), self.1.style());
to.blit(&self.1.ne(), area.x() + area.w() - 1, area.y(), self.1.style());
to.blit(&self.1.sw(), area.x(), area.y() + area.h() - 1, self.1.style());
to.blit(&self.1.se(), area.x() + area.w() - 1, area.y() + area.h() - 1, self.1.style());
for x in area.x()+1..area.x()+area.w()-1 {
to.blit(&self.0.n(), x, area.y(), self.0.style());
to.blit(&self.0.s(), x, area.y() + area.h() - 1, self.0.style());
to.blit(&self.1.n(), x, area.y(), self.1.style());
to.blit(&self.1.s(), x, area.y() + area.h() - 1, self.1.style());
}
for y in area.y()+1..area.y()+area.h()-1 {
to.blit(&self.0.w(), area.x(), y, self.0.style());
to.blit(&self.0.e(), area.x() + area.w() - 1, y, self.0.style());
to.blit(&self.1.w(), area.x(), y, self.1.style());
to.blit(&self.1.e(), area.x() + area.w() - 1, y, self.1.style());
}
}
}
});
pub trait BorderStyle: Send + Sync + Copy {
fn wrap <W: Content<TuiOut>> (self, w: W) -> Bordered<Self, W> {
Bordered(self, w)
}
fn enabled (&self) -> bool;
fn enclose <W: Content<TuiOut>> (self, w: W) -> impl Content<TuiOut> {
lay!(Fill::xy(Border(self)), w)
Bsp::b(Fill::xy(Border(self.enabled(), self)), w)
}
fn enclose2 <W: Content<TuiOut>> (self, w: W) -> impl Content<TuiOut> {
Bsp::b(Margin::xy(1, 1, Fill::xy(Border(self))), w)
Bsp::b(Margin::xy(1, 1, Fill::xy(Border(self.enabled(), self))), w)
}
fn enclose_bg <W: Content<TuiOut>> (self, w: W) -> impl Content<TuiOut> {
Tui::bg(self.style().unwrap().bg.unwrap_or(Color::Reset), lay!(
Fill::xy(Border(self)),
w
))
Tui::bg(self.style().unwrap().bg.unwrap_or(Color::Reset),
Bsp::b(Fill::xy(Border(self.enabled(), self)), w))
}
const NW: &'static str = "";
const N: &'static str = "";
@ -65,9 +63,11 @@ pub trait BorderStyle: Send + Sync + Copy {
#[inline] fn draw <'a> (
&self, to: &mut TuiOut
) -> Usually<()> {
if self.enabled() {
self.draw_horizontal(to, None)?;
self.draw_vertical(to, None)?;
self.draw_corners(to, None)?;
}
Ok(())
}
#[inline] fn draw_horizontal (
@ -135,11 +135,13 @@ macro_rules! border {
const S: &'static str = $s;
const SE: &'static str = $se;
$($x)*
fn enabled (&self) -> bool { self.0 }
}
#[derive(Copy, Clone)]
pub struct $T(pub Style);
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
impl Content<TuiOut> for $T {
fn render (&self, to: &mut TuiOut) { let _ = self.draw(to); }
fn render (&self, to: &mut TuiOut) {
if self.enabled() { let _ = self.draw(to); }
}
}
)+}
}
@ -148,57 +150,57 @@ border! {
Square {
"" "" ""
"" ""
"" "" "" fn style (&self) -> Option<Style> { Some(self.0) }
"" "" "" fn style (&self) -> Option<Style> { Some(self.1) }
},
SquareBold {
"" "" ""
"" ""
"" "" "" fn style (&self) -> Option<Style> { Some(self.0) }
"" "" "" fn style (&self) -> Option<Style> { Some(self.1) }
},
TabLike {
"" "" ""
"" ""
"" " " "" fn style (&self) -> Option<Style> { Some(self.0) }
"" " " "" fn style (&self) -> Option<Style> { Some(self.1) }
},
Lozenge {
"" "" ""
"" ""
"" "" "" fn style (&self) -> Option<Style> { Some(self.0) }
"" "" "" fn style (&self) -> Option<Style> { Some(self.1) }
},
Brace {
"" "" ""
"" ""
"" "" "" fn style (&self) -> Option<Style> { Some(self.0) }
"" "" "" fn style (&self) -> Option<Style> { Some(self.1) }
},
LozengeDotted {
"" "" ""
"" ""
"" "" "" fn style (&self) -> Option<Style> { Some(self.0) }
"" "" "" fn style (&self) -> Option<Style> { Some(self.1) }
},
Quarter {
"" "" "🮇"
"" "🮇"
"" "" "🮇" fn style (&self) -> Option<Style> { Some(self.0) }
"" "" "🮇" fn style (&self) -> Option<Style> { Some(self.1) }
},
QuarterV {
"" "" "🮇"
"" "🮇"
"" "" "🮇" fn style (&self) -> Option<Style> { Some(self.0) }
"" "" "🮇" fn style (&self) -> Option<Style> { Some(self.1) }
},
Chamfer {
"🭂" "" "🭍"
"" "🮇"
"🭓" "" "🭞" fn style (&self) -> Option<Style> { Some(self.0) }
"🭓" "" "🭞" fn style (&self) -> Option<Style> { Some(self.1) }
},
Corners {
"🬆" "" "🬊" // 🬴 🬸
"" ""
"🬱" "" "🬵" fn style (&self) -> Option<Style> { Some(self.0) }
"🬱" "" "🬵" fn style (&self) -> Option<Style> { Some(self.1) }
},
CornersTall {
"🭽" "" "🭾"
"" ""
"🭼" "" "🭿" fn style (&self) -> Option<Style> { Some(self.0) }
"🭼" "" "🭿" fn style (&self) -> Option<Style> { Some(self.1) }
},
Outer {
"🭽" "" "🭾"
@ -208,25 +210,25 @@ border! {
const E0: &'static str = "]";
const N0: &'static str = "";
const S0: &'static str = "";
fn style (&self) -> Option<Style> { Some(self.0) }
fn style (&self) -> Option<Style> { Some(self.1) }
},
Phat {
"" "" ""
"" ""
"" "" ""
fn style (&self) -> Option<Style> { Some(self.0) }
fn style (&self) -> Option<Style> { Some(self.1) }
},
Rugged {
"" "" ""
"" ""
"" "🮂" ""
fn style (&self) -> Option<Style> { Some(self.0) }
fn style (&self) -> Option<Style> { Some(self.1) }
},
Skinny {
"" "" ""
"" ""
"" "" ""
fn style (&self) -> Option<Style> { Some(self.0) }
fn style (&self) -> Option<Style> { Some(self.1) }
},
Brackets {
"" "" ""
@ -236,7 +238,7 @@ border! {
const E0: &'static str = "]";
const N0: &'static str = "";
const S0: &'static str = "";
fn style (&self) -> Option<Style> { Some(self.0) }
fn style (&self) -> Option<Style> { Some(self.1) }
},
Reticle {
"" "" ""
@ -246,22 +248,6 @@ border! {
const E0: &'static str = "";
const N0: &'static str = "";
const S0: &'static str = "";
fn style (&self) -> Option<Style> { Some(self.0) }
fn style (&self) -> Option<Style> { Some(self.1) }
}
}
pub const CORNERS: Brackets = Brackets(Style {
fg: Some(Color::Rgb(96, 255, 32)),
bg: None,
underline_color: None,
add_modifier: Modifier::empty(),
sub_modifier: Modifier::DIM
});
pub const RETICLE: Reticle = Reticle(Style {
fg: Some(Color::Rgb(96, 255, 32)),
bg: None,
underline_color: None,
add_modifier: Modifier::empty(),
sub_modifier: Modifier::DIM
});

View file

@ -113,7 +113,8 @@ pub fn phat_sel_3 <T: Content<TuiOut>> (
) -> impl Content<TuiOut> {
let border = Style::default().fg(Color::Rgb(255,255,255)).bg(middle);
Either::new(selected,
Tui::bg(middle, Outer(border).enclose(Align::w(Bsp::s("", Bsp::n("", Fill::y(field_1)))))),
Tui::bg(middle, Outer(true, border)
.enclose(Align::w(Bsp::s("", Bsp::n("", Fill::y(field_1)))))),
Bsp::s(Fixed::y(1, top.map(|top|phat_lo(middle, top))),
Bsp::n(Fixed::y(1, phat_hi(middle, bottom)),
Fill::xy(Tui::bg(middle, field_2)),

View file

@ -10,11 +10,11 @@ pub trait TuiStyle {
fn fg_bg <R: Content<TuiOut>> (fg: Color, bg: Color, w: R) -> Background<Foreground<R>> {
Background(bg, Foreground(fg, w))
}
fn bold <R: Content<TuiOut>> (on: bool, w: R) -> Bold<R> {
Bold(on, w)
fn bold <R: Content<TuiOut>> (enable: bool, w: R) -> Bold<R> {
Bold(enable, w)
}
fn border <R: Content<TuiOut>, S: BorderStyle> (style: S, w: R) -> Bordered<S, R> {
Bordered(style, w)
fn border <R: Content<TuiOut>, S: BorderStyle> (enable: bool, style: S, w: R) -> Bordered<S, R> {
Bordered(enable, style, w)
}
}