mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
wip: provide more components to app
This commit is contained in:
parent
ed462cd0f6
commit
2cd56e7391
3 changed files with 126 additions and 21 deletions
|
|
@ -3,7 +3,7 @@ use crate::*;
|
|||
#[derive(Debug)]
|
||||
pub struct JackPort<T: PortSpec> {
|
||||
/// Port name
|
||||
pub name: String,
|
||||
pub name: Arc<str>,
|
||||
/// Handle to JACK client, for receiving reconnect events.
|
||||
pub jack: Arc<RwLock<JackConnection>>,
|
||||
/// Port handle.
|
||||
|
|
@ -23,7 +23,7 @@ impl<T: PortSpec> JackPort<T> {
|
|||
if port.as_str() == &**name {
|
||||
if let Some(port) = self.jack.port_by_name(port.as_str()) {
|
||||
let port_status = Self::try_both_ways(&self.jack, &port, &self.port);
|
||||
let name = port.name()?;
|
||||
let name = port.name()?.into();
|
||||
status.push((port, name, port_status));
|
||||
if port_status == Connected {
|
||||
break
|
||||
|
|
@ -34,7 +34,7 @@ impl<T: PortSpec> JackPort<T> {
|
|||
RegExp(re) => for port in self.jack.ports(Some(&re), None, PortFlags::empty()).iter() {
|
||||
if let Some(port) = self.jack.port_by_name(port.as_str()) {
|
||||
let port_status = Self::try_both_ways(&self.jack, &port, &self.port);
|
||||
let name = port.name()?;
|
||||
let name = port.name()?.into();
|
||||
status.push((port, name, port_status));
|
||||
if port_status == Connected && connect.scope == One {
|
||||
break
|
||||
|
|
@ -64,7 +64,7 @@ impl<T: PortSpec> JackPort<T> {
|
|||
pub struct PortConnection {
|
||||
pub name: PortConnectionName,
|
||||
pub scope: PortConnectionScope,
|
||||
pub status: Vec<(Port<Unowned>, String, PortConnectionStatus)>,
|
||||
pub status: Vec<(Port<Unowned>, Arc<str>, PortConnectionStatus)>,
|
||||
}
|
||||
impl PortConnection {
|
||||
pub fn collect (exact: &[impl AsRef<str>], re: &[impl AsRef<str>], re_all: &[impl AsRef<str>])
|
||||
|
|
@ -89,14 +89,14 @@ impl PortConnection {
|
|||
let name = PortConnectionName::RegExp(name.as_ref().into());
|
||||
Self { name, scope: PortConnectionScope::All, status: vec![] }
|
||||
}
|
||||
pub fn info (&self) -> String {
|
||||
pub fn info (&self) -> Arc<str> {
|
||||
format!("{} {} {}", match self.scope {
|
||||
PortConnectionScope::One => " ",
|
||||
PortConnectionScope::All => "*",
|
||||
}, match &self.name {
|
||||
PortConnectionName::Exact(name) => format!("= {name}"),
|
||||
PortConnectionName::RegExp(name) => format!("~ {name}"),
|
||||
}, self.status.len())
|
||||
}, self.status.len()).into()
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
|
@ -123,7 +123,7 @@ impl JackPort<MidiIn> {
|
|||
let mut port = JackPort {
|
||||
jack: jack.clone(),
|
||||
port: jack.midi_in(name.as_ref())?,
|
||||
name: name.as_ref().to_string(),
|
||||
name: name.as_ref().into(),
|
||||
connect: connect.to_vec()
|
||||
};
|
||||
port.connect_to_matching()?;
|
||||
|
|
@ -137,7 +137,7 @@ impl JackPort<MidiOut> {
|
|||
let mut port = Self {
|
||||
jack: jack.clone(),
|
||||
port: jack.midi_out(name.as_ref())?,
|
||||
name: name.as_ref().to_string(),
|
||||
name: name.as_ref().into(),
|
||||
connect: connect.to_vec()
|
||||
};
|
||||
port.connect_to_matching()?;
|
||||
|
|
@ -151,7 +151,7 @@ impl JackPort<AudioIn> {
|
|||
let mut port = Self {
|
||||
jack: jack.clone(),
|
||||
port: jack.audio_in(name.as_ref())?,
|
||||
name: name.as_ref().to_string(),
|
||||
name: name.as_ref().into(),
|
||||
connect: connect.to_vec()
|
||||
};
|
||||
port.connect_to_matching()?;
|
||||
|
|
@ -165,7 +165,7 @@ impl JackPort<AudioOut> {
|
|||
let mut port = Self {
|
||||
jack: jack.clone(),
|
||||
port: jack.audio_out(name.as_ref())?,
|
||||
name: name.as_ref().to_string(),
|
||||
name: name.as_ref().into(),
|
||||
connect: connect.to_vec()
|
||||
};
|
||||
port.connect_to_matching()?;
|
||||
|
|
|
|||
117
tek/src/app.rs
117
tek/src/app.rs
|
|
@ -98,15 +98,15 @@ impl EdnViewData<TuiOut> for &App {
|
|||
Nil => Box::new(()),
|
||||
Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())),
|
||||
Sym(":editor") => (&self.editor).boxed(),
|
||||
Sym(":inputs") => self.input_row(w).boxed(),
|
||||
Sym(":outputs") => self.output_row(w).boxed(),
|
||||
Sym(":inputs") => self.input_row(w, 3).boxed(),
|
||||
Sym(":outputs") => self.output_row(w, 3).boxed(),
|
||||
Sym(":pool") => self.pool().boxed(),
|
||||
Sym(":sample") => self.sample().boxed(),
|
||||
Sym(":sampler") => self.sampler().boxed(),
|
||||
Sym(":scenes") => self.scene_row(w).boxed(),
|
||||
Sym(":status") => self.status(0).boxed(),
|
||||
Sym(":toolbar") => self.toolbar().boxed(),
|
||||
Sym(":tracks") => self.track_row(w).boxed(),
|
||||
Sym(":tracks") => self.track_row(w, 3).boxed(),
|
||||
_ => panic!("no content for {item:?}")
|
||||
}
|
||||
}
|
||||
|
|
@ -162,10 +162,25 @@ impl App {
|
|||
}
|
||||
None
|
||||
}
|
||||
fn track_row (&self, w: u16) -> impl Content<TuiOut> + '_ { "" }
|
||||
fn input_row (&self, w: u16) -> impl Content<TuiOut> + '_ { "" }
|
||||
|
||||
fn row <'a> (
|
||||
&'a self, w: u16, h: u16, a: impl Content<TuiOut> + 'a, b: impl Content<TuiOut> + 'a
|
||||
) -> impl Content<TuiOut> + 'a {
|
||||
Fixed::y(h, Bsp::e(
|
||||
Fixed::xy(self.sidebar_w() as u16, h, a),
|
||||
Fill::x(Align::c(Fixed::xy(w, h, b)))
|
||||
))
|
||||
}
|
||||
fn track_row (&self, w: u16, h: u16) -> impl Content<TuiOut> + '_ {
|
||||
self.row(w, h, track_header(&self), track_cells(&self))
|
||||
}
|
||||
fn input_row (&self, w: u16, h: u16) -> impl Content<TuiOut> + '_ {
|
||||
self.row(w, h, input_header(&self), input_cells(&self))
|
||||
}
|
||||
fn output_row (&self, w: u16, h: u16) -> impl Content<TuiOut> + '_ {
|
||||
self.row(w, h, output_header(&self), output_cells(&self))
|
||||
}
|
||||
fn scene_row (&self, w: u16) -> impl Content<TuiOut> + '_ { "" }
|
||||
fn output_row (&self, w: u16) -> impl Content<TuiOut> + '_ { "" }
|
||||
|
||||
pub fn tracks_with_sizes (&self)
|
||||
-> impl Iterator<Item = (usize, &ArrangerTrack, usize, usize)>
|
||||
|
|
@ -205,3 +220,93 @@ pub fn tracks_with_sizes <'a> (
|
|||
data
|
||||
})
|
||||
}
|
||||
pub fn track_header <'a> (state: &'a App) -> BoxThunk<'a, TuiOut> {
|
||||
(||Tui::bg(TuiTheme::g(32), Tui::bold(true, Bsp::s(
|
||||
row!(
|
||||
Tui::fg(TuiTheme::g(128), "add "),
|
||||
Tui::fg(TuiTheme::orange(), "t"),
|
||||
Tui::fg(TuiTheme::g(128), "rack"),
|
||||
),
|
||||
row!(
|
||||
Tui::fg(TuiTheme::orange(), "a"),
|
||||
Tui::fg(TuiTheme::g(128), "dd scene"),
|
||||
),
|
||||
))).boxed()).into()
|
||||
}
|
||||
pub fn track_cells <'a> (state: &'a App) -> BoxThunk<'a, TuiOut> {
|
||||
let iter = ||state.tracks_with_sizes();
|
||||
(move||Align::x(Map::new(iter, move|(_, track, x1, x2), i| {
|
||||
let name = Push::x(1, &track.name);
|
||||
let color = track.color();
|
||||
let fg = color.lightest.rgb;
|
||||
let bg = color.base.rgb;
|
||||
let active = state.selected.track() == Some(i);
|
||||
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)))))
|
||||
))
|
||||
})).boxed()).into()
|
||||
}
|
||||
fn help_tag <'a>(before: &'a str, key: &'a str, after: &'a str) -> impl Content<TuiOut> + 'a {
|
||||
let lo = TuiTheme::g(128);
|
||||
let hi = TuiTheme::orange();
|
||||
Tui::bold(true, row!(Tui::fg(lo, before), Tui::fg(hi, key), Tui::fg(lo, after)))
|
||||
}
|
||||
fn input_header <'a> (state: &'a App) -> BoxThunk<'a, TuiOut> {
|
||||
let fg = TuiTheme::g(224);
|
||||
let bg = TuiTheme::g(64);
|
||||
(move||Bsp::s(help_tag("midi ", "I", "ns"), state.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,
|
||||
Tui::fg_bg(fg, bg, connect.info()))))),
|
||||
))).boxed()).into()
|
||||
}
|
||||
fn input_cells <'a> (state: &'a App) -> BoxThunk<'a, TuiOut> {
|
||||
(move||Align::x(Map::new(||state.tracks_with_sizes(), move|(_, track, x1, x2), i| {
|
||||
let w = (x2 - x1) as u16;
|
||||
let color: ItemPalette = track.color().dark.into();
|
||||
map_east(x1 as u16, w, Fixed::x(w, cell(color, Bsp::n(
|
||||
rec_mon(color.base.rgb, false, false),
|
||||
phat_hi(color.base.rgb, color.dark.rgb)
|
||||
))))
|
||||
})).boxed()).into()
|
||||
}
|
||||
fn output_header <'a> (state: &'a App) -> BoxThunk<'a, TuiOut> {
|
||||
let fg = TuiTheme::g(224);
|
||||
let bg = TuiTheme::g(64);
|
||||
(move||Bsp::s(help_tag("midi ", "O", "uts"), state.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,
|
||||
Tui::fg_bg(fg, bg, connect.info()))))),
|
||||
))).boxed()).into()
|
||||
}
|
||||
fn output_cells <'a> (state: &'a App) -> BoxThunk<'a, TuiOut> {
|
||||
(move||Align::x(Map::new(||state.tracks_with_sizes(), move|(_, track, x1, x2), i| {
|
||||
let w = (x2 - x1) as u16;
|
||||
let color: ItemPalette = track.color().dark.into();
|
||||
map_east(x1 as u16, w, Fixed::x(w, cell(color, Bsp::n(
|
||||
mute_solo(color.base.rgb, false, false),
|
||||
phat_hi(color.dark.rgb, color.darker.rgb)
|
||||
))))
|
||||
})).boxed()).into()
|
||||
}
|
||||
fn rec_mon (bg: Color, rec: bool, mon: bool) -> impl Content<TuiOut> {
|
||||
row!(
|
||||
Tui::fg_bg(if rec { Color::Red } else { bg }, bg, "▐"),
|
||||
Tui::fg_bg(if rec { Color::White } else { Color::Rgb(0,0,0) }, bg, "REC"),
|
||||
Tui::fg_bg(if rec { Color::White } else { bg }, bg, "▐"),
|
||||
Tui::fg_bg(if mon { Color::White } else { Color::Rgb(0,0,0) }, bg, "MON"),
|
||||
Tui::fg_bg(if mon { Color::White } else { bg }, bg, "▌"),
|
||||
)
|
||||
}
|
||||
fn mute_solo (bg: Color, mute: bool, solo: bool) -> impl Content<TuiOut> {
|
||||
row!(
|
||||
Tui::fg_bg(if mute { Color::White } else { Color::Rgb(0,0,0) }, bg, "MUTE"),
|
||||
Tui::fg_bg(if mute { Color::White } else { bg }, bg, "▐"),
|
||||
Tui::fg_bg(if solo { Color::White } else { Color::Rgb(0,0,0) }, bg, "SOLO"),
|
||||
)
|
||||
}
|
||||
fn cell <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
|
||||
Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -520,15 +520,15 @@ impl Arranger {
|
|||
}
|
||||
}
|
||||
/// A phat line
|
||||
fn phat_lo (fg: Color, bg: Color) -> impl Content<TuiOut> {
|
||||
pub fn phat_lo (fg: Color, bg: Color) -> impl Content<TuiOut> {
|
||||
Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(&"▄")))
|
||||
}
|
||||
/// A phat line
|
||||
fn phat_hi (fg: Color, bg: Color) -> impl Content<TuiOut> {
|
||||
pub fn phat_hi (fg: Color, bg: Color) -> impl Content<TuiOut> {
|
||||
Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(&"▀")))
|
||||
}
|
||||
/// A cell that is 3-row on its own, but stacks, giving (N+1)*2 rows per N cells.
|
||||
fn phat_cell <T: Content<TuiOut>> (
|
||||
pub fn phat_cell <T: Content<TuiOut>> (
|
||||
color: ItemPalette, last: ItemPalette, field: T
|
||||
) -> impl Content<TuiOut> {
|
||||
Bsp::s(phat_lo(color.base.rgb, last.base.rgb),
|
||||
|
|
@ -537,7 +537,7 @@ fn phat_cell <T: Content<TuiOut>> (
|
|||
)
|
||||
)
|
||||
}
|
||||
fn phat_cell_3 <T: Content<TuiOut>> (
|
||||
pub fn phat_cell_3 <T: Content<TuiOut>> (
|
||||
field: T, top: Color, middle: Color, bottom: Color
|
||||
) -> impl Content<TuiOut> {
|
||||
Bsp::s(phat_lo(middle, top),
|
||||
|
|
@ -546,7 +546,7 @@ fn phat_cell_3 <T: Content<TuiOut>> (
|
|||
)
|
||||
)
|
||||
}
|
||||
fn phat_sel_3 <T: Content<TuiOut>> (
|
||||
pub fn phat_sel_3 <T: Content<TuiOut>> (
|
||||
selected: bool, field_1: T, field_2: T, top: Option<Color>, middle: Color, bottom: Color
|
||||
) -> impl Content<TuiOut> {
|
||||
let border = Style::default().fg(Color::Rgb(255,255,255)).bg(middle);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue