diff --git a/src/device/chain/mod.rs b/src/device/chain/mod.rs index de1c1acd..0e6b8b8e 100644 --- a/src/device/chain/mod.rs +++ b/src/device/chain/mod.rs @@ -70,8 +70,9 @@ pub fn render (state: &Chain, buf: &mut Buffer, area: Rect) draw_box_styled(buf, area, selected) }, ChainView::Row => { + draw_box_styled(buf, area, selected); let (area, areas) = Row::draw(buf, area, &state.items, 0)?; - draw_box_styled(buf, area, selected) + area }, ChainView::Column => { draw_as_column(state, buf, area, selected)? @@ -124,7 +125,7 @@ pub fn draw_as_row ( x = x + 1; let mut h = 0u16; let mut frames = vec![]; - for device in state.items.iter() { + for (i, device) in state.items.iter().enumerate() { let mut x2 = 1u16; let mut y2 = 1u16; for port in device.midi_ins()?.iter() { @@ -138,7 +139,18 @@ pub fn draw_as_row ( y2 = y2 + 1; } let width = width.saturating_sub(x).saturating_sub(x2); + let style = Some(if i == state.focus { + if state.focused { + Style::default().green().not_dim() + } else { + Style::default().green().dim() + } + } else { + Style::default().dim() + }); + lozenge_left(buf, x + x2, y, height, style); let frame = device.render(buf, Rect { x: x + x2, y, width, height })?; + lozenge_right(buf, x + x2 + frame.width - 1, y, height, style); let mut y2 = 1u16; for port in device.midi_outs()?.iter() { port.blit(buf, x + x2 + frame.width, y + y2, Some(Style::default())); diff --git a/src/device/chain/plugin.rs b/src/device/chain/plugin.rs index a9537f14..bf4c4ea7 100644 --- a/src/device/chain/plugin.rs +++ b/src/device/chain/plugin.rs @@ -168,43 +168,34 @@ impl PortList for Plugin { pub fn render (state: &Plugin, buf: &mut Buffer, area: Rect) -> Usually { - let Rect { x, y, width, height } = area; - let height = height.saturating_sub(3); - let style = Style::default().gray(); + let Rect { x, y, height, .. } = area; let mut width = 40u16; match &state.plugin { - Some(PluginKind::LV2 { portList, instance, .. }) => { - for i in 0..height - 4 { + Some(PluginKind::LV2 { portList, .. }) => { + //draw_box(buf, Rect { x, y, width, height }); + for i in 0..height-3 { if let Some(port) = portList.get(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); + label.blit(buf, x + 2, y + 2 + i as u16, None); } else { break } } - draw_box(buf, Rect { x, y, width, height }); }, - _ => { - buf.set_string(x + 1, y + 3, &format!(" Parameter 1 0.0"), style); - buf.set_string(x + 1, y + 4, &format!(" Parameter 2 0.0"), style); - buf.set_string(x + 1, y + 5, &format!(" Parameter 3 0.0"), style); - buf.set_string(x + 1, y + 6, &format!(" Parameter 4 0.0"), style); - } + _ => {} }; - draw_header(state, buf, area.x, area.y, width); + draw_header(state, buf, area.x, area.y, width)?; Ok(Rect { width, ..area }) } fn draw_header (state: &Plugin, buf: &mut Buffer, x: u16, y: u16, w: u16) -> Usually { - let style = Style::default().gray(); - buf.set_string(x + 1, y + 1, - &format!(" {}", state.name), style.white().bold()); - buf.set_string(x + 13, y + 1, - &format!("│ {}...", &HELM[..(w as usize - 20).min(HELM.len())]), style.not_dim()); - buf.set_string(x + 0, y + 2, - &format!("├{}┤", "-".repeat(w as usize - 2)), style.dim()); - Ok(Rect { x, y, width: w, height: 3 }) + let style = Style::default().gray(); + let label1 = format!(" {}", state.name); + label1.blit(buf, x + 1, y, Some(style.white().bold())); + let label2 = format!("{}…", &HELM[..(w as usize - 10).min(HELM.len())]); + label2.blit(buf, x + 2 + label1.len() as u16, y, Some(style.not_dim())); + Ok(Rect { x, y, width: w, height: 1 }) } pub fn handle (s: &mut Plugin, event: &AppEvent) -> Usually { diff --git a/src/device/chain/sampler.rs b/src/device/chain/sampler.rs index 572612ff..cfc8c463 100644 --- a/src/device/chain/sampler.rs +++ b/src/device/chain/sampler.rs @@ -52,7 +52,7 @@ impl Sampler { samples: samples.unwrap_or(BTreeMap::new()), voices: vec![], midi_in: client.register_port("midi", ::jack::MidiIn::default())?, - audio_ins: vec![ + audio_ins: vec![ client.register_port("recL", ::jack::AudioIn::default())?, client.register_port("recR", ::jack::AudioIn::default())?, ], @@ -127,7 +127,7 @@ pub fn render (state: &Sampler, buf: &mut Buffer, Rect { x, y, height, .. }: Rec { let width = 40; let style = Style::default().gray(); - format!(" {}", state.name).blit(buf, x+1, y, Some(style.white().bold())); + format!(" {} ", state.name).blit(buf, x+1, y, Some(style.white().bold().not_dim())); for (i, (note, sample)) in state.samples.iter().enumerate() { let style = if i == state.cursor.0 { Style::default().green() @@ -198,9 +198,9 @@ pub fn handle (state: &mut Sampler, event: &AppEvent) -> Usually { Ok(handle_keymap(state, event, KEYMAP)?) } pub const KEYMAP: &'static [KeyBinding] = keymap!(Sampler { - [Up, NONE, "cursor_up", "move cursor up", cursor_up], - [Down, NONE, "cursor_down", "move cursor down", cursor_down], - [Enter, NONE, "enter", "activate", enter], + [Up, NONE, "cursor_up", "move cursor up", cursor_up], + [Down, NONE, "cursor_down", "move cursor down", cursor_down], + [Enter, NONE, "select", "select item under cursor", select], }); fn cursor_up (state: &mut Sampler) -> Usually { state.cursor.0 = if state.cursor.0 == 0 { @@ -214,6 +214,9 @@ fn cursor_down (state: &mut Sampler) -> Usually { state.cursor.0 = (state.cursor.0 + 1) % state.samples.len(); Ok(true) } -fn enter (state: &mut Sampler) -> Usually { +fn select (state: &mut Sampler) -> Usually { + if let Some(sample) = state.samples.get(&u7::from_int_lossy(state.cursor.0 as u8)) { + state.voices.push(sample.play()) + } Ok(true) } diff --git a/src/device/launcher/grid.rs b/src/device/launcher/grid.rs index 17bc0b00..9c18ae25 100644 --- a/src/device/launcher/grid.rs +++ b/src/device/launcher/grid.rs @@ -17,13 +17,13 @@ impl<'a> LauncherGridView<'a> { //self.separator_h(2, false); //self.separator_h((self.state.cursor.1 * 2) as u16, true); //self.separator_h(((self.state.cursor.1 + 1) * 2) as u16, true); - draw_box_styled(self.buf, self.area, Some( - if self.focused { - Style::default().green().dim() - } else { - Style::default().dim() - } - )); + let style = Some(Style::default().green().dim()); + if self.focused { + let Rect { x, y, width, height } = self.area; + lozenge_left(self.buf, x, y, height, style); + lozenge_right(self.buf, x + width - 1, y, height, style); + } + let columns = self.column_names(); let mut x = self.area.x; diff --git a/src/device/launcher/mod.rs b/src/device/launcher/mod.rs index 9c2943da..08f4ef58 100644 --- a/src/device/launcher/mod.rs +++ b/src/device/launcher/mod.rs @@ -52,15 +52,16 @@ impl Launcher { let transport = client.transport(); let ppq = timebase.ppq() as u32; DynamicDevice::new(render, handle, process, Self { - name: name.into(), - view: LauncherView::Tracks, - playing: transport.query_state()?, - monitoring: true, - recording: false, - overdub: true, + name: name.into(), + view: LauncherView::Tracks, + playing: transport.query_state()?, transport, - cursor: (0, 0), - position: 0, + timebase: timebase.clone(), + monitoring: true, + recording: false, + overdub: true, + cursor: (0, 0), + position: 0, scenes: scenes.unwrap_or_else(||vec![Scene::new(&"Scene 1", &[None])]), tracks: if let Some(tracks) = tracks { tracks } else { vec![ Track::new("Track 1", &timebase, None, Some(vec![ @@ -72,8 +73,7 @@ impl Launcher { ]))) ]))?, ] }, - timebase: timebase.clone(), - show_help: true + show_help: true, }).activate(client) } fn cols (&self) -> usize { @@ -181,15 +181,13 @@ pub fn render (state: &Launcher, buf: &mut Buffer, mut area: Rect) -> Usually Us let view = &state.view; match view { LauncherView::Sequencer => { - draw_box_styled(buf, area, style); + lozenge_left(buf, x, y, height, style); + lozenge_right(buf, x + width - 1, y, height, style); }, _ => {}, }; @@ -236,9 +235,13 @@ fn draw_section_sequencer (state: &Launcher, buf: &mut Buffer, area: Rect) -> Us let state = track.sequencer.state(); let zoom = state.resolution; - let step = state.phrase().map(|_|tick / zoom); + let steps = if let Some(_phrase) = state.phrase() { + 0 + } else { + 0 + } / 4; crate::device::sequencer::horizontal::timer(buf, x+5, y, - step.unwrap_or(0) / 4, + steps, state.steps * zoom, state.time_axis.0, state.time_axis.1 @@ -279,6 +282,14 @@ fn draw_highlight (buf: &mut Buffer, highlight: &Option, style: Style) { } fn draw_section_chains (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually { let style = Some(Style::default().green().dim()); + match state.view { + LauncherView::Chains => { + let Rect { x, y, width, height} = area; + lozenge_left(buf, x, y, height, style); + lozenge_right(buf, x + width - 1, y, height, style); + }, + _ => {}, + }; let chain = state.active_chain(); let plugins = if let Some(chain) = &chain { let (_, plugins) = crate::device::chain::draw_as_row( @@ -294,17 +305,17 @@ fn draw_section_chains (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usual //}, //_ => {}, //}; - draw_highlight(buf, &Some(area), match state.view { - LauncherView::Chains => Style::default().green().dim(), - _ => Style::default().dim() - }); - if let Some(chain) = &chain { - if let Some(plugin) = plugins.get(chain.focus) { - draw_highlight(buf, &Some(*plugin), match state.view { - LauncherView::Chains => Style::default().green().not_dim(), - _ => Style::default().green().dim() - }); - } - } + //draw_highlight(buf, &Some(area), match state.view { + //LauncherView::Chains => Style::default().green().dim(), + //_ => Style::default().dim() + //}); + //if let Some(chain) = &chain { + //if let Some(plugin) = plugins.get(chain.focus) { + //draw_highlight(buf, &Some(*plugin), match state.view { + //LauncherView::Chains => Style::default().green().not_dim(), + //_ => Style::default().green().dim() + //}); + //} + //} Ok(area) } diff --git a/src/layout/lozenge.rs b/src/layout/lozenge.rs new file mode 100644 index 00000000..254dfc57 --- /dev/null +++ b/src/layout/lozenge.rs @@ -0,0 +1,35 @@ +use crate::prelude::*; + +const LOZENGE: [[&'static str;3];3] = [ + ["╭", "─", "╮"], + ["│", " ", "│"], + ["╰", "─", "╯"], +]; + +pub fn lozenge_left (buf: &mut Buffer, x: u16, y1: u16, h: u16, style: Option