From c18aa2cbbd6f2b0cb36d363e927eee97dcdc4d63 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 21 Jun 2024 12:13:11 +0300 Subject: [PATCH] wip: component refactor --- src/device.rs | 2 + src/device/chain.rs | 20 +++- src/device/launcher.rs | 23 ++++- src/device/plugin.rs | 27 ++++-- src/device/sequencer.rs | 80 ++++++++++------ src/device/transport.rs | 6 +- src/layout.rs | 208 ---------------------------------------- src/layout/collect.rs | 0 src/layout/container.rs | 61 ++++++++++++ src/layout/focus.rs | 180 ++++++++++++++++++++++++++++++++++ src/layout/mod.rs | 6 ++ src/layout/scroll.rs | 7 ++ src/main.rs | 18 ++-- src/prelude.rs | 8 +- 14 files changed, 374 insertions(+), 272 deletions(-) delete mode 100644 src/layout.rs create mode 100644 src/layout/collect.rs create mode 100644 src/layout/container.rs create mode 100644 src/layout/focus.rs create mode 100644 src/layout/mod.rs create mode 100644 src/layout/scroll.rs diff --git a/src/device.rs b/src/device.rs index afa0b805..af5b48c9 100644 --- a/src/device.rs +++ b/src/device.rs @@ -7,6 +7,7 @@ mod sampler; mod mixer; mod looper; mod plugin; +mod launcher; pub use self::transport::Transport; pub use self::chain::Chain; @@ -15,6 +16,7 @@ pub use self::sampler::Sampler; pub use self::mixer::Mixer; pub use self::looper::Looper; pub use self::plugin::Plugin; +pub use self::launcher::Launcher; use crossterm::event; diff --git a/src/device/chain.rs b/src/device/chain.rs index 94eae8b7..33b2c9f5 100644 --- a/src/device/chain.rs +++ b/src/device/chain.rs @@ -67,6 +67,9 @@ pub fn render (state: &Chain, buf: &mut Buffer, area: Rect) } impl Focus for Chain { + fn unfocus (&mut self) { + self.focused = false + } fn focused (&self) -> Option<&Box> { match self.focused { true => self.items.get(self.focus), @@ -80,29 +83,36 @@ impl Focus for Chain { } } fn handle_focus (&mut self, event: &FocusEvent) -> Usually { - match event { + Ok(match event { FocusEvent::Backward => { if self.focus == 0 { self.focus = self.items.len(); } self.focus = self.focus - 1; + true }, FocusEvent::Forward => { self.focus = self.focus + 1; if self.focus >= self.items.len() { self.focus = 0; } + true }, FocusEvent::Inward => { self.focused = true; self.items[self.focus].handle(&AppEvent::Focus)?; + true }, FocusEvent::Outward => { - self.focused = false; - self.items[self.focus].handle(&AppEvent::Blur)?; + if self.focused { + self.focused = false; + self.items[self.focus].handle(&AppEvent::Blur)?; + true + } else { + false + } }, - }; - Ok(true) + }) } } diff --git a/src/device/launcher.rs b/src/device/launcher.rs index 2a217ae4..489d85da 100644 --- a/src/device/launcher.rs +++ b/src/device/launcher.rs @@ -6,7 +6,7 @@ pub struct Launcher { impl Launcher { pub fn new (name: &str) -> Result, Box> { - Ok(DynamicDevice::new(render, handle, Self { + Ok(DynamicDevice::new(render, handle, process, Self { name: name.into(), })) } @@ -16,10 +16,23 @@ pub fn process (_: &mut Launcher, _: &Client, _: &ProcessScope) -> Control { Control::Continue } -pub fn render (_: &Launcher, _: &mut Buffer, _: Rect) -> Usually { - Ok(Rect::default()) +pub fn render (_: &Launcher, buf: &mut Buffer, area: Rect) -> Usually { + for i in 1..=8 { + buf.set_string(area.x + 2 + (i-1) * 10, area.y, format!("Track#{i} | "), Style::default()) + } + for i in 0..=7 { + for j in 0..=7 { + buf.set_string(area.x + 2 + i * 10, area.y + 2 + j, format!("······· | "), Style::default().dim()) + } + } + Ok(draw_box(buf, Rect { + x: area.x, + y: area.y - 1, + width: area.width, + height: 12 + })) } -pub fn handle (_: &mut Launcher, _: &AppEvent) -> Result<(), Box> { - Ok(()) +pub fn handle (_: &mut Launcher, _: &AppEvent) -> Usually { + Ok(false) } diff --git a/src/device/plugin.rs b/src/device/plugin.rs index daeb9324..fae54cab 100644 --- a/src/device/plugin.rs +++ b/src/device/plugin.rs @@ -7,7 +7,8 @@ mod vst3; pub struct Plugin { name: String, path: String, - plugin: Option + plugin: Option, + parameter_offset: usize, } enum PluginKind { @@ -24,6 +25,7 @@ impl Plugin { name: name.into(), path: HELM.into(), plugin: None, + parameter_offset: 0, }); device.state.lock().unwrap().plugin = Some(self::lv2::plug_in(HELM)?); Ok(device) @@ -40,13 +42,13 @@ pub fn render (state: &Plugin, buf: &mut Buffer, Rect { x, y, .. }: Rect) -> Usually { let style = Style::default().gray(); - buf.set_string(x + 1, y + 1, &format!(" {}", state.name), style.white().bold()); + buf.set_string(x + 1, y + 1, &format!(" {}", state.name), style.white().bold()); buf.set_string(x + 13, y + 1, &format!("│ ...{}...", &HELM[13..30]), style.not_dim()); - buf.set_string(x + 0, y + 2, &format!("├--------------------------------------┤"), style.dim()); + buf.set_string(x + 0, y + 2, &format!("├--------------------------------------┤"), style.dim()); match &state.plugin { Some(PluginKind::LV2(ports, instance)) => { let mut height = 3; - for (i, port) in ports.iter().enumerate() { + for (i, port) in ports.iter().skip(state.parameter_offset).enumerate() { if i >= 10 { break } @@ -56,7 +58,7 @@ pub fn render (state: &Plugin, buf: &mut Buffer, Rect { x, y, .. }: Rect) ), Style::default()); height = height + 1; } - Ok(draw_box(buf, Rect { x, y, width: 40, height })) + Ok(draw_box(buf, Rect { x, y, width: 40, height: height.max(10) })) }, _ => { buf.set_string(x + 1, y + 3, &format!(" Parameter 1 0.0"), style); @@ -68,6 +70,17 @@ pub fn render (state: &Plugin, buf: &mut Buffer, Rect { x, y, .. }: Rect) } } -pub fn handle (_: &mut Plugin, _: &AppEvent) -> Usually { - Ok(false) +pub fn handle (s: &mut Plugin, event: &AppEvent) -> Usually { + handle_keymap(s, event, keymap!(Plugin { + [Up, NONE, "cursor_up", "move cursor up", + |s: &mut Plugin|{ + s.parameter_offset = s.parameter_offset.saturating_sub(1); + Ok(true) + }], + [Down, NONE, "cursor_down", "move cursor down", + |s: &mut Plugin|{ + s.parameter_offset = s.parameter_offset + 1; + Ok(true) + }] + })) } diff --git a/src/device/sequencer.rs b/src/device/sequencer.rs index 431a0053..770b8e1a 100644 --- a/src/device/sequencer.rs +++ b/src/device/sequencer.rs @@ -76,7 +76,7 @@ impl Sequencer { output_port: client.register_port("out", MidiOut::default())?, output_connect: vec![], - mode: SequencerView::Vertical, + mode: SequencerView::Horizontal, note_axis: (36, 68), note_cursor: 0, time_axis: (0, 64), @@ -167,7 +167,7 @@ fn process_out (s: &mut Sequencer, scope: &ProcessScope) { } } -fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually { +fn render (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually { let Rect { x, y, width, .. } = area; let (time0, time1) = s.time_axis; let (note0, note1) = s.note_axis; @@ -199,7 +199,7 @@ fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually { x, y, width: header.width.max(piano.width), - height: header.height + piano.height + 3 + height: header.height + piano.height })) } @@ -210,42 +210,55 @@ fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usu let steps = s.steps % s.resolution; let Rect { x, y, .. } = area; let style = Style::default().gray(); - buf.set_string(x + 1, y + 1, - &format!(" │ {rep}.{step:2} / {reps}.{steps}"), - style); buf.set_string(x + 2, y + 1, - &format!("{}", &s.name), - style.white().bold()); - buf.set_string(x + 1, y + 2, - &format!(" ▶ PLAY │ ⏹ STOP │ │"), + &format!("{rep}.{step:2} / {reps}.{steps}"), style); buf.set_string(x + 2, y + 2, - &format!("▶ PLAY"), if s.playing { - Style::default().green() - } else { - Style::default().dim() - }); - buf.set_string(x + 24, y + 2, + &format!("⏹ STOP"), + style); + //buf.set_string(x + 2, y + 2, + //&format!("▶ PLAY"), if s.playing { + //Style::default().green() + //} else { + //Style::default().dim() + //}); + buf.set_string(x + 10, y + 2, &format!("⏺ REC"), if s.recording { Style::default().red() } else { Style::default().dim() }); - buf.set_string(x + 32, y + 2, + buf.set_string(x + 17, y + 2, &format!("⏺ DUB"), if s.overdub { Style::default().yellow() } else { Style::default().dim() }); - Ok(Rect { x, y, width: 39, height: 4 }) + let clips = draw_clips(s, buf, area)?; + Ok(Rect { x, y, width: area.width, height: 4 }) +} + +fn draw_clips (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually { + let Rect { x, y, .. } = area; + let style = Style::default().gray(); + buf.set_string(x + 2, y + 4, &format!("▶ {}", &s.name), style.white().bold()); + buf.set_string(x + 2, y + 6, &format!("⏺ {}", &s.name), style.dim()); + buf.set_string(x + 2, y + 8, &format!("⏺ {}", &s.name), style.dim()); + buf.set_string(x + 2, y + 10, &format!("⏺ {}", &s.name), style.dim()); + buf.set_string(x + 2, y + 12, &format!("⏺ {}", &s.name), style.dim()); + buf.set_string(x + 2, y + 14, &format!("⏺ {}", &s.name), style.dim()); + buf.set_string(x + 2, y + 16, &format!("⏺ {}", &s.name), style.dim()); + buf.set_string(x + 2, y + 18, &format!("⏺ {}", &s.name), style.dim()); + Ok(Rect { x, y, width: 14, height: 14 }) } const KEYS_VERTICAL: [&'static str; 6] = [ "▀", "▀", "▀", "█", "▄", "▄", ]; -fn draw_vertical (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usually { +fn draw_vertical (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize) -> Usually { let ppq = s.timebase.ppq() as u32; + area.x = area.x + 13; let Rect { x, y, .. } = area; let (time0, time1) = s.time_axis; let (note0, note1) = s.note_axis; @@ -369,7 +382,8 @@ const KEY_HORIZONTAL_STYLE: [Style;12] = [ KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE, ]; -fn draw_horizontal (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually { +fn draw_horizontal (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually { + area.x = area.x + 13; let Rect { x, y, .. } = area; let (time0, time1) = s.time_axis; let (note0, note1) = s.note_axis; @@ -377,9 +391,9 @@ fn draw_horizontal (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually Usually Usually Usually { @@ -495,7 +514,7 @@ fn cursor_up (s: &mut Sequencer) -> Usually { match s.mode { SequencerView::Vertical => time_cursor_dec(s), SequencerView::Horizontal => note_cursor_dec(s), - _ => unimplemented!() + _ => Ok(false) }; Ok(true) } @@ -503,7 +522,7 @@ fn cursor_down (s: &mut Sequencer) -> Usually { match s.mode { SequencerView::Vertical => time_cursor_inc(s), SequencerView::Horizontal => note_cursor_inc(s), - _ => unimplemented!() + _ => Ok(false) }; Ok(true) } @@ -511,7 +530,7 @@ fn cursor_left (s: &mut Sequencer) -> Usually { match s.mode { SequencerView::Vertical => note_cursor_dec(s), SequencerView::Horizontal => time_cursor_dec(s), - _ => unimplemented!() + _ => Ok(false) }; Ok(true) } @@ -519,7 +538,7 @@ fn cursor_right (s: &mut Sequencer) -> Usually { match s.mode { SequencerView::Vertical => note_cursor_inc(s), SequencerView::Horizontal => time_cursor_inc(s), - _ => unimplemented!() + _ => Ok(false) }; Ok(true) } @@ -539,7 +558,8 @@ impl SequencerView { fn next (&self) -> Self { match self { Self::Horizontal => Self::Vertical, - Self::Vertical => Self::Horizontal, + Self::Vertical => Self::Tiny, + Self::Tiny => Self::Horizontal, _ => self.clone() } } diff --git a/src/device/transport.rs b/src/device/transport.rs index e0a50cbe..017ba08d 100644 --- a/src/device/transport.rs +++ b/src/device/transport.rs @@ -72,12 +72,12 @@ pub fn render (state: &Transport, buf: &mut Buffer, mut area: Rect) "STOP", "REC", "DUB", - "0.0.00", - "0:00.000", &format!("BPM {:03}.{:03}", state.timebase.tempo() / 1000, state.timebase.tempo() % 1000, - ) + ), + "0.0+00", + "0:00.000", ].iter() { buf.set_string(area.x + x, area.y + 1, button, label); x = x + button.len() as u16 + 1; diff --git a/src/layout.rs b/src/layout.rs deleted file mode 100644 index 315aa2d6..00000000 --- a/src/layout.rs +++ /dev/null @@ -1,208 +0,0 @@ -use crate::prelude::*; - -pub trait Focus { - fn focused (&self) -> Option<&Box>; - fn focused_mut (&mut self) -> Option<&mut Box>; - fn handle_focus (&mut self, event: &FocusEvent) -> Usually; -} - -pub enum FocusEvent { - Forward, - Backward, - Inward, - Outward, -} - -pub fn handle_focus ( - state: &mut T, - event: &AppEvent, - keymap: &[KeyBinding] -) -> Usually { - let handled = if let Some(focused) = state.focused_mut() { - focused.handle(event) - } else { - Ok(false) - }; - return Ok(handled? || handle_keymap( - state, event, keymap - )?) -} - -pub struct Rows { - focused: bool, - focus: usize, - items: Vec>, -} - -pub struct Columns { - focused: bool, - focus: usize, - items: Vec>, -} - -impl Rows { - pub fn new (focused: bool, items: Vec>) -> Self { - Self { focused, focus: 0, items } - } -} - -impl Columns { - pub fn new (focused: bool, items: Vec>) -> Self { - Self { focused, focus: 0, items } - } -} - -impl Focus for Rows { - fn focused (&self) -> Option<&Box> { - match self.focused { - true => self.items.get(self.focus), - false => None - } - } - fn focused_mut (&mut self) -> Option<&mut Box> { - match self.focused { - true => self.items.get_mut(self.focus), - false => None - } - } - fn handle_focus (&mut self, event: &FocusEvent) -> Usually { - match event { - FocusEvent::Backward => { - if self.focus == 0 { - self.focus = self.items.len(); - } - self.focus = self.focus - 1; - }, - FocusEvent::Forward => { - self.focus = self.focus + 1; - if self.focus >= self.items.len() { - self.focus = 0; - } - }, - FocusEvent::Inward => { - self.focused = true; - self.items[self.focus].handle(&AppEvent::Focus)?; - }, - FocusEvent::Outward => { - self.focused = false; - self.items[self.focus].handle(&AppEvent::Blur)?; - }, - }; - Ok(true) - } -} - -impl Focus for Columns { - fn focused (&self) -> Option<&Box> { - match self.focused { - true => self.items.get(self.focus), - false => None - } - } - fn focused_mut (&mut self) -> Option<&mut Box> { - match self.focused { - true => self.items.get_mut(self.focus), - false => None - } - } - fn handle_focus (&mut self, event: &FocusEvent) -> Usually { - match event { - FocusEvent::Backward => { - if self.focus == 0 { - self.focus = self.items.len(); - } - self.focus = self.focus - 1; - }, - FocusEvent::Forward => { - self.focus = self.focus + 1; - if self.focus >= self.items.len() { - self.focus = 0; - } - }, - FocusEvent::Inward => { - self.focused = true; - self.items[self.focus].handle(&AppEvent::Focus)?; - }, - FocusEvent::Outward => { - self.focused = false; - self.items[self.focus].handle(&AppEvent::Blur)?; - }, - }; - Ok(true) - } -} - -impl Device for Rows { - fn handle (&mut self, event: &AppEvent) -> Usually { - handle_focus(self, event, keymap!(Self { - [Up, NONE, "focus_up", "focus row above", - |s: &mut Self|s.handle_focus(&FocusEvent::Backward)], - [Down, NONE, "focus_down", "focus row below", - |s: &mut Self|s.handle_focus(&FocusEvent::Forward)], - [Enter, NONE, "focus_down", "focus row below", - |s: &mut Self|s.handle_focus(&FocusEvent::Inward)], - [Esc, NONE, "focus_down", "focus row below", - |s: &mut Self|s.handle_focus(&FocusEvent::Outward)] - })) - } - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { - let mut w = 0u16; - let mut h = 0u16; - for (i, device) in self.items.iter().enumerate() { - let result = device.render(buf, Rect { - x: area.x, - y: area.y + h, - width: area.width, - height: area.height - h - })?; - if i == self.focus { - if self.focused { - draw_box_styled(buf, result, Some(Style::default().green().not_dim())) - } else { - draw_box_styled_dotted(buf, result, Some(Style::default().green().dim())) - }; - }; - w = w.max(result.width); - h = h + result.height; - } - Ok(Rect { x: area.x, y: area.y, width: w, height: h }) - } -} - -impl Device for Columns { - fn handle (&mut self, event: &AppEvent) -> Usually { - handle_focus(self, event, keymap!(Self { - [Left, NONE, "focus_up", "focus row above", - |s: &mut Self|s.handle_focus(&FocusEvent::Backward)], - [Right, NONE, "focus_down", "focus row below", - |s: &mut Self|s.handle_focus(&FocusEvent::Forward)], - [Enter, NONE, "focus_down", "focus row below", - |s: &mut Self|s.handle_focus(&FocusEvent::Inward)], - [Esc, NONE, "focus_down", "focus row below", - |s: &mut Self|s.handle_focus(&FocusEvent::Outward)] - })) - } - - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { - let mut w = 0u16; - let mut h = 0u16; - for (i, device) in self.items.iter().enumerate() { - let result = device.render(buf, Rect { - x: area.x + w, - y: area.y, - width: area.width - w, - height: area.height - })?; - if i == self.focus { - if self.focused { - draw_box_styled(buf, result, Some(Style::default().green().not_dim())) - } else { - draw_box_styled_dotted(buf, result, Some(Style::default().green().dim())) - }; - }; - w = w + result.width; - h = h.max(result.height); - } - Ok(Rect { x: area.x, y: area.y, width: w, height: h }) - } -} diff --git a/src/layout/collect.rs b/src/layout/collect.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/layout/container.rs b/src/layout/container.rs new file mode 100644 index 00000000..cb0e0d24 --- /dev/null +++ b/src/layout/container.rs @@ -0,0 +1,61 @@ +use crate::prelude::*; + +pub struct Column(pub Vec>); + +impl Column { + pub fn new (items: Vec>) -> Self { + Self(items) + } +} + +pub struct Row(pub Vec>); + +impl Row { + pub fn new (items: Vec>) -> Self { + Self(items) + } +} + +impl Device for Column { + fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { + Ok(render_column(&self.0, buf, area)?.0) + } +} + +impl Device for Row { + fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { + Ok(render_row(&self.0, buf, area)?.0) + } +} + +pub fn render_column (items: &[Box], buf: &mut Buffer, area: Rect) + -> Usually<(Rect, Vec)> +{ + let mut w = 0u16; + let mut h = 0u16; + let mut rects = vec![]; + for (i, device) in items.iter().enumerate() { + let rect = Rect { x: area.x, y: area.y + h, 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 render_row (items: &[Box], buf: &mut Buffer, area: Rect) + -> Usually<(Rect, Vec)> +{ + let mut w = 0u16; + let mut h = 0u16; + let mut rects = vec![]; + for (i, device) in items.iter().enumerate() { + let rect = Rect { x: area.x + w, 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)) +} diff --git a/src/layout/focus.rs b/src/layout/focus.rs new file mode 100644 index 00000000..aeb84001 --- /dev/null +++ b/src/layout/focus.rs @@ -0,0 +1,180 @@ +use crate::prelude::*; + +pub trait Focus { + fn unfocus (&mut self); + fn focused (&self) -> Option<&Box>; + fn focused_mut (&mut self) -> Option<&mut Box>; + fn handle_focus (&mut self, event: &FocusEvent) -> Usually; +} + +pub enum FocusEvent { Forward, Backward, Inward, Outward, } + +pub fn handle_focus ( + state: &mut T, + event: &AppEvent, + keymap: &[KeyBinding] +) -> Usually { + let handled = if let Some(focused) = state.focused_mut() { + focused.handle(event) + } else { + Ok(false) + }; + return Ok(handled? || handle_keymap( + state, event, keymap + )?) +} + +pub struct FocusColumn(pub Option, pub Column); + +impl Device for FocusColumn { + fn handle (&mut self, event: &AppEvent) -> Usually { + handle_focus(self, event, KEYMAP_FOCUS_COLUMN) + } + fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { + let (rect, rects) = super::render_column(self.1.0.as_ref(), buf, area, )?; + //if i == self.focus { + //if self.focused { + //draw_box_styled(buf, result, Some(Style::default().white().not_dim())) + //} else { + //draw_box_styled_dotted(buf, result, Some(Style::default().white().dim())) + //}; + //}; + Ok(rect) + } +} + +const KEYMAP_FOCUS_COLUMN: &'static [KeyBinding] = keymap!(FocusColumn { + [Up, NONE, "focus_up", "focus row above", + |s: &mut FocusColumn|s.handle_focus(&FocusEvent::Backward)], + [Down, NONE, "focus_down", "focus row below", + |s: &mut FocusColumn|s.handle_focus(&FocusEvent::Forward)], + [Enter, NONE, "focus_down", "focus row below", + |s: &mut FocusColumn|s.handle_focus(&FocusEvent::Inward)], + [Esc, NONE, "focus_down", "focus row below", + |s: &mut FocusColumn|s.handle_focus(&FocusEvent::Outward)] +}); + +impl Focus for FocusColumn { + fn unfocus (&mut self) { + self.0 = None + } + fn focused (&self) -> Option<&Box> { + self.0.map(|index|self.1.0.get(index))? + } + fn focused_mut (&mut self) -> Option<&mut Box> { + self.0.map(|index|self.1.0.get_mut(index))? + } + fn handle_focus (&mut self, event: &FocusEvent) -> Usually { + Ok(match event { + FocusEvent::Backward => match self.0 { + Some(i) => { + self.0 = Some(if i == 0 { + self.1.0.len() - 1 + } else { + i - 1 + }); + true + }, + _ => false + }, + FocusEvent::Forward => match self.0 { + Some(i) => { + self.0 = Some(if i >= self.1.0.len() { + 0 + } else { + i + 1 + }); + true + }, + _ => false + }, + FocusEvent::Inward => match self.0 { + None => { + self.0 = Some(0); + true + }, + _ => false + }, + FocusEvent::Outward => match self.0 { + Some(i) => { + self.0 = None; + true + }, + _ => false + }, + }) + } +} + +pub struct FocusRow(pub Option, pub Row); + +impl Device for FocusRow { + fn handle (&mut self, event: &AppEvent) -> Usually { + handle_focus(self, event, keymap!(Self { + [Left, NONE, "focus_up", "focus row above", + |s: &mut Self|s.handle_focus(&FocusEvent::Backward)], + [Right, NONE, "focus_down", "focus row below", + |s: &mut Self|s.handle_focus(&FocusEvent::Forward)], + [Enter, NONE, "focus_down", "focus row below", + |s: &mut Self|s.handle_focus(&FocusEvent::Inward)], + [Esc, NONE, "focus_down", "focus row below", + |s: &mut Self|s.handle_focus(&FocusEvent::Outward)] + })) + } + fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { + let (rect, rects) = super::render_row(&self.1.0, buf, area)?; + Ok(rect) + } +} + +impl Focus for FocusRow { + fn unfocus (&mut self) { + self.0 = None + } + fn focused (&self) -> Option<&Box> { + self.0.map(|index|self.1.0.get(index))? + } + fn focused_mut (&mut self) -> Option<&mut Box> { + self.0.map(|index|self.1.0.get_mut(index))? + } + fn handle_focus (&mut self, event: &FocusEvent) -> Usually { + Ok(match event { + FocusEvent::Backward => match self.0 { + Some(i) => { + self.0 = Some(if i == 0 { + self.1.0.len() - 1 + } else { + i - 1 + }); + true + }, + _ => false + }, + FocusEvent::Forward => match self.0 { + Some(i) => { + self.0 = Some(if i >= self.1.0.len() { + 0 + } else { + i + 1 + }); + true + }, + _ => false + }, + FocusEvent::Inward => match self.0 { + None => { + self.0 = Some(0); + true + }, + _ => false + }, + FocusEvent::Outward => match self.0 { + Some(i) => { + self.0 = None; + true + }, + _ => false + }, + }) + } +} diff --git a/src/layout/mod.rs b/src/layout/mod.rs new file mode 100644 index 00000000..0c059f14 --- /dev/null +++ b/src/layout/mod.rs @@ -0,0 +1,6 @@ +mod focus; +pub use focus::*; +mod container; +pub use container::*; +mod scroll; +pub use scroll::*; diff --git a/src/layout/scroll.rs b/src/layout/scroll.rs new file mode 100644 index 00000000..8acb7a91 --- /dev/null +++ b/src/layout/scroll.rs @@ -0,0 +1,7 @@ +use crate::prelude::*; + +pub struct ScrollY; + +pub struct ScrollX; + +pub struct ScrollXY; diff --git a/src/main.rs b/src/main.rs index 5bf6d392..8a461331 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,16 +23,12 @@ fn main () -> Result<(), Box> { //crate::device::run(Sequencer::new("Rhythm#000")?) let transport = Transport::new("Transport")?; let timebase = transport.state.lock().unwrap().timebase(); - crate::device::run(Rows::new(true, vec![ - Box::new(transport), - Box::new(Columns::new(false, vec![ - Box::new(Chain::new("Chain#0000", vec![ - Box::new(Sequencer::new("Melody#000", &timebase)?), - Box::new(Plugin::new("Plugin#000")?), - ])?), - Box::new(Sequencer::new("Melody#001", &timebase)?), - Box::new(Sequencer::new("Rhythm#000", &timebase)?), - ])), + crate::device::run(FocusColumn(Some(0), Column::new(vec![ + Box::new(transport) as Box, + Box::new(Chain::new("Chain#0000", vec![ + Box::new(Sequencer::new("Melody#000", &timebase)?), + Box::new(Plugin::new("Plugin#000")?), + ])?), //Box::new(Columns::new(false, vec![ //Box::new(Chain::new("Chain#00", vec![ //Box::new(Sequencer::new("Rhythm#000")?), @@ -45,5 +41,5 @@ fn main () -> Result<(), Box> { //])), //Box::new(Mixer::new("Mixer#000")?), //Box::new(Sequencer::new("Rhythm#000")?), - ])) + ]))) } diff --git a/src/prelude.rs b/src/prelude.rs index 3b137cbe..1b4bbbad 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,8 +11,10 @@ pub use crate::device::{ pub use crate::time::*; pub use crate::layout::{ - Rows, - Columns, + Row, + Column, + FocusRow, + FocusColumn, Focus, FocusEvent, handle_focus @@ -106,7 +108,7 @@ pub type KeyBinding = ( } #[macro_export] macro_rules! keymap { - ($T:ty { $([$k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr]),* }) => { + ($T:ty { $([$k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr]),* $(,)? }) => { &[ $((KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as KeyHandler<$T>)),* ] as &'static [KeyBinding<$T>]