wip: show single-device chain in launcher

This commit is contained in:
🪞👃🪞 2024-06-24 22:55:24 +03:00
parent 1f194dafd8
commit 2f52e97c58
7 changed files with 315 additions and 239 deletions

View file

@ -52,58 +52,7 @@ pub fn render (state: &Chain, buf: &mut Buffer, area: Rect)
draw_box_styled(buf, area, selected)
},
ChainView::Column => {
//let (area, areas) = Column::draw(buf, area, &state.items, 0)?;
let mut w = 0u16;
let mut y = area.y;
let mut frames = vec![];
for (i, device) in state.items.iter().enumerate() {
let midi_ins = device.midi_ins()?;
let midi_outs = device.midi_outs()?;
let audio_ins = device.audio_ins()?;
let audio_outs = device.audio_outs()?;
y = y + midi_ins.len() as u16;
let frame = device.render(buf, Rect { x, y, width, height: height - y })?;
frames.push(frame);
w = w.max(frame.width);
y = y - midi_ins.len() as u16;
for port in midi_ins.iter() {
buf.set_string(x + frame.width - 10, y,
&format!(" <i> MIDI {port} "),
Style::default().black().bold().on_green());
y = y + 1;
}
y = y - audio_ins.len() as u16;
for port in audio_ins.iter() {
buf.set_string(x + frame.width - 10, y,
&format!(" <i> MIDI {port} "),
Style::default().black().bold().on_red());
y = y + 1;
}
y = y + frame.height - 1;
y = y + midi_outs.len() as u16;
for port in midi_outs.iter() {
buf.set_string(x + 2, y,
&format!(" <o> MIDI {port} "),
Style::default().black().bold().on_green());
y = y + 1;
}
y = y + audio_outs.len() as u16;
for port in audio_outs.iter() {
buf.set_string(x + 2, y,
&format!(" <o> Audio {port} "),
Style::default().black().bold().on_red());
y = y + 1;
}
}
draw_box_styled(buf, frames[state.focus], selected);
area
draw_as_column(state, buf, area, selected)?
},
})
//let area = Rect { x, y, width: 40, height: 30 };
@ -146,6 +95,87 @@ pub fn render (state: &Chain, buf: &mut Buffer, area: Rect)
//}))
}
pub fn draw_as_row (
state: &Chain, buf: &mut Buffer, area: Rect, selected: Option<Style>
) -> Usually<Rect> {
let Rect { mut x, mut y, width, height } = area;
let mut w = 0u16;
let mut h = 0u16;
let mut frames = vec![];
for (i, device) in state.items.iter().enumerate() {
let midi_ins = device.midi_ins()?;
let midi_outs = device.midi_outs()?;
let audio_ins = device.audio_ins()?;
let audio_outs = device.audio_outs()?;
let width = width.saturating_sub(x);
let frame = device.render(buf, Rect { x, y, width, height })?;
frames.push(frame);
h = h.max(frame.height);
x = x + frame.width;
}
Ok(area)
}
pub fn draw_as_column (
state: &Chain, buf: &mut Buffer, area: Rect, selected: Option<Style>
) -> Usually<Rect> {
let Rect { mut x, mut y, width, height } = area;
//let (area, areas) = Column::draw(buf, area, &state.items, 0)?;
let mut w = 0u16;
let mut y = area.y;
let mut frames = vec![];
for (i, device) in state.items.iter().enumerate() {
let midi_ins = device.midi_ins()?;
let midi_outs = device.midi_outs()?;
let audio_ins = device.audio_ins()?;
let audio_outs = device.audio_outs()?;
y = y + midi_ins.len() as u16;
let frame = device.render(buf, Rect {
x, y, width, height: height.saturating_sub(y)
})?;
frames.push(frame);
w = w.max(frame.width);
y = y - midi_ins.len() as u16;
for port in midi_ins.iter() {
buf.set_string(x + frame.width - 10, y,
&format!(" <i> MIDI {port} "),
Style::default().black().bold().on_green());
y = y + 1;
}
y = y - audio_ins.len() as u16;
for port in audio_ins.iter() {
buf.set_string(x + frame.width - 10, y,
&format!(" <i> MIDI {port} "),
Style::default().black().bold().on_red());
y = y + 1;
}
y = y + frame.height - 1;
y = y + midi_outs.len() as u16;
for port in midi_outs.iter() {
buf.set_string(x + 2, y,
&format!(" <o> MIDI {port} "),
Style::default().black().bold().on_green());
y = y + 1;
}
y = y + audio_outs.len() as u16;
for port in audio_outs.iter() {
buf.set_string(x + 2, y,
&format!(" <o> Audio {port} "),
Style::default().black().bold().on_red());
y = y + 1;
}
}
draw_box_styled(buf, frames[state.focus], selected);
Ok(area)
}
impl Focus for Chain {
fn unfocus (&mut self) {
self.focused = false

View file

@ -7,11 +7,21 @@ pub struct Launcher {
chains: Vec<DynamicDevice<Chain>>,
rows: usize,
show_help: bool,
view: LauncherView,
}
pub enum LauncherView {
Tracks,
Sequencer,
Chains
}
impl Launcher {
pub fn new (name: &str, timebase: &Arc<Timebase>) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
pub fn new (
name: &str,
timebase: &Arc<Timebase>,
) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
Ok(DynamicDevice::new(render, handle, process, Self {
name: name.into(),
view: LauncherView::Tracks,
timebase: timebase.clone(),
cursor: (0, 0),
rows: 8,
@ -22,7 +32,18 @@ impl Launcher {
Sequencer::new("Lead", timebase)?,
],
chains: vec![
// TODO
Chain::new("Chain#0000", vec![
Box::new(Plugin::new("Plugin#000")?),
])?,
Chain::new("Chain#0000", vec![
Box::new(Plugin::new("Plugin#001")?),
])?,
Chain::new("Chain#0000", vec![
Box::new(Plugin::new("Plugin#002")?),
])?,
Chain::new("Chain#0000", vec![
Box::new(Plugin::new("Plugin#003")?),
])?,
],
show_help: true
}))
@ -72,38 +93,52 @@ impl DevicePorts for Launcher {}
pub fn process (_: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
Control::Continue
}
macro_rules! set {
($buf:expr, $style:expr, $x: expr, $y: expr, $fmt: literal $(, $val:expr)*) => {
$buf.set_string($x, $y, &format!($fmt $(, $val)*), $style)
}
}
trait Blit {
fn blit (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>);
}
impl<T: AsRef<str>> Blit for T {
fn blit (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>) {
buf.set_string(x, y, self.as_ref(), style.unwrap_or(Style::default()))
}
}
pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, width, height } = area;
let separator = format!("{}", "-".repeat((width - 2).into()));
let scenes = draw_launcher_scenes(state, buf, x, y);
let track_area = Rect { x: 0, y: 0, width, height: 22 };
let seq_area = Rect { x: 0, y: 23, width, height: 18 };
let chain_area = Rect { x: 0, y: 40, width, height: 22 };
let separator = format!("{}", "-".repeat((width - 2).into()));
let scenes = draw_scenes(state, buf, x, y);
separator.blit(buf, x, y + 2, Some(Style::default().dim()));
separator.blit(buf, x, y + 4, Some(Style::default().dim()));
separator.blit(buf, x, y + 40, Some(Style::default().dim()));
let (w, mut highlight) = draw_launcher_tracks(state, buf, x, y);
let (w, mut highlight) = draw_tracks(state, buf, x, y);
if state.col() == 0 {
highlight = Some(scenes);
}
draw_crossings(state, buf, x + w - 2, y);
draw_sequencer(state, buf, x, y + 21, width, height - 22)?;
draw_box(buf, area);
draw_launcher_highlight(state, buf, &highlight);
draw_highlight(state, buf, &highlight);
let style = Some(Style::default().green().not_dim());
crate::device::chain::draw_as_row(
&*state.chains[0].state(), buf, chain_area, Some(Style::default().green().dim())
)?;
match state.view {
LauncherView::Tracks => draw_box_styled(buf, track_area, style),
LauncherView::Sequencer => draw_box_styled(buf, seq_area, style),
LauncherView::Chains => draw_box_styled(buf, chain_area, style),
};
if state.show_help {
let style = Some(Style::default().bold().white().not_dim().on_black().italic());
//" [Tab] ".blit(buf, 1, match state.view {
//LauncherView::Tracks => 21,
//LauncherView::Sequencer => 40,
//LauncherView::Chains => 0,
//}, style);
//" [Shift-Tab] ".blit(buf, 1, match state.view {
//LauncherView::Tracks => 40,
//LauncherView::Sequencer => 0,
//LauncherView::Chains => 21,
//}, style);
let hide = "[F1] Toggle help ";
hide.blit(buf, width - hide.len() as u16, height - 1, style);
}
Ok(area)
}
fn draw_launcher_scenes (
fn draw_scenes (
state: &Launcher, buf: &mut Buffer, x: u16, y: u16,
) -> Rect {
let style = Style::default().not_dim().bold();
@ -128,8 +163,7 @@ fn draw_launcher_scenes (
}
Rect { x, y, width, height }
}
fn draw_launcher_tracks (
fn draw_tracks (
state: &Launcher, buf: &mut Buffer, x: u16, y: u16
) -> (u16, Option<Rect>) {
let mut w = 15;
@ -137,7 +171,7 @@ fn draw_launcher_tracks (
for (i, track) in state.tracks.iter().enumerate() {
let track = track.state();
draw_crossings(state, buf, x + w - 2, y);
let width = draw_launcher_track(state, buf, x + w, y, i as u16, &track);
let width = draw_track(state, buf, x + w, y, i as u16, &track);
if i + 1 == state.col() {
highlight = Some(Rect { x: x + w - 2, y, width: width + 1, height: 22 });
}
@ -145,8 +179,7 @@ fn draw_launcher_tracks (
}
(w, highlight)
}
fn draw_launcher_track (
fn draw_track (
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, i: u16, track: &Sequencer
) -> u16 {
let mut width = track.name.len() as u16 + 3;
@ -190,7 +223,6 @@ fn draw_launcher_track (
width
}
fn draw_crossings (state: &Launcher, buf: &mut Buffer, x: u16, y: u16) {
"".blit(buf, x, y + 0, None);
"".blit(buf, x, y + 1, Some(Style::default().dim()));
@ -198,7 +230,6 @@ fn draw_crossings (state: &Launcher, buf: &mut Buffer, x: u16, y: u16) {
"".blit(buf, x, y + 3, Some(Style::default().dim()));
"".blit(buf, x, y + 4, Some(Style::default().dim()));
}
fn draw_sequencer (
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, width: u16, height: u16
) -> Usually<()> {
@ -212,9 +243,9 @@ fn draw_sequencer (
}
Ok(())
}
fn draw_launcher_highlight (
state: &Launcher, buf: &mut Buffer, highlight: &Option<Rect>) {
fn draw_highlight (
state: &Launcher, buf: &mut Buffer, highlight: &Option<Rect>
) {
if let Some(area) = highlight {
draw_box_styled(buf, *area, Some(Style::default().green().dim()));
}
@ -224,12 +255,14 @@ pub fn handle (state: &mut Launcher, event: &AppEvent) -> Usually<bool> {
handle_keymap(state, event, KEYMAP)
}
pub const KEYMAP: &'static [KeyBinding<Launcher>] = keymap!(Launcher {
[Up, NONE, "cursor_up", "move cursor up", cursor_up],
[Down, NONE, "cursor_down", "move cursor down", cursor_down],
[Left, NONE, "cursor_left", "move cursor left", cursor_left],
[Right, NONE, "cursor_right", "move cursor right", cursor_right],
[Char('r'), NONE, "rename", "rename current element", rename],
[F(1), NONE, "toggle_help", "toggle help", toggle_help],
[Up, NONE, "cursor_up", "move cursor up", cursor_up],
[Down, NONE, "cursor_down", "move cursor down", cursor_down],
[Left, NONE, "cursor_left", "move cursor left", cursor_left],
[Right, NONE, "cursor_right", "move cursor right", cursor_right],
[Char('r'), NONE, "rename", "rename current element", rename],
[F(1), NONE, "toggle_help", "toggle help", toggle_help],
[Tab, SHIFT, "focus_prev", "focus previous area", focus_prev],
[Tab, NONE, "focus_next", "focus next area", focus_next],
});
fn rename (_: &mut Launcher) -> Usually<bool> {
Ok(true)
@ -254,3 +287,19 @@ fn toggle_help (state: &mut Launcher) -> Usually<bool> {
state.show_help = !state.show_help;
Ok(true)
}
fn focus_next (state: &mut Launcher) -> Usually<bool> {
state.view = match state.view {
LauncherView::Tracks => LauncherView::Sequencer,
LauncherView::Sequencer => LauncherView::Chains,
LauncherView::Chains => LauncherView::Tracks,
};
Ok(true)
}
fn focus_prev (state: &mut Launcher) -> Usually<bool> {
state.view = match state.view {
LauncherView::Tracks => LauncherView::Chains,
LauncherView::Chains => LauncherView::Sequencer,
LauncherView::Sequencer => LauncherView::Tracks,
};
Ok(true)
}

View file

@ -47,31 +47,38 @@ pub fn render (state: &Plugin, buf: &mut Buffer, area: Rect)
{
let header = draw_header(state, buf, area)?;
let Rect { x, y, width, height } = area;
let height = height - header.height;
let height = height.saturating_sub(header.height);
let style = Style::default().gray();
let mut width = 40u16;
match &state.plugin {
Some(PluginKind::LV2(ports, instance)) => {
let mut height = 3;
for (i, port) in ports
.iter()
.filter(|port|port.port_type == ::livi::PortType::ControlInput)
.skip(state.offset)
.enumerate()
{
if i >= 20 {
break
}
buf.set_string(x + 2, y + 3 + i as u16, &format!("C·· M·· {:25} = {:03}",
port.name,
port.default_value
), if i + state.offset == state.selected {
Style::default().white().bold()
} else {
Style::default().not_dim()
});
height = height + 1;
for i in 0..height - 4 {
let port = &ports[i as usize];
let label = &format!("C·· M·· {:25} = {:03}", port.name, port.default_value);
width = width.max(label.len() as u16);
label.blit(buf, x + 2, y + 3 + i as u16, None);
}
Ok(draw_box(buf, Rect { x, y, width, height: height.max(20) }))
//let mut height = 3;
//for (i, port) in ports
//.iter()
//.filter(|port|port.port_type == ::livi::PortType::ControlInput)
//.skip(state.offset)
//.enumerate()
//{
//if i >= 20 {
//break
//}
//buf.set_string(x + 2, y + 3 + i as u16, &format!("C·· M·· {:25} = {:03}",
//port.name,
//port.default_value
//), if i + state.offset == state.selected {
//Style::default().white().bold()
//} else {
//Style::default().not_dim()
//});
//height = height + 1;
//}
Ok(draw_box(buf, Rect { x, y, width, height }))
},
_ => {
buf.set_string(x + 1, y + 3, &format!(" Parameter 1 0.0"), style);

View file

@ -14,7 +14,7 @@ pub fn draw (
let height = 32.max(s.note_axis.1 - s.note_axis.0) / 2;
lanes(s, buf, x, y, width);
cursor(s, buf, x, y);
footer(s, buf, x - 13, y, width, height);
footer(s, buf, x, y, width, height);
Ok(Rect {
x: x - 13,
y: y,
@ -25,7 +25,6 @@ pub fn draw (
pub fn timer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, beat: usize) {
let bw = Style::default().dim();
let bg = Style::default().on_black();
let (time0, time1) = s.time_axis;
for step in time0..time1 {
buf.set_string(x + 6 + step, y - 1, &"-", if beat % s.steps == step as usize {
@ -38,7 +37,7 @@ pub fn timer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, beat: usize) {
pub fn lanes (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, width: u16) {
let bw = Style::default().dim();
let bg = Style::default().on_black();
let bg = Style::default();
let ppq = s.timebase.ppq() as u32;
let (time0, time1) = s.time_axis;
let notes = &s.sequences[s.sequence].notes;
@ -58,7 +57,7 @@ pub fn lanes (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, width: u16) {
pub fn footer (s: &Sequencer, buf: &mut Buffer, mut x: u16, y: u16, width: u16, height: u16) {
let bw = Style::default().dim();
let bg = Style::default().on_black();
let bg = Style::default();
let (note0, note1) = s.note_axis;
buf.set_string(x, y + height, format!("{}", "-".repeat((width - 2).into())),
Style::default().dim());
@ -91,8 +90,6 @@ pub fn footer (s: &Sequencer, buf: &mut Buffer, mut x: u16, y: u16, width: u16,
}
pub fn cursor (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) {
let bw = Style::default().dim();
let bg = Style::default().on_black();
buf.set_string(
x + 6 + s.time_cursor,
y + s.note_cursor / 2,
@ -103,18 +100,17 @@ pub fn cursor (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) {
pub fn keys (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
let bw = Style::default().dim();
let bg = Style::default().on_black();
let Rect { x, y, .. } = area;
let (note0, note1) = s.note_axis;
let (time0, time1) = s.time_axis;
for i in 0..32.max(note1-note0)/2 {
let y = y + i;
buf.set_string(x + 2, y, KEYS_VERTICAL[(i % 6) as usize], bw);
buf.set_string(x + 3, y, "", bw);
buf.set_string(x + 6, y, &"·".repeat((time1 - time0) as usize), bg.dim());
buf.set_string(x + 1, y, KEYS_VERTICAL[(i % 6) as usize], bw);
buf.set_string(x + 2, y, "", bw);
buf.set_string(x + 5, y, &"·".repeat((time1 - time0) as usize), bw.black());
if i % 6 == 0 {
let octave = format!("C{}", ((note1 - i) / 6) as i8 - 4);
buf.set_string(x + 4, y, &octave, Style::default());
buf.set_string(x + 3, y, &octave, Style::default());
}
}
Ok(area)