mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
refactor: separate Render from Handle
This commit is contained in:
parent
d9b3bd150e
commit
72ead536be
12 changed files with 255 additions and 212 deletions
|
|
@ -20,6 +20,44 @@ pub use self::launcher::Launcher;
|
||||||
|
|
||||||
use crossterm::event;
|
use crossterm::event;
|
||||||
|
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for Box<dyn Device> {
|
||||||
|
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||||
|
(**self).render(b, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Device: Render + Handle + Send + Sync {}
|
||||||
|
|
||||||
|
impl<T: Render + Handle + Send + Sync> Device for T {}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
@ -89,19 +127,6 @@ pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn E
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Device: Send + Sync {
|
|
||||||
// Returns Ok(true) if the device handled the event.
|
|
||||||
// This is the mechanism which allows nesting of components;.
|
|
||||||
fn handle (&mut self, _event: &AppEvent) -> Usually<bool> {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
// Returns space used by component.
|
|
||||||
// This is insufficient but for the most basic dynamic layout algorithms.
|
|
||||||
fn render (&self, _buffer: &mut Buffer, _area: Rect) -> Usually<Rect> {
|
|
||||||
Ok(Rect { x: 0, y: 0, width: 0, height: 0 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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>>,
|
||||||
|
|
@ -110,10 +135,13 @@ pub struct DynamicDevice<T> {
|
||||||
client: Option<DynamicAsyncClient>
|
client: Option<DynamicAsyncClient>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Send + Sync> Device for DynamicDevice<T> {
|
impl<T> Handle for DynamicDevice<T> {
|
||||||
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
||||||
self.handle.lock().unwrap()(&mut *self.state.lock().unwrap(), event)
|
self.handle.lock().unwrap()(&mut *self.state.lock().unwrap(), event)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Render for DynamicDevice<T> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
self.render.lock().unwrap()(&*self.state.lock().unwrap(), buf, area)
|
self.render.lock().unwrap()(&*self.state.lock().unwrap(), buf, area)
|
||||||
}
|
}
|
||||||
|
|
@ -162,18 +190,6 @@ impl<T: Send + Sync + 'static> DynamicDevice<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WidgetRef for &dyn Device {
|
|
||||||
fn render_ref (&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
Device::render(*self, buf, area).expect("Failed to render device.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WidgetRef for dyn Device {
|
|
||||||
fn render_ref (&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
Device::render(self, buf, area).expect("Failed to render device.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AppEvent {
|
pub enum AppEvent {
|
||||||
/// Terminal input
|
/// Terminal input
|
||||||
|
|
|
||||||
|
|
@ -35,29 +35,25 @@ pub fn render (state: &Chain, buf: &mut Buffer, area: Rect)
|
||||||
-> Usually<Rect>
|
-> Usually<Rect>
|
||||||
{
|
{
|
||||||
let Rect { x, y, .. } = area;
|
let Rect { x, y, .. } = area;
|
||||||
|
let selected = Some(if state.focused {
|
||||||
|
Style::default().green().not_dim()
|
||||||
|
} else {
|
||||||
|
Style::default().green().dim()
|
||||||
|
});
|
||||||
Ok(match state.view {
|
Ok(match state.view {
|
||||||
ChainView::Hidden => Rect { x, y, width: 0, height: 0 },
|
ChainView::Hidden => Rect { x, y, width: 0, height: 0 },
|
||||||
ChainView::Compact => {
|
ChainView::Compact => {
|
||||||
let area = Rect { x, y, width: (state.name.len() + 4) as u16, height: 3 };
|
let area = Rect { x, y, width: (state.name.len() + 4) as u16, height: 3 };
|
||||||
buf.set_string(area.x + 2, area.y + 1, &state.name, Style::default());
|
buf.set_string(area.x + 2, area.y + 1, &state.name, Style::default());
|
||||||
draw_box_styled(buf, area, Some(Style::default().dim()))
|
draw_box_styled(buf, area, selected)
|
||||||
},
|
},
|
||||||
ChainView::Row => {
|
ChainView::Row => {
|
||||||
let (area, areas) = draw_row(&state.items, buf, area, 0)?;
|
let (area, areas) = Row::draw(buf, area, &state.items, 0)?;
|
||||||
draw_box_styled(buf, area, Some(Style::default().dim()))
|
draw_box_styled(buf, area, selected)
|
||||||
},
|
},
|
||||||
ChainView::Column => {
|
ChainView::Column => {
|
||||||
let (area, areas) = draw_column(&state.items, buf, area, 0)?;
|
let (area, areas) = Column::draw(buf, area, &state.items, 0)?;
|
||||||
//draw_box_styled(buf, area, Some(if state.focused {
|
draw_box_styled(buf, areas[state.focus], selected);
|
||||||
//Style::default().dim()
|
|
||||||
//} else {
|
|
||||||
//Style::default().not_dim()
|
|
||||||
//}));
|
|
||||||
draw_box_styled(buf, areas[state.focus], Some(if state.focused {
|
|
||||||
Style::default().green().not_dim()
|
|
||||||
} else {
|
|
||||||
Style::default().green().dim()
|
|
||||||
}));
|
|
||||||
area
|
area
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,22 @@ impl Sequencer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Ports for Sequencer {
|
||||||
|
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![self.input_port.short_name()?])
|
||||||
|
}
|
||||||
|
fn midi_outs (&self) -> Usually<Vec<String>> {
|
||||||
|
Ok(vec![self.output_port.short_name()?])
|
||||||
|
}
|
||||||
|
fn connect (&mut self, connect: bool, source: &str, target: &str) {}
|
||||||
|
}
|
||||||
|
|
||||||
fn process_in (s: &mut Sequencer, scope: &ProcessScope, transport: &::jack::TransportStatePosition) {
|
fn process_in (s: &mut Sequencer, scope: &ProcessScope, transport: &::jack::TransportStatePosition) {
|
||||||
if !s.recording {
|
if !s.recording {
|
||||||
return
|
return
|
||||||
|
|
@ -227,11 +243,11 @@ fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usu
|
||||||
let style = Style::default().gray();
|
let style = Style::default().gray();
|
||||||
let timer = format!("{rep}.{step:02} / {reps}.{steps}");
|
let timer = format!("{rep}.{step:02} / {reps}.{steps}");
|
||||||
buf.set_string(x + width - 2 - timer.len() as u16, y + 1, &timer, style.bold().not_dim());
|
buf.set_string(x + width - 2 - timer.len() as u16, y + 1, &timer, style.bold().not_dim());
|
||||||
buf.set_string(x + 2, y + 1, &format!("⏹ STOP"), if s.playing {
|
if s.playing {
|
||||||
style.dim().bold()
|
buf.set_string(x + 2, y + 1, &format!("▶ PLAYING"), style.not_dim().white().bold());
|
||||||
} else {
|
} else {
|
||||||
style.not_dim().white().bold()
|
buf.set_string(x + 2, y + 1, &format!("⏹ STOPPED"), style.dim().bold());
|
||||||
});
|
}
|
||||||
buf.set_string(x, y + 2, format!("├{}┤", "-".repeat((area.width - 2).into())), style.dim());
|
buf.set_string(x, y + 2, format!("├{}┤", "-".repeat((area.width - 2).into())), style.dim());
|
||||||
//buf.set_string(x + 2, y + 2,
|
//buf.set_string(x + 2, y + 2,
|
||||||
//&format!("▶ PLAY"), if s.playing {
|
//&format!("▶ PLAY"), if s.playing {
|
||||||
|
|
@ -239,19 +255,19 @@ fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usu
|
||||||
//} else {
|
//} else {
|
||||||
//Style::default().dim()
|
//Style::default().dim()
|
||||||
//});
|
//});
|
||||||
buf.set_string(x + 10, y + 1,
|
buf.set_string(x + 13, y + 1,
|
||||||
&format!("⏺ REC"), if s.recording {
|
&format!("⏺ REC"), if s.recording {
|
||||||
Style::default().bold().red()
|
Style::default().bold().red()
|
||||||
} else {
|
} else {
|
||||||
Style::default().bold().dim()
|
Style::default().bold().dim()
|
||||||
});
|
});
|
||||||
buf.set_string(x + 17, y + 1,
|
buf.set_string(x + 20, y + 1,
|
||||||
&format!("⏺ DUB"), if s.overdub {
|
&format!("⏺ DUB"), if s.overdub {
|
||||||
Style::default().bold().yellow()
|
Style::default().bold().yellow()
|
||||||
} else {
|
} else {
|
||||||
Style::default().bold().dim()
|
Style::default().bold().dim()
|
||||||
});
|
});
|
||||||
buf.set_string(x + 24, y + 1,
|
buf.set_string(x + 27, y + 1,
|
||||||
&format!("⏺ MON"), if s.monitoring {
|
&format!("⏺ MON"), if s.monitoring {
|
||||||
Style::default().bold().green()
|
Style::default().bold().green()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,26 @@ pub fn draw_horizontal (
|
||||||
mut area: Rect,
|
mut area: Rect,
|
||||||
beat: usize
|
beat: usize
|
||||||
) -> Usually<Rect> {
|
) -> Usually<Rect> {
|
||||||
let ppq = s.timebase.ppq() as u32;
|
|
||||||
area.x = area.x + 13;
|
area.x = area.x + 13;
|
||||||
|
|
||||||
draw_keys_horizontal(s, buf, area)?;
|
|
||||||
|
|
||||||
let Rect { x, y, width, .. } = area;
|
let Rect { x, y, width, .. } = area;
|
||||||
let (time0, time1) = s.time_axis;
|
keys(s, buf, area)?;
|
||||||
let (note0, note1) = s.note_axis;
|
timer(s, buf, x, y, beat);
|
||||||
|
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, y, width, height);
|
||||||
|
Ok(Rect {
|
||||||
|
x: x - 13,
|
||||||
|
y: y,
|
||||||
|
width: s.time_axis.1 - s.time_axis.0 + 19,
|
||||||
|
height: height + 3
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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 bg = Style::default().on_black();
|
||||||
|
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 {
|
||||||
Style::default().yellow().bold().not_dim()
|
Style::default().yellow().bold().not_dim()
|
||||||
|
|
@ -25,6 +34,13 @@ pub fn draw_horizontal (
|
||||||
Style::default()
|
Style::default()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ppq = s.timebase.ppq() as u32;
|
||||||
|
let (time0, time1) = s.time_axis;
|
||||||
for step in time0..time1 {
|
for step in time0..time1 {
|
||||||
let time_start = step as u32 * ppq;
|
let time_start = step as u32 * ppq;
|
||||||
let time_end = (step + 1) as u32 * ppq;
|
let time_end = (step + 1) as u32 * ppq;
|
||||||
|
|
@ -33,12 +49,16 @@ pub fn draw_horizontal (
|
||||||
}
|
}
|
||||||
for (_, (_, events)) in s.sequences[s.sequence].range(time_start..time_end).enumerate() {
|
for (_, (_, events)) in s.sequences[s.sequence].range(time_start..time_end).enumerate() {
|
||||||
if events.len() > 0 {
|
if events.len() > 0 {
|
||||||
buf.set_string(x + 5 + step as u16, y, "█", bw);
|
buf.set_string(x + 6 + step as u16, y, "█", bw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let height = 32.max(note1 - note0) / 2;
|
fn footer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, width: u16, height: u16) {
|
||||||
|
let bw = Style::default().dim();
|
||||||
|
let bg = Style::default().on_black();
|
||||||
|
let (note0, note1) = s.note_axis;
|
||||||
buf.set_string(x - 13, y + height, format!("├{}┤", "-".repeat((width - 2).into())),
|
buf.set_string(x - 13, y + height, format!("├{}┤", "-".repeat((width - 2).into())),
|
||||||
Style::default().dim());
|
Style::default().dim());
|
||||||
{
|
{
|
||||||
|
|
@ -65,26 +85,25 @@ pub fn draw_horizontal (
|
||||||
x = x + 2;
|
x = x + 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
if s.note_cursor % 2 == 0 { "▀" } else { "▄" },
|
if s.note_cursor % 2 == 0 { "▀" } else { "▄" },
|
||||||
Style::default()
|
Style::default()
|
||||||
);
|
);
|
||||||
Ok(Rect {
|
|
||||||
x: x - 13,
|
|
||||||
y,
|
|
||||||
width: time1 - time0 + 19,
|
|
||||||
height: height + 3
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_keys_horizontal (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
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 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;
|
||||||
let bw = Style::default().dim();
|
|
||||||
let bg = Style::default().on_black();
|
|
||||||
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 + 2, y, KEYS_VERTICAL[(i % 6) as usize], bw);
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,23 @@ pub fn draw_vertical (
|
||||||
mut area: Rect,
|
mut area: Rect,
|
||||||
beat: usize
|
beat: usize
|
||||||
) -> Usually<Rect> {
|
) -> Usually<Rect> {
|
||||||
let ppq = s.timebase.ppq() as u32;
|
|
||||||
area.x = area.x + 13;
|
area.x = area.x + 13;
|
||||||
|
let Rect { x, y, .. } = area;
|
||||||
|
keys(s, buf, area, beat);
|
||||||
|
steps(s, buf, area, beat);
|
||||||
|
let height = (s.time_axis.1-s.time_axis.0)/2;
|
||||||
|
footer(s, buf, x, y, height);
|
||||||
|
playhead(s, buf, x, y);
|
||||||
|
Ok(Rect { x, y, width: area.width, height: height + 1 })
|
||||||
|
}
|
||||||
|
|
||||||
draw_keys_vertical(s, buf, area, beat);
|
fn steps (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) {
|
||||||
|
let ppq = s.timebase.ppq() as u32;
|
||||||
|
let bw = Style::default().dim().on_black();
|
||||||
|
let bg = Style::default().on_black();
|
||||||
let Rect { x, y, .. } = area;
|
let Rect { x, y, .. } = area;
|
||||||
let (time0, time1) = s.time_axis;
|
let (time0, time1) = s.time_axis;
|
||||||
let (note0, note1) = s.note_axis;
|
let (note0, note1) = s.note_axis;
|
||||||
let bw = Style::default().dim().on_black();
|
|
||||||
let bg = Style::default().on_black();
|
|
||||||
|
|
||||||
for step in time0..time1 {
|
for step in time0..time1 {
|
||||||
let y = y - time0 + step / 2;
|
let y = y - time0 + step / 2;
|
||||||
let step = step as usize;
|
let step = step as usize;
|
||||||
|
|
@ -56,8 +62,9 @@ pub fn draw_vertical (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let height = (time1-time0)/2;
|
fn footer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, height: u16) {
|
||||||
buf.set_string(x + 2, y + height + 1, format!(
|
buf.set_string(x + 2, y + height + 1, format!(
|
||||||
"Q 1/{} | N {} ({}-{}) | T {} ({}-{})",
|
"Q 1/{} | N {} ({}-{}) | T {} ({}-{})",
|
||||||
4 * s.resolution,
|
4 * s.resolution,
|
||||||
|
|
@ -68,16 +75,18 @@ pub fn draw_vertical (
|
||||||
s.time_axis.0 + 1,
|
s.time_axis.0 + 1,
|
||||||
s.time_axis.1,
|
s.time_axis.1,
|
||||||
), Style::default().dim());
|
), Style::default().dim());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn playhead (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) {
|
||||||
buf.set_string(
|
buf.set_string(
|
||||||
x + 5 + s.note_cursor,
|
x + 5 + s.note_cursor,
|
||||||
y + s.time_cursor / 2,
|
y + s.time_cursor / 2,
|
||||||
if s.time_cursor % 2 == 0 { "▀" } else { "▄" },
|
if s.time_cursor % 2 == 0 { "▀" } else { "▄" },
|
||||||
Style::default()
|
Style::default()
|
||||||
);
|
);
|
||||||
Ok(Rect { x, y, width: area.width, height: height + 1 })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_keys_vertical (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize) {
|
fn keys (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize) {
|
||||||
let ppq = s.timebase.ppq() as u32;
|
let ppq = s.timebase.ppq() as u32;
|
||||||
let Rect { x, y, .. } = area;
|
let Rect { x, y, .. } = area;
|
||||||
let (note0, note1) = s.note_axis;
|
let (note0, note1) = s.note_axis;
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,9 @@ impl Transport {
|
||||||
DynamicDevice::new(render, handle, process, Self {
|
DynamicDevice::new(render, handle, process, Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
timebase: Arc::new(Timebase {
|
timebase: Arc::new(Timebase {
|
||||||
rate: AtomicUsize::new(
|
rate: AtomicUsize::new(client.sample_rate()),
|
||||||
transport.query()?.pos.frame_rate().map(|x|x as usize).unwrap_or(0)
|
tempo: AtomicUsize::new(113000),
|
||||||
),
|
ppq: AtomicUsize::new(96),
|
||||||
tempo: AtomicUsize::new(
|
|
||||||
113000
|
|
||||||
),
|
|
||||||
ppq: AtomicUsize::new(
|
|
||||||
96
|
|
||||||
),
|
|
||||||
}),
|
}),
|
||||||
transport
|
transport
|
||||||
}).activate(client)
|
}).activate(client)
|
||||||
|
|
|
||||||
|
|
@ -1,111 +1,62 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use super::*;
|
||||||
pub fn draw_box (buffer: &mut Buffer, area: Rect) -> Rect {
|
|
||||||
if area.width < 1 || area.height < 1 {
|
|
||||||
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 {
|
|
||||||
if area.width < 1 || area.height < 1 {
|
|
||||||
return area
|
|
||||||
}
|
|
||||||
let style = style.unwrap_or(Style::default());
|
|
||||||
let top = format!("╭{}╮", "─".repeat((area.width - 2).into()));
|
|
||||||
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) {
|
|
||||||
buffer.set_string(area.x, y, format!("│"), style);
|
|
||||||
buffer.set_string(area.x + area.width - 1, y, format!("│"), style);
|
|
||||||
}
|
|
||||||
buffer.set_string(area.x, area.y + area.height - 1, bottom, style);
|
|
||||||
area
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_box_styled_dotted (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
|
||||||
if area.width < 1 || area.height < 1 {
|
|
||||||
return area
|
|
||||||
}
|
|
||||||
let style = style.unwrap_or(Style::default());
|
|
||||||
let top = format!("╭{}╮", "┅".repeat((area.width - 2).into()));
|
|
||||||
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) {
|
|
||||||
buffer.set_string(area.x, y, format!("┇"), style);
|
|
||||||
buffer.set_string(area.x + area.width - 1, y, format!("┇"), style);
|
|
||||||
}
|
|
||||||
buffer.set_string(area.x, area.y + area.height - 1, bottom, style);
|
|
||||||
area
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Column(pub Vec<Box<dyn Device>>);
|
pub struct Column(pub Vec<Box<dyn Device>>);
|
||||||
|
|
||||||
|
pub struct Row(pub Vec<Box<dyn Device>>);
|
||||||
|
|
||||||
impl Column {
|
impl Column {
|
||||||
pub fn new (items: Vec<Box<dyn Device>>) -> Self {
|
pub fn new (items: Vec<Box<dyn Device>>) -> Self {
|
||||||
Self(items)
|
Self(items)
|
||||||
}
|
}
|
||||||
|
pub fn draw (buf: &mut Buffer, area: Rect, items: &[impl Render], gap: i16)
|
||||||
|
-> Usually<(Rect, Vec<Rect>)>
|
||||||
|
{
|
||||||
|
let mut w = 0u16;
|
||||||
|
let mut h = 0u16;
|
||||||
|
let mut rects = vec![];
|
||||||
|
for (i, device) in items.iter().enumerate() {
|
||||||
|
let y = area.y + h;
|
||||||
|
let rect = Rect { x: area.x, y, width: area.width, height: area.height - h };
|
||||||
|
let result = device.render(buf, rect)?;
|
||||||
|
rects.push(result);
|
||||||
|
w = w.max(result.width);
|
||||||
|
h = ((h + result.height) as i16 + gap).max(0) as u16;
|
||||||
|
}
|
||||||
|
Ok((Rect { x: area.x, y: area.y, width: w, height: h }, rects))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Row(pub Vec<Box<dyn Device>>);
|
impl Render for Column {
|
||||||
|
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
|
Ok(Column::draw(buf, area, &self.0, 0)?.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Row {
|
impl Row {
|
||||||
pub fn new (items: Vec<Box<dyn Device>>) -> Self {
|
pub fn new (items: Vec<Box<dyn Device>>) -> Self {
|
||||||
Self(items)
|
Self(items)
|
||||||
}
|
}
|
||||||
|
pub fn draw (buf: &mut Buffer, area: Rect, items: &[impl Render], gap: i16)
|
||||||
|
-> Usually<(Rect, Vec<Rect>)>
|
||||||
|
{
|
||||||
|
let mut w = 0u16;
|
||||||
|
let mut h = 0u16;
|
||||||
|
let mut rects = vec![];
|
||||||
|
for (i, device) in items.iter().enumerate() {
|
||||||
|
let x = area.x + w;
|
||||||
|
let rect = Rect { x, y: area.y, width: area.width - w, height: area.height };
|
||||||
|
let result = device.render(buf, rect)?;
|
||||||
|
rects.push(result);
|
||||||
|
w = ((w + result.width) as i16 + gap).max(0) as u16;
|
||||||
|
h = h.max(result.height);
|
||||||
|
}
|
||||||
|
Ok((Rect { x: area.x, y: area.y, width: w, height: h }, rects))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device for Column {
|
impl Render for Row {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
Ok(draw_column(&self.0, buf, area, 0)?.0)
|
Ok(Row::draw(buf, area, &self.0, 0)?.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device for Row {
|
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
|
||||||
Ok(draw_row(&self.0, buf, area, 0)?.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_column (items: &[Box<dyn Device>], buf: &mut Buffer, area: Rect, space: i16)
|
|
||||||
-> Usually<(Rect, Vec<Rect>)>
|
|
||||||
{
|
|
||||||
let mut w = 0u16;
|
|
||||||
let mut h = 0u16;
|
|
||||||
let mut rects = vec![];
|
|
||||||
for (i, device) in items.iter().enumerate() {
|
|
||||||
let y = ((area.y + h) as i16).saturating_add(space) as u16;
|
|
||||||
let rect = Rect { x: area.x, y, width: area.width, height: area.height - h };
|
|
||||||
let result = device.render(buf, rect)?;
|
|
||||||
rects.push(result);
|
|
||||||
w = w.max(result.width);
|
|
||||||
h = h + result.height;
|
|
||||||
}
|
|
||||||
Ok((Rect { x: area.x, y: area.y, width: w, height: h }, rects))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_row (items: &[Box<dyn Device>], buf: &mut Buffer, area: Rect, space: i16)
|
|
||||||
-> Usually<(Rect, Vec<Rect>)>
|
|
||||||
{
|
|
||||||
let mut w = 0u16;
|
|
||||||
let mut h = 0u16;
|
|
||||||
let mut rects = vec![];
|
|
||||||
for (i, device) in items.iter().enumerate() {
|
|
||||||
let x = ((area.x + w) as i16).saturating_add(space) as u16;
|
|
||||||
let rect = Rect { x, y: area.y, width: area.width - w, height: area.height };
|
|
||||||
let result = device.render(buf, rect)?;
|
|
||||||
rects.push(result);
|
|
||||||
w = w + result.width;
|
|
||||||
h = h.max(result.height);
|
|
||||||
}
|
|
||||||
Ok((Rect { x: area.x, y: area.y, width: w, height: h }, rects))
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,15 @@ pub fn handle_focus <T: Focus> (
|
||||||
|
|
||||||
pub struct FocusColumn(pub Option<usize>, pub Column);
|
pub struct FocusColumn(pub Option<usize>, pub Column);
|
||||||
|
|
||||||
impl Device for FocusColumn {
|
impl Handle for FocusColumn {
|
||||||
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
||||||
handle_focus(self, event, KEYMAP_FOCUS_COLUMN)
|
handle_focus(self, event, KEYMAP_FOCUS_COLUMN)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for FocusColumn {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
let (rect, rects) = draw_column(self.1.0.as_ref(), buf, area, 0)?;
|
let (rect, rects) = Column::draw(buf, area, self.1.0.as_ref(), 0)?;
|
||||||
//if i == self.focus {
|
//if i == self.focus {
|
||||||
//if self.focused {
|
//if self.focused {
|
||||||
//draw_box_styled(buf, result, Some(Style::default().white().not_dim()))
|
//draw_box_styled(buf, result, Some(Style::default().white().not_dim()))
|
||||||
|
|
@ -108,12 +111,15 @@ impl Focus for FocusColumn {
|
||||||
|
|
||||||
pub struct FocusRow(pub Option<usize>, pub Row);
|
pub struct FocusRow(pub Option<usize>, pub Row);
|
||||||
|
|
||||||
impl Device for FocusRow {
|
impl Handle for FocusRow {
|
||||||
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
||||||
handle_focus(self, event, KEYMAP_FOCUS_ROW)
|
handle_focus(self, event, KEYMAP_FOCUS_ROW)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for FocusRow {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
let (rect, rects) = draw_row(&self.1.0, buf, area, 0)?;
|
let (rect, rects) = Row::draw(buf, area, &self.1.0, 0)?;
|
||||||
Ok(rect)
|
Ok(rect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,53 @@ mod container;
|
||||||
pub use container::*;
|
pub use container::*;
|
||||||
mod scroll;
|
mod scroll;
|
||||||
pub use scroll::*;
|
pub use scroll::*;
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub fn draw_box (buffer: &mut Buffer, area: Rect) -> Rect {
|
||||||
|
if area.width < 1 || area.height < 1 {
|
||||||
|
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 {
|
||||||
|
if area.width < 1 || area.height < 1 {
|
||||||
|
return area
|
||||||
|
}
|
||||||
|
let style = style.unwrap_or(Style::default());
|
||||||
|
let top = format!("╭{}╮", "─".repeat((area.width - 2).into()));
|
||||||
|
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) {
|
||||||
|
buffer.set_string(area.x, y, format!("│"), style);
|
||||||
|
buffer.set_string(area.x + area.width - 1, y, format!("│"), style);
|
||||||
|
}
|
||||||
|
buffer.set_string(area.x, area.y + area.height - 1, bottom, style);
|
||||||
|
area
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_box_styled_dotted (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
||||||
|
if area.width < 1 || area.height < 1 {
|
||||||
|
return area
|
||||||
|
}
|
||||||
|
let style = style.unwrap_or(Style::default());
|
||||||
|
let top = format!("╭{}╮", "┅".repeat((area.width - 2).into()));
|
||||||
|
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) {
|
||||||
|
buffer.set_string(area.x, y, format!("┇"), style);
|
||||||
|
buffer.set_string(area.x + area.width - 1, y, format!("┇"), style);
|
||||||
|
}
|
||||||
|
buffer.set_string(area.x, area.y + area.height - 1, bottom, style);
|
||||||
|
area
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ pub mod draw;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
pub mod port;
|
||||||
|
|
||||||
use crate::device::*;
|
use crate::device::*;
|
||||||
use crate::layout::*;
|
use crate::layout::*;
|
||||||
|
|
|
||||||
14
src/port.rs
Normal file
14
src/port.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub trait Ports {
|
||||||
|
fn audio_ins (&self) -> Usually<Vec<String>>;
|
||||||
|
fn audio_outs (&self) -> Usually<Vec<String>>;
|
||||||
|
fn midi_ins (&self) -> Usually<Vec<String>>;
|
||||||
|
fn midi_outs (&self) -> Usually<Vec<String>>;
|
||||||
|
fn connect (&mut self, connect: bool, source: &str, target: &str);
|
||||||
|
fn connect_all (&mut self, connections: &[(bool, &str, &str)]) {
|
||||||
|
for (connect, source, target) in connections.iter() {
|
||||||
|
self.connect(*connect, source, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,44 +1,15 @@
|
||||||
pub type Usually<T> = Result<T, Box<dyn Error>>;
|
pub type Usually<T> = Result<T, Box<dyn Error>>;
|
||||||
|
|
||||||
pub use crate::draw::*;
|
pub use crate::draw::*;
|
||||||
|
pub use crate::device::*;
|
||||||
pub use crate::device::{
|
|
||||||
Device,
|
|
||||||
DynamicDevice,
|
|
||||||
AppEvent,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use crate::time::*;
|
pub use crate::time::*;
|
||||||
|
pub use crate::port::*;
|
||||||
pub use crate::layout::{
|
pub use crate::layout::*;
|
||||||
Row,
|
|
||||||
Column,
|
|
||||||
FocusRow,
|
|
||||||
FocusColumn,
|
|
||||||
Focus,
|
|
||||||
FocusEvent,
|
|
||||||
handle_focus,
|
|
||||||
draw_box,
|
|
||||||
draw_box_styled,
|
|
||||||
draw_row,
|
|
||||||
draw_column,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use std::error::Error;
|
pub use std::error::Error;
|
||||||
|
pub use std::io::{stdout, Stdout, Write};
|
||||||
pub use std::io::{
|
pub use std::thread::{spawn, JoinHandle};
|
||||||
stdout,
|
|
||||||
Stdout,
|
|
||||||
Write
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use std::thread::{
|
|
||||||
spawn,
|
|
||||||
JoinHandle
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use std::time::Duration;
|
pub use std::time::Duration;
|
||||||
|
|
||||||
pub use std::sync::{
|
pub use std::sync::{
|
||||||
Arc,
|
Arc,
|
||||||
Mutex,
|
Mutex,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue