mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip: launcher grid
This commit is contained in:
parent
55e6c19c92
commit
1f194dafd8
5 changed files with 371 additions and 129 deletions
|
|
@ -1,38 +1,256 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
pub struct Launcher {
|
||||
name: String
|
||||
name: String,
|
||||
timebase: Arc<Timebase>,
|
||||
cursor: (usize, usize),
|
||||
tracks: Vec<DynamicDevice<Sequencer>>,
|
||||
chains: Vec<DynamicDevice<Chain>>,
|
||||
rows: usize,
|
||||
show_help: bool,
|
||||
}
|
||||
|
||||
impl Launcher {
|
||||
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||
pub fn new (name: &str, timebase: &Arc<Timebase>) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||
Ok(DynamicDevice::new(render, handle, process, Self {
|
||||
name: name.into(),
|
||||
name: name.into(),
|
||||
timebase: timebase.clone(),
|
||||
cursor: (0, 0),
|
||||
rows: 8,
|
||||
tracks: vec![
|
||||
Sequencer::new("Drum", timebase)?,
|
||||
Sequencer::new("Bass", timebase)?,
|
||||
Sequencer::new("Pads", timebase)?,
|
||||
Sequencer::new("Lead", timebase)?,
|
||||
],
|
||||
chains: vec![
|
||||
// TODO
|
||||
],
|
||||
show_help: true
|
||||
}))
|
||||
}
|
||||
fn cols (&self) -> usize {
|
||||
(self.tracks.len() + 1) as usize
|
||||
}
|
||||
fn col (&self) -> usize {
|
||||
self.cursor.0 as usize
|
||||
}
|
||||
fn dec_col (&mut self) {
|
||||
self.cursor.0 = if self.cursor.0 > 0 {
|
||||
self.cursor.0 - 1
|
||||
} else {
|
||||
(self.cols() - 1) as usize
|
||||
}
|
||||
}
|
||||
fn inc_col (&mut self) {
|
||||
self.cursor.0 = if self.cursor.0 >= self.cols() - 1 {
|
||||
0
|
||||
} else {
|
||||
self.cursor.0 + 1
|
||||
}
|
||||
}
|
||||
fn rows (&self) -> usize {
|
||||
(self.rows + 2) as usize
|
||||
}
|
||||
fn row (&self) -> usize {
|
||||
self.cursor.1 as usize
|
||||
}
|
||||
fn dec_row (&mut self) {
|
||||
self.cursor.1 = if self.cursor.1 > 0 {
|
||||
self.cursor.1 - 1
|
||||
} else {
|
||||
self.rows() - 1
|
||||
}
|
||||
}
|
||||
fn inc_row (&mut self) {
|
||||
self.cursor.1 = if self.cursor.1 >= self.rows() - 1 {
|
||||
0
|
||||
} else {
|
||||
self.cursor.1 + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DevicePorts for Launcher {}
|
||||
pub fn process (_: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
|
||||
pub fn render (_: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
for i in 1..=8 {
|
||||
buf.set_string(area.x + 2 + (i-1) * 10, area.y, format!("Track#{i} | "), Style::default())
|
||||
macro_rules! set {
|
||||
($buf:expr, $style:expr, $x: expr, $y: expr, $fmt: literal $(, $val:expr)*) => {
|
||||
$buf.set_string($x, $y, &format!($fmt $(, $val)*), $style)
|
||||
}
|
||||
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())
|
||||
}
|
||||
trait Blit {
|
||||
fn blit (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>);
|
||||
}
|
||||
impl<T: AsRef<str>> Blit for T {
|
||||
fn blit (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>) {
|
||||
buf.set_string(x, y, self.as_ref(), style.unwrap_or(Style::default()))
|
||||
}
|
||||
}
|
||||
pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let Rect { x, y, width, height } = area;
|
||||
let separator = format!("├{}┤", "-".repeat((width - 2).into()));
|
||||
let scenes = draw_launcher_scenes(state, buf, x, y);
|
||||
separator.blit(buf, x, y + 2, Some(Style::default().dim()));
|
||||
separator.blit(buf, x, y + 4, Some(Style::default().dim()));
|
||||
separator.blit(buf, x, y + 40, Some(Style::default().dim()));
|
||||
let (w, mut highlight) = draw_launcher_tracks(state, buf, x, y);
|
||||
if state.col() == 0 {
|
||||
highlight = Some(scenes);
|
||||
}
|
||||
draw_crossings(state, buf, x + w - 2, y);
|
||||
draw_sequencer(state, buf, x, y + 21, width, height - 22)?;
|
||||
draw_box(buf, area);
|
||||
draw_launcher_highlight(state, buf, &highlight);
|
||||
Ok(area)
|
||||
}
|
||||
|
||||
fn draw_launcher_scenes (
|
||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16,
|
||||
) -> Rect {
|
||||
let style = Style::default().not_dim().bold();
|
||||
let row = state.row() as u16;
|
||||
let col = state.col() as u16;
|
||||
let mut width = 8u16;
|
||||
let mut height = 6u16;
|
||||
format!("{} | ", state.name).blit(buf, x+2, y+1, Some(
|
||||
if row == 0 && col == 0 { Style::default().green() } else { Style::default() }
|
||||
));
|
||||
format!("Sync 1/1").blit(buf, x+2, y+3, Some(
|
||||
if row == 1 && col == 0 { Style::default().green() } else { Style::default() }
|
||||
));
|
||||
for j in 0..=7 {
|
||||
let y = y + 5 + j as u16 * 2;
|
||||
let label = format!("▶ Scene {j}");
|
||||
width = width.max(label.len() as u16 + 3);
|
||||
label.blit(buf, x + 2, y, Some(
|
||||
if row == j + 2 && col == 0 { Style::default().green() } else { Style::default() }
|
||||
));
|
||||
height = height + 2;
|
||||
}
|
||||
Rect { x, y, width, height }
|
||||
}
|
||||
|
||||
fn draw_launcher_tracks (
|
||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16
|
||||
) -> (u16, Option<Rect>) {
|
||||
let mut w = 15;
|
||||
let mut highlight = None;
|
||||
for (i, track) in state.tracks.iter().enumerate() {
|
||||
let track = track.state();
|
||||
draw_crossings(state, buf, x + w - 2, y);
|
||||
let width = draw_launcher_track(state, buf, x + w, y, i as u16, &track);
|
||||
if i + 1 == state.col() {
|
||||
highlight = Some(Rect { x: x + w - 2, y, width: width + 1, height: 22 });
|
||||
}
|
||||
w = w + width;
|
||||
}
|
||||
(w, highlight)
|
||||
}
|
||||
|
||||
fn draw_launcher_track (
|
||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, i: u16, track: &Sequencer
|
||||
) -> u16 {
|
||||
let mut width = track.name.len() as u16 + 3;
|
||||
let row = state.row() as u16;
|
||||
let col = state.col() as u16;
|
||||
|
||||
track.name.blit(buf, x, y + 1, Some(
|
||||
if row == 0 && col == i + 1 { Style::default().green() } else { Style::default() }
|
||||
));
|
||||
|
||||
"(global)".blit(buf, x, y + 3, Some(
|
||||
if row == 1 && col == i + 1 { Style::default().green() } else { Style::default().dim() }
|
||||
));
|
||||
|
||||
"┊".blit(buf, x - 2, y + 3, Some(
|
||||
Style::default().dim()
|
||||
));
|
||||
|
||||
for j in 0..=7 {
|
||||
let y = y + 5 + j as u16 * 2;
|
||||
if let Some(sequence) = track.sequences.get(j) {
|
||||
let label = format!("▶ {}", &sequence.name);
|
||||
width = width.max(label.len() as u16 + 1);
|
||||
let style = if j + 2 == row as usize && i + 1 == col {
|
||||
Style::default().green().bold()
|
||||
} else {
|
||||
Style::default()
|
||||
};
|
||||
buf.set_string(x - 0, y + 0, &label, style);
|
||||
buf.set_string(x - 2, y + 0, &"┊", style.dim());
|
||||
buf.set_string(x - 2, y + 1, &"┊", style.dim());
|
||||
buf.set_string(x + width - 2, y + 0, &"┊", style.dim());
|
||||
buf.set_string(x + width - 2, y + 1, &"┊", style.dim());
|
||||
} else {
|
||||
buf.set_string(x - 2, y as u16,
|
||||
format!("┊ {}", &"·".repeat(track.name.len())),
|
||||
Style::default().dim()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(draw_box(buf, Rect {
|
||||
x: area.x,
|
||||
y: area.y - 1,
|
||||
width: area.width,
|
||||
height: 12
|
||||
}))
|
||||
|
||||
width
|
||||
}
|
||||
|
||||
pub fn handle (_: &mut Launcher, _: &AppEvent) -> Usually<bool> {
|
||||
Ok(false)
|
||||
fn draw_crossings (state: &Launcher, buf: &mut Buffer, x: u16, y: u16) {
|
||||
"╭".blit(buf, x, y + 0, None);
|
||||
"┊".blit(buf, x, y + 1, Some(Style::default().dim()));
|
||||
"┼".blit(buf, x, y + 2, Some(Style::default().dim()));
|
||||
"┊".blit(buf, x, y + 3, Some(Style::default().dim()));
|
||||
"┼".blit(buf, x, y + 4, Some(Style::default().dim()));
|
||||
}
|
||||
|
||||
fn draw_sequencer (
|
||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, width: u16, height: u16
|
||||
) -> Usually<()> {
|
||||
if let Some(sequencer) = state.tracks.get(state.col().saturating_sub(1)) {
|
||||
crate::device::sequencer::horizontal::footer(
|
||||
&sequencer.state(), buf, 0, y, width, 0
|
||||
);
|
||||
crate::device::sequencer::horizontal::keys(
|
||||
&sequencer.state(), buf, Rect { x, y: y + 3, width, height }
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_launcher_highlight (
|
||||
state: &Launcher, buf: &mut Buffer, highlight: &Option<Rect>) {
|
||||
if let Some(area) = highlight {
|
||||
draw_box_styled(buf, *area, Some(Style::default().green().dim()));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle (state: &mut Launcher, event: &AppEvent) -> Usually<bool> {
|
||||
handle_keymap(state, event, KEYMAP)
|
||||
}
|
||||
pub const KEYMAP: &'static [KeyBinding<Launcher>] = keymap!(Launcher {
|
||||
[Up, NONE, "cursor_up", "move cursor up", cursor_up],
|
||||
[Down, NONE, "cursor_down", "move cursor down", cursor_down],
|
||||
[Left, NONE, "cursor_left", "move cursor left", cursor_left],
|
||||
[Right, NONE, "cursor_right", "move cursor right", cursor_right],
|
||||
[Char('r'), NONE, "rename", "rename current element", rename],
|
||||
[F(1), NONE, "toggle_help", "toggle help", toggle_help],
|
||||
});
|
||||
fn rename (_: &mut Launcher) -> Usually<bool> {
|
||||
Ok(true)
|
||||
}
|
||||
fn cursor_up (state: &mut Launcher) -> Usually<bool> {
|
||||
state.dec_row();
|
||||
Ok(true)
|
||||
}
|
||||
fn cursor_down (state: &mut Launcher) -> Usually<bool> {
|
||||
state.inc_row();
|
||||
Ok(true)
|
||||
}
|
||||
fn cursor_left (state: &mut Launcher) -> Usually<bool> {
|
||||
state.dec_col();
|
||||
Ok(true)
|
||||
}
|
||||
fn cursor_right (state: &mut Launcher) -> Usually<bool> {
|
||||
state.inc_col();
|
||||
Ok(true)
|
||||
}
|
||||
fn toggle_help (state: &mut Launcher) -> Usually<bool> {
|
||||
state.show_help = !state.show_help;
|
||||
Ok(true)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue