mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip: show single-device chain in launcher
This commit is contained in:
parent
1f194dafd8
commit
2f52e97c58
7 changed files with 315 additions and 239 deletions
183
src/device.rs
183
src/device.rs
|
|
@ -23,90 +23,6 @@ use ::jack::{AudioIn, AudioOut, MidiIn, MidiOut, Port, PortSpec, Client};
|
||||||
|
|
||||||
pub trait Device: Render + Handle + DevicePorts + Send + Sync {}
|
pub trait Device: Render + Handle + DevicePorts + Send + Sync {}
|
||||||
|
|
||||||
impl<T: Render + Handle + DevicePorts + Send + Sync> Device for T {}
|
|
||||||
|
|
||||||
pub trait Handle {
|
|
||||||
// Returns Ok(true) if the device handled the event.
|
|
||||||
// This is the mechanism which allows nesting of components;.
|
|
||||||
fn handle (&mut self, _e: &AppEvent) -> Usually<bool> {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Render {
|
|
||||||
// Returns space used by component.
|
|
||||||
// This is insufficient but for the most basic dynamic layout algorithms.
|
|
||||||
fn render (&self, _b: &mut Buffer, _a: Rect) -> Usually<Rect> {
|
|
||||||
Ok(Rect { x: 0, y: 0, width: 0, height: 0 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait DevicePorts {
|
|
||||||
fn audio_ins (&self) -> Usually<Vec<String>> {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
fn audio_outs (&self) -> Usually<Vec<String>> {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
fn midi_ins (&self) -> Usually<Vec<String>> {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
fn midi_outs (&self) -> Usually<Vec<String>> {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
fn connect (&mut self, connect: bool, source: &str, target: &str)
|
|
||||||
-> Usually<()>
|
|
||||||
{
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn connect_all (&mut self, connections: &[(bool, &str, &str)])
|
|
||||||
-> Usually<()>
|
|
||||||
{
|
|
||||||
for (connect, source, target) in connections.iter() {
|
|
||||||
self.connect(*connect, source, target)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Render for Box<dyn Device> {
|
|
||||||
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
|
||||||
(**self).render(b, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DevicePort<T: PortSpec> {
|
|
||||||
pub name: String,
|
|
||||||
pub port: Port<T>,
|
|
||||||
pub connect: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: PortSpec + Default> DevicePort<T> {
|
|
||||||
pub fn new (client: &Client, name: &str, connect: &[&str]) -> Usually<Self> {
|
|
||||||
let mut connects = vec![];
|
|
||||||
for port in connect.iter() {
|
|
||||||
connects.push(port.to_string());
|
|
||||||
}
|
|
||||||
Ok(Self {
|
|
||||||
name: name.to_string(),
|
|
||||||
port: client.register_port(name, T::default())?,
|
|
||||||
connect: connects,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WidgetRef for &dyn Render {
|
|
||||||
fn render_ref (&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
Render::render(*self, buf, area).expect("Failed to render device.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WidgetRef for dyn Render {
|
|
||||||
fn render_ref (&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
Render::render(self, buf, area).expect("Failed to render device.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn Error>> {
|
pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn Error>> {
|
||||||
let device = Arc::new(Mutex::new(device));
|
let device = Arc::new(Mutex::new(device));
|
||||||
let exited = Arc::new(AtomicBool::new(false));
|
let exited = Arc::new(AtomicBool::new(false));
|
||||||
|
|
@ -176,6 +92,105 @@ pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn E
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Render + Handle + DevicePorts + Send + Sync> Device for T {}
|
||||||
|
|
||||||
|
pub trait Handle {
|
||||||
|
// Handle an input event.
|
||||||
|
// Returns Ok(true) if the device handled the event.
|
||||||
|
// This is the mechanism which allows nesting of components;.
|
||||||
|
fn handle (&mut self, _e: &AppEvent) -> Usually<bool> {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Render {
|
||||||
|
// Render something to an area of the buffer.
|
||||||
|
// Returns area used by component.
|
||||||
|
// This is insufficient but for the most basic dynamic layout algorithms.
|
||||||
|
fn render (&self, _b: &mut Buffer, _a: Rect) -> Usually<Rect> {
|
||||||
|
Ok(Rect { x: 0, y: 0, width: 0, height: 0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Blit {
|
||||||
|
// Render something to X, Y coordinates in a buffer, ignoring width/height.
|
||||||
|
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>) {
|
||||||
|
if x < buf.area.width && y < buf.area.height {
|
||||||
|
buf.set_string(x, y, self.as_ref(), style.unwrap_or(Style::default()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DevicePorts {
|
||||||
|
fn audio_ins (&self) -> Usually<Vec<String>> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
fn audio_outs (&self) -> Usually<Vec<String>> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
fn midi_ins (&self) -> Usually<Vec<String>> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
fn midi_outs (&self) -> Usually<Vec<String>> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
fn connect (&mut self, connect: bool, source: &str, target: &str)
|
||||||
|
-> Usually<()>
|
||||||
|
{
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn connect_all (&mut self, connections: &[(bool, &str, &str)])
|
||||||
|
-> Usually<()>
|
||||||
|
{
|
||||||
|
for (connect, source, target) in connections.iter() {
|
||||||
|
self.connect(*connect, source, target)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for Box<dyn Device> {
|
||||||
|
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||||
|
(**self).render(b, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DevicePort<T: PortSpec> {
|
||||||
|
pub name: String,
|
||||||
|
pub port: Port<T>,
|
||||||
|
pub connect: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PortSpec + Default> DevicePort<T> {
|
||||||
|
pub fn new (client: &Client, name: &str, connect: &[&str]) -> Usually<Self> {
|
||||||
|
let mut connects = vec![];
|
||||||
|
for port in connect.iter() {
|
||||||
|
connects.push(port.to_string());
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
port: client.register_port(name, T::default())?,
|
||||||
|
connect: connects,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetRef for &dyn Render {
|
||||||
|
fn render_ref (&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
Render::render(*self, buf, area).expect("Failed to render device.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetRef for dyn Render {
|
||||||
|
fn render_ref (&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
Render::render(self, buf, area).expect("Failed to render device.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DynamicDevice<T> {
|
pub struct DynamicDevice<T> {
|
||||||
pub state: Arc<Mutex<T>>,
|
pub state: Arc<Mutex<T>>,
|
||||||
pub render: Mutex<Box<dyn FnMut(&T, &mut Buffer, Rect)->Usually<Rect> + Send>>,
|
pub render: Mutex<Box<dyn FnMut(&T, &mut Buffer, Rect)->Usually<Rect> + Send>>,
|
||||||
|
|
|
||||||
|
|
@ -52,58 +52,7 @@ pub fn render (state: &Chain, buf: &mut Buffer, area: Rect)
|
||||||
draw_box_styled(buf, area, selected)
|
draw_box_styled(buf, area, selected)
|
||||||
},
|
},
|
||||||
ChainView::Column => {
|
ChainView::Column => {
|
||||||
//let (area, areas) = Column::draw(buf, area, &state.items, 0)?;
|
draw_as_column(state, buf, area, selected)?
|
||||||
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
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
//let area = Rect { x, y, width: 40, height: 30 };
|
//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 {
|
impl Focus for Chain {
|
||||||
fn unfocus (&mut self) {
|
fn unfocus (&mut self) {
|
||||||
self.focused = false
|
self.focused = false
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,21 @@ pub struct Launcher {
|
||||||
chains: Vec<DynamicDevice<Chain>>,
|
chains: Vec<DynamicDevice<Chain>>,
|
||||||
rows: usize,
|
rows: usize,
|
||||||
show_help: bool,
|
show_help: bool,
|
||||||
|
view: LauncherView,
|
||||||
|
}
|
||||||
|
pub enum LauncherView {
|
||||||
|
Tracks,
|
||||||
|
Sequencer,
|
||||||
|
Chains
|
||||||
}
|
}
|
||||||
impl Launcher {
|
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 {
|
Ok(DynamicDevice::new(render, handle, process, Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
|
view: LauncherView::Tracks,
|
||||||
timebase: timebase.clone(),
|
timebase: timebase.clone(),
|
||||||
cursor: (0, 0),
|
cursor: (0, 0),
|
||||||
rows: 8,
|
rows: 8,
|
||||||
|
|
@ -22,7 +32,18 @@ impl Launcher {
|
||||||
Sequencer::new("Lead", timebase)?,
|
Sequencer::new("Lead", timebase)?,
|
||||||
],
|
],
|
||||||
chains: vec![
|
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
|
show_help: true
|
||||||
}))
|
}))
|
||||||
|
|
@ -72,38 +93,52 @@ impl DevicePorts for Launcher {}
|
||||||
pub fn process (_: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
|
pub fn process (_: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
|
||||||
Control::Continue
|
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> {
|
pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
let Rect { x, y, width, height } = area;
|
let Rect { x, y, width, height } = area;
|
||||||
let separator = format!("├{}┤", "-".repeat((width - 2).into()));
|
let track_area = Rect { x: 0, y: 0, width, height: 22 };
|
||||||
let scenes = draw_launcher_scenes(state, buf, x, y);
|
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 + 2, Some(Style::default().dim()));
|
||||||
separator.blit(buf, x, y + 4, Some(Style::default().dim()));
|
separator.blit(buf, x, y + 4, Some(Style::default().dim()));
|
||||||
separator.blit(buf, x, y + 40, 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 {
|
if state.col() == 0 {
|
||||||
highlight = Some(scenes);
|
highlight = Some(scenes);
|
||||||
}
|
}
|
||||||
draw_crossings(state, buf, x + w - 2, y);
|
draw_crossings(state, buf, x + w - 2, y);
|
||||||
draw_sequencer(state, buf, x, y + 21, width, height - 22)?;
|
draw_sequencer(state, buf, x, y + 21, width, height - 22)?;
|
||||||
draw_box(buf, area);
|
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)
|
Ok(area)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_launcher_scenes (
|
fn draw_scenes (
|
||||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16,
|
state: &Launcher, buf: &mut Buffer, x: u16, y: u16,
|
||||||
) -> Rect {
|
) -> Rect {
|
||||||
let style = Style::default().not_dim().bold();
|
let style = Style::default().not_dim().bold();
|
||||||
|
|
@ -128,8 +163,7 @@ fn draw_launcher_scenes (
|
||||||
}
|
}
|
||||||
Rect { x, y, width, height }
|
Rect { x, y, width, height }
|
||||||
}
|
}
|
||||||
|
fn draw_tracks (
|
||||||
fn draw_launcher_tracks (
|
|
||||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16
|
state: &Launcher, buf: &mut Buffer, x: u16, y: u16
|
||||||
) -> (u16, Option<Rect>) {
|
) -> (u16, Option<Rect>) {
|
||||||
let mut w = 15;
|
let mut w = 15;
|
||||||
|
|
@ -137,7 +171,7 @@ fn draw_launcher_tracks (
|
||||||
for (i, track) in state.tracks.iter().enumerate() {
|
for (i, track) in state.tracks.iter().enumerate() {
|
||||||
let track = track.state();
|
let track = track.state();
|
||||||
draw_crossings(state, buf, x + w - 2, y);
|
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() {
|
if i + 1 == state.col() {
|
||||||
highlight = Some(Rect { x: x + w - 2, y, width: width + 1, height: 22 });
|
highlight = Some(Rect { x: x + w - 2, y, width: width + 1, height: 22 });
|
||||||
}
|
}
|
||||||
|
|
@ -145,8 +179,7 @@ fn draw_launcher_tracks (
|
||||||
}
|
}
|
||||||
(w, highlight)
|
(w, highlight)
|
||||||
}
|
}
|
||||||
|
fn draw_track (
|
||||||
fn draw_launcher_track (
|
|
||||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, i: u16, track: &Sequencer
|
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, i: u16, track: &Sequencer
|
||||||
) -> u16 {
|
) -> u16 {
|
||||||
let mut width = track.name.len() as u16 + 3;
|
let mut width = track.name.len() as u16 + 3;
|
||||||
|
|
@ -190,7 +223,6 @@ fn draw_launcher_track (
|
||||||
|
|
||||||
width
|
width
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_crossings (state: &Launcher, buf: &mut Buffer, x: u16, y: u16) {
|
fn draw_crossings (state: &Launcher, buf: &mut Buffer, x: u16, y: u16) {
|
||||||
"╭".blit(buf, x, y + 0, None);
|
"╭".blit(buf, x, y + 0, None);
|
||||||
"┊".blit(buf, x, y + 1, Some(Style::default().dim()));
|
"┊".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 + 3, Some(Style::default().dim()));
|
||||||
"┼".blit(buf, x, y + 4, Some(Style::default().dim()));
|
"┼".blit(buf, x, y + 4, Some(Style::default().dim()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_sequencer (
|
fn draw_sequencer (
|
||||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, width: u16, height: u16
|
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, width: u16, height: u16
|
||||||
) -> Usually<()> {
|
) -> Usually<()> {
|
||||||
|
|
@ -212,9 +243,9 @@ fn draw_sequencer (
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
fn draw_highlight (
|
||||||
fn draw_launcher_highlight (
|
state: &Launcher, buf: &mut Buffer, highlight: &Option<Rect>
|
||||||
state: &Launcher, buf: &mut Buffer, highlight: &Option<Rect>) {
|
) {
|
||||||
if let Some(area) = highlight {
|
if let Some(area) = highlight {
|
||||||
draw_box_styled(buf, *area, Some(Style::default().green().dim()));
|
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)
|
handle_keymap(state, event, KEYMAP)
|
||||||
}
|
}
|
||||||
pub const KEYMAP: &'static [KeyBinding<Launcher>] = keymap!(Launcher {
|
pub const KEYMAP: &'static [KeyBinding<Launcher>] = keymap!(Launcher {
|
||||||
[Up, NONE, "cursor_up", "move cursor up", cursor_up],
|
[Up, NONE, "cursor_up", "move cursor up", cursor_up],
|
||||||
[Down, NONE, "cursor_down", "move cursor down", cursor_down],
|
[Down, NONE, "cursor_down", "move cursor down", cursor_down],
|
||||||
[Left, NONE, "cursor_left", "move cursor left", cursor_left],
|
[Left, NONE, "cursor_left", "move cursor left", cursor_left],
|
||||||
[Right, NONE, "cursor_right", "move cursor right", cursor_right],
|
[Right, NONE, "cursor_right", "move cursor right", cursor_right],
|
||||||
[Char('r'), NONE, "rename", "rename current element", rename],
|
[Char('r'), NONE, "rename", "rename current element", rename],
|
||||||
[F(1), NONE, "toggle_help", "toggle help", toggle_help],
|
[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> {
|
fn rename (_: &mut Launcher) -> Usually<bool> {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
|
@ -254,3 +287,19 @@ fn toggle_help (state: &mut Launcher) -> Usually<bool> {
|
||||||
state.show_help = !state.show_help;
|
state.show_help = !state.show_help;
|
||||||
Ok(true)
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,31 +47,38 @@ pub fn render (state: &Plugin, buf: &mut Buffer, area: Rect)
|
||||||
{
|
{
|
||||||
let header = draw_header(state, buf, area)?;
|
let header = draw_header(state, buf, area)?;
|
||||||
let Rect { x, y, width, height } = 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 style = Style::default().gray();
|
||||||
|
let mut width = 40u16;
|
||||||
match &state.plugin {
|
match &state.plugin {
|
||||||
Some(PluginKind::LV2(ports, instance)) => {
|
Some(PluginKind::LV2(ports, instance)) => {
|
||||||
let mut height = 3;
|
for i in 0..height - 4 {
|
||||||
for (i, port) in ports
|
let port = &ports[i as usize];
|
||||||
.iter()
|
let label = &format!("C·· M·· {:25} = {:03}", port.name, port.default_value);
|
||||||
.filter(|port|port.port_type == ::livi::PortType::ControlInput)
|
width = width.max(label.len() as u16);
|
||||||
.skip(state.offset)
|
label.blit(buf, x + 2, y + 3 + i as u16, None);
|
||||||
.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: 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);
|
buf.set_string(x + 1, y + 3, &format!(" Parameter 1 0.0"), style);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ pub fn draw (
|
||||||
let height = 32.max(s.note_axis.1 - s.note_axis.0) / 2;
|
let height = 32.max(s.note_axis.1 - s.note_axis.0) / 2;
|
||||||
lanes(s, buf, x, y, width);
|
lanes(s, buf, x, y, width);
|
||||||
cursor(s, buf, x, y);
|
cursor(s, buf, x, y);
|
||||||
footer(s, buf, x - 13, y, width, height);
|
footer(s, buf, x, y, width, height);
|
||||||
Ok(Rect {
|
Ok(Rect {
|
||||||
x: x - 13,
|
x: x - 13,
|
||||||
y: y,
|
y: y,
|
||||||
|
|
@ -25,7 +25,6 @@ pub fn draw (
|
||||||
|
|
||||||
pub fn timer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, beat: usize) {
|
pub fn timer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, beat: usize) {
|
||||||
let bw = Style::default().dim();
|
let bw = Style::default().dim();
|
||||||
let bg = Style::default().on_black();
|
|
||||||
let (time0, time1) = s.time_axis;
|
let (time0, time1) = s.time_axis;
|
||||||
for step in time0..time1 {
|
for step in time0..time1 {
|
||||||
buf.set_string(x + 6 + step, y - 1, &"-", if beat % s.steps == step as usize {
|
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) {
|
pub fn lanes (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, width: u16) {
|
||||||
let bw = Style::default().dim();
|
let bw = Style::default().dim();
|
||||||
let bg = Style::default().on_black();
|
let bg = Style::default();
|
||||||
let ppq = s.timebase.ppq() as u32;
|
let ppq = s.timebase.ppq() as u32;
|
||||||
let (time0, time1) = s.time_axis;
|
let (time0, time1) = s.time_axis;
|
||||||
let notes = &s.sequences[s.sequence].notes;
|
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) {
|
pub fn footer (s: &Sequencer, buf: &mut Buffer, mut x: u16, y: u16, width: u16, height: u16) {
|
||||||
let bw = Style::default().dim();
|
let bw = Style::default().dim();
|
||||||
let bg = Style::default().on_black();
|
let bg = Style::default();
|
||||||
let (note0, note1) = s.note_axis;
|
let (note0, note1) = s.note_axis;
|
||||||
buf.set_string(x, y + height, format!("├{}┤", "-".repeat((width - 2).into())),
|
buf.set_string(x, y + height, format!("├{}┤", "-".repeat((width - 2).into())),
|
||||||
Style::default().dim());
|
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) {
|
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(
|
buf.set_string(
|
||||||
x + 6 + s.time_cursor,
|
x + 6 + s.time_cursor,
|
||||||
y + s.note_cursor / 2,
|
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> {
|
pub fn keys (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
||||||
let bw = Style::default().dim();
|
let bw = Style::default().dim();
|
||||||
let bg = Style::default().on_black();
|
|
||||||
let Rect { x, y, .. } = area;
|
let Rect { x, y, .. } = area;
|
||||||
let (note0, note1) = s.note_axis;
|
let (note0, note1) = s.note_axis;
|
||||||
let (time0, time1) = s.time_axis;
|
let (time0, time1) = s.time_axis;
|
||||||
for i in 0..32.max(note1-note0)/2 {
|
for i in 0..32.max(note1-note0)/2 {
|
||||||
let y = y + i;
|
let y = y + i;
|
||||||
buf.set_string(x + 2, y, KEYS_VERTICAL[(i % 6) as usize], bw);
|
buf.set_string(x + 1, y, KEYS_VERTICAL[(i % 6) as usize], bw);
|
||||||
buf.set_string(x + 3, y, "█", bw);
|
buf.set_string(x + 2, y, "█", bw);
|
||||||
buf.set_string(x + 6, y, &"·".repeat((time1 - time0) as usize), bg.dim());
|
buf.set_string(x + 5, y, &"·".repeat((time1 - time0) as usize), bw.black());
|
||||||
if i % 6 == 0 {
|
if i % 6 == 0 {
|
||||||
let octave = format!("C{}", ((note1 - i) / 6) as i8 - 4);
|
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)
|
Ok(area)
|
||||||
|
|
|
||||||
|
|
@ -8,34 +8,21 @@ pub use scroll::*;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub fn draw_box (buffer: &mut Buffer, area: Rect) -> Rect {
|
pub fn draw_box (buffer: &mut Buffer, area: Rect) -> Rect {
|
||||||
if area.width < 1 || area.height < 1 {
|
draw_box_styled(buffer, area, Some(Style::default().gray().dim()))
|
||||||
return area
|
|
||||||
}
|
|
||||||
let border = Style::default().gray().dim();
|
|
||||||
let top = format!("╭{}╮", "─".repeat((area.width - 2).into()));
|
|
||||||
let bottom = format!("╰{}╯", "─".repeat((area.width - 2).into()));
|
|
||||||
buffer.set_string(area.x, area.y, top, border);
|
|
||||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
|
||||||
buffer.set_string(area.x, y, format!("│"), border);
|
|
||||||
buffer.set_string(area.x + area.width - 1, y, format!("│"), border);
|
|
||||||
}
|
|
||||||
buffer.set_string(area.x, area.y + area.height - 1, bottom, border);
|
|
||||||
area
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_box_styled (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
pub fn draw_box_styled (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
||||||
if area.width < 1 || area.height < 1 {
|
if area.width < 1 || area.height < 1 {
|
||||||
return area
|
return area
|
||||||
}
|
}
|
||||||
let style = style.unwrap_or(Style::default());
|
format!("╭{}╮", "─".repeat((area.width - 2).into()))
|
||||||
let top = format!("╭{}╮", "─".repeat((area.width - 2).into()));
|
.blit(buffer, area.x, area.y, style);
|
||||||
let bottom = format!("╰{}╯", "─".repeat((area.width - 2).into()));
|
|
||||||
buffer.set_string(area.x, area.y, top, style);
|
|
||||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
for y in (area.y + 1)..(area.y + area.height - 1) {
|
||||||
buffer.set_string(area.x, y, format!("│"), style);
|
"│".blit(buffer, area.x, y, style);
|
||||||
buffer.set_string(area.x + area.width - 1, y, format!("│"), style);
|
"│".blit(buffer, area.x + area.width - 1, y, style);
|
||||||
}
|
}
|
||||||
buffer.set_string(area.x, area.y + area.height - 1, bottom, style);
|
format!("╰{}╯", "─".repeat((area.width - 2).into()))
|
||||||
|
.blit(buffer, area.x, area.y + area.height - 1, style);
|
||||||
area
|
area
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,14 +30,13 @@ pub fn draw_box_styled_dotted (buffer: &mut Buffer, area: Rect, style: Option<St
|
||||||
if area.width < 1 || area.height < 1 {
|
if area.width < 1 || area.height < 1 {
|
||||||
return area
|
return area
|
||||||
}
|
}
|
||||||
let style = style.unwrap_or(Style::default());
|
format!("╭{}╮", "┅".repeat((area.width - 2).into()))
|
||||||
let top = format!("╭{}╮", "┅".repeat((area.width - 2).into()));
|
.blit(buffer, area.x, area.y, style);
|
||||||
let bottom = format!("╰{}╯", "┅".repeat((area.width - 2).into()));
|
|
||||||
buffer.set_string(area.x, area.y, top, style);
|
|
||||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
for y in (area.y + 1)..(area.y + area.height - 1) {
|
||||||
buffer.set_string(area.x, y, format!("┇"), style);
|
"┇".blit(buffer, area.x, y, style);
|
||||||
buffer.set_string(area.x + area.width - 1, y, format!("┇"), style);
|
"┇".blit(buffer, area.x + area.width - 1, y, style);
|
||||||
}
|
}
|
||||||
buffer.set_string(area.x, area.y + area.height - 1, bottom, style);
|
format!("╰{}╯", "┅".repeat((area.width - 2).into()))
|
||||||
|
.blit(buffer, area.x, area.y + area.height - 1, style);
|
||||||
area
|
area
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,5 @@ fn main () -> Result<(), Box<dyn Error>> {
|
||||||
crate::config::create_dirs(&xdg)?;
|
crate::config::create_dirs(&xdg)?;
|
||||||
let transport = crate::device::Transport::new("Transport")?;
|
let transport = crate::device::Transport::new("Transport")?;
|
||||||
let timebase = transport.state.lock().unwrap().timebase();
|
let timebase = transport.state.lock().unwrap().timebase();
|
||||||
//crate::device::run(Sequencer::new("Sequencer#0", &timebase)?)
|
run(Launcher::new("Launcher#0", &timebase)?)
|
||||||
crate::device::run(Launcher::new("Launcher#0", &timebase)?)
|
|
||||||
//crate::device::run(Chain::new("Chain#0000", vec![
|
|
||||||
//Box::new(Sequencer::new("Phrase#000", &timebase)?),
|
|
||||||
//Box::new(Sequencer::new("Phrase#001", &timebase)?),
|
|
||||||
////Box::new(Sequencer::new("Phrase#002", &timebase)?),
|
|
||||||
////Box::new(Plugin::new("Plugin#000")?),
|
|
||||||
//])?)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue