mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
fix keymap macros. rendering issue
This commit is contained in:
parent
6f51872856
commit
f3fd88a199
15 changed files with 303 additions and 180 deletions
|
|
@ -1,5 +1,6 @@
|
|||
use crate::*;
|
||||
use ClockCommand::{Play, Pause};
|
||||
use ArrangerCommand as Cmd;
|
||||
|
||||
#[derive(Clone, Debug)] pub enum ArrangerCommand {
|
||||
History(isize),
|
||||
|
|
@ -15,6 +16,7 @@ use ClockCommand::{Play, Pause};
|
|||
StopAll,
|
||||
Clear,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ArrangerTrackCommand {
|
||||
Add,
|
||||
|
|
@ -25,6 +27,7 @@ pub enum ArrangerTrackCommand {
|
|||
SetZoom(usize),
|
||||
SetColor(usize, ItemPalette),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ArrangerSceneCommand {
|
||||
Enqueue(usize),
|
||||
|
|
@ -35,6 +38,7 @@ pub enum ArrangerSceneCommand {
|
|||
SetZoom(usize),
|
||||
SetColor(usize, ItemPalette),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ArrangerClipCommand {
|
||||
Get(usize, usize),
|
||||
|
|
@ -45,7 +49,9 @@ pub enum ArrangerClipCommand {
|
|||
SetColor(usize, usize, ItemPalette),
|
||||
}
|
||||
|
||||
use ArrangerCommand as Cmd;
|
||||
//handle!(<Tui>|self: ArrangerTui, input|ArrangerCommand::execute_with_state(self, input.event()));
|
||||
//input_to_command!(ArrangerCommand: |state: ArrangerTui, input: Event|{KEYS_ARRANGER.handle(state, input)?});
|
||||
|
||||
keymap!(KEYS_ARRANGER = |state: ArrangerTui, input: Event| ArrangerCommand {
|
||||
key(Char('u')) => Cmd::History(-1),
|
||||
key(Char('U')) => Cmd::History(1),
|
||||
|
|
|
|||
40
src/arranger/arranger_keys.edn
Normal file
40
src/arranger/arranger_keys.edn
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
(def keys
|
||||
(u :undo 1)
|
||||
(shift-u :redo 1)
|
||||
(ctrl-k :todo "keyboard")
|
||||
(space :play-toggle)
|
||||
(shift-space :play-start-toggle)
|
||||
(e :editor-show :pool-phrase)
|
||||
(ctrl-a :scene-add)
|
||||
(ctrl-t :track-add)
|
||||
(tab :pool-toggle))
|
||||
|
||||
(def keys-clip
|
||||
(q :clip-launch)
|
||||
(c :clip-color)
|
||||
(g :clip-get)
|
||||
(p :clip-put)
|
||||
(del :clip-del)
|
||||
(, :clip-prev)
|
||||
(. :clip-next)
|
||||
(< :clip-swap-prev)
|
||||
(> :clip-swap-next)
|
||||
(l :clip-loop-toggle))
|
||||
|
||||
(def keys-scene
|
||||
(q :scene-launch)
|
||||
(c :scene-color)
|
||||
(, :scene-prev)
|
||||
(. :scene-next)
|
||||
(< :scene-swap-prev)
|
||||
(> :scene-swap-next)
|
||||
(del :scene-delete))
|
||||
|
||||
(def keys-track
|
||||
(q :track-launch)
|
||||
(c :track-color)
|
||||
(, :track-prev)
|
||||
(. :track-next)
|
||||
(< :track-swap-prev)
|
||||
(> :track-swap-next)
|
||||
(del :track-delete))
|
||||
|
|
@ -18,7 +18,7 @@ impl<'a> ArrangerVClips<'a> {
|
|||
}
|
||||
}
|
||||
impl<'a> Content<Tui> for ArrangerVClips<'a> {
|
||||
fn content (&self) -> impl Content<Tui> {
|
||||
fn content (&self) -> impl Render<Tui> {
|
||||
let iter = ||self.scenes.iter().zip(self.rows.iter().map(|row|row.0));
|
||||
let col = Map(iter, |(scene, pulses), i|Self::format_scene(self.tracks, scene, pulses));
|
||||
Fill::xy(col)
|
||||
|
|
|
|||
|
|
@ -2,25 +2,24 @@ use crate::*;
|
|||
|
||||
#[macro_export] macro_rules! keymap {
|
||||
(
|
||||
$KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty
|
||||
$(<$lt:lifetime>)? $KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty
|
||||
{ $($key:expr => $handler:expr),* $(,)? } $(,)?
|
||||
) => {
|
||||
pub const $KEYS: EventMap<'static, $State, $Input, $Command> = EventMap {
|
||||
fallback: None,
|
||||
bindings: &[ $(($key, &|$state|Some($handler)),)* ]
|
||||
};
|
||||
input_to_command!($Command: |state: $State, input: $Input|$KEYS.handle(state, input)?);
|
||||
input_to_command!($(<$lt>)? $Command: |$state: $State, input: $Input|$KEYS.handle($state, input)?);
|
||||
};
|
||||
(
|
||||
$KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty
|
||||
{ $($key:expr => $handler:expr),* $(,)? },
|
||||
$default:expr
|
||||
$(<$lt:lifetime>)? $KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty
|
||||
{ $($key:expr => $handler:expr),* $(,)? }, $default:expr
|
||||
) => {
|
||||
pub const $KEYS: EventMap<'static, $State, $Input, $Command> = EventMap {
|
||||
fallback: Some(&|$state, $input|Some($default)),
|
||||
bindings: &[ $(($key, &|$state|Some($handler)),)* ]
|
||||
};
|
||||
input_to_command!($Command: |state: $State, input: $Input|$KEYS.handle(state, input)?);
|
||||
input_to_command!($(<$lt>)? $Command: |$state: $State, input: $Input|$KEYS.handle($state, input)?);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -28,7 +27,6 @@ pub struct EventMap<'a, S, I: PartialEq, C> {
|
|||
pub bindings: &'a [(I, &'a dyn Fn(&S) -> Option<C>)],
|
||||
pub fallback: Option<&'a dyn Fn(&S, &I) -> Option<C>>
|
||||
}
|
||||
|
||||
impl<'a, S, I: PartialEq, C> EventMap<'a, S, I, C> {
|
||||
pub fn handle (&self, state: &S, input: &I) -> Option<C> {
|
||||
for (binding, handler) in self.bindings.iter() {
|
||||
|
|
@ -43,15 +41,20 @@ impl<'a, S, I: PartialEq, C> EventMap<'a, S, I, C> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Command<S>: Send + Sync + Sized {
|
||||
fn execute (self, state: &mut S) -> Perhaps<Self>;
|
||||
fn delegate <T> (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps<T> {
|
||||
Ok(self.execute(state)?.map(wrap))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! input_to_command {
|
||||
(<$($l:lifetime),+> $Command:ty: |$state:ident:$State:ty, $input:ident:$Input:ty| $handler:expr) => {
|
||||
impl<$($l),+> InputToCommand<$Input, $State> for $Command {
|
||||
fn input_to_command ($state: &$State, $input: &$Input) -> Option<Self> {
|
||||
Some($handler)
|
||||
}
|
||||
}
|
||||
};
|
||||
($Command:ty: |$state:ident:$State:ty, $input:ident:$Input:ty| $handler:expr) => {
|
||||
impl InputToCommand<$Input, $State> for $Command {
|
||||
fn input_to_command ($state: &$State, $input: &$Input) -> Option<Self> {
|
||||
|
|
@ -74,11 +77,11 @@ pub trait InputToCommand<I, S>: Command<S> + Sized {
|
|||
}
|
||||
|
||||
#[macro_export] macro_rules! command {
|
||||
(|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => {
|
||||
impl Command<$State> for $Command {
|
||||
($(<$($l:lifetime),+>)?|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => {
|
||||
impl$(<$($l),+>)? Command<$State> for $Command {
|
||||
fn execute ($self, $state: &mut $State) -> Perhaps<Self> {
|
||||
Ok($handler)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub struct Field<T, U>(pub ItemPalette, pub T, pub U)
|
|||
impl<T, U> Content<Tui> for Field<T, U>
|
||||
where T: AsRef<str> + Send + Sync, U: AsRef<str> + Send + Sync
|
||||
{
|
||||
fn content (&self) -> impl Content<Tui> {
|
||||
fn content (&self) -> impl Render<Tui> {
|
||||
let ItemPalette { darkest, dark, lighter, lightest, .. } = self.0;
|
||||
row!(
|
||||
Tui::fg_bg(dark.rgb, darkest.rgb, "▐"),
|
||||
|
|
@ -23,7 +23,7 @@ pub struct FieldV<T, U>(pub ItemPalette, pub T, pub U)
|
|||
impl<T, U> Content<Tui> for FieldV<T, U>
|
||||
where T: AsRef<str> + Send + Sync, U: AsRef<str> + Send + Sync
|
||||
{
|
||||
fn content (&self) -> impl Content<Tui> {
|
||||
fn content (&self) -> impl Render<Tui> {
|
||||
let ItemPalette { darkest, dark, lighter, lightest, .. } = self.0;
|
||||
let sep1 = Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▐"));
|
||||
let sep2 = Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▌"));
|
||||
|
|
|
|||
|
|
@ -58,16 +58,17 @@ impl<'a> Groovebox<'a> {
|
|||
note_buf: vec![],
|
||||
perf: PerfModel::default(),
|
||||
|
||||
view: EdnView::from(include_str!("groovebox/groovebox.edn")),
|
||||
view: EdnView::new(include_str!("groovebox/groovebox.edn"))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
has_clock!(|self: Groovebox|self.player.clock());
|
||||
has_clock!(|self: Groovebox<'a>|self.player.clock());
|
||||
|
||||
impl<'a> EdnLayout<'a, Tui> for Groovebox {
|
||||
fn get_bool (&self, item: &Item<&str>) -> bool { todo!() }
|
||||
fn get_unit (&self, item: &Item<&str>) -> u16 {
|
||||
impl<'a> EdnLayout<'a, Tui> for Groovebox<'a> {
|
||||
fn get_bool (&self, item: &EdnItem<&str>) -> bool { todo!() }
|
||||
fn get_unit (&self, item: &EdnItem<&str>) -> u16 {
|
||||
use EdnItem::*;
|
||||
match item {
|
||||
Sym(":sample-h") => if self.compact { 0 } else { 5 },
|
||||
Sym(":samples-w") => if self.compact { 4 } else { 11 },
|
||||
|
|
@ -79,7 +80,8 @@ impl<'a> EdnLayout<'a, Tui> for Groovebox {
|
|||
_ => 0
|
||||
}
|
||||
}
|
||||
fn get_content (&self, item: &Item<&str>) -> Box<dyn Render<Tui> + '_> {
|
||||
fn get_content (&'a self, item: &EdnItem<&str>) -> Box<EdnRender<'a, Tui>> {
|
||||
use EdnItem::*;
|
||||
match item {
|
||||
Sym(":input-meter-l") => Box::new(Meter("L/", self.sampler.input_meter[0])),
|
||||
Sym(":input-meter-r") => Box::new(Meter("R/", self.sampler.input_meter[1])),
|
||||
|
|
@ -103,3 +105,52 @@ impl<'a> EdnLayout<'a, Tui> for Groovebox {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Status bar for sequencer app
|
||||
#[derive(Clone)]
|
||||
pub struct GrooveboxStatus {
|
||||
pub(crate) width: usize,
|
||||
pub(crate) cpu: Option<String>,
|
||||
pub(crate) size: String,
|
||||
pub(crate) playing: bool,
|
||||
}
|
||||
from!(|state: &Groovebox<'_>|GrooveboxStatus = {
|
||||
let samples = state.clock().chunk.load(Relaxed);
|
||||
let rate = state.clock().timebase.sr.get();
|
||||
let buffer = samples as f64 / rate;
|
||||
let width = state.size.w();
|
||||
Self {
|
||||
width,
|
||||
playing: state.clock().is_rolling(),
|
||||
cpu: state.perf.percentage().map(|cpu|format!("│{cpu:.01}%")),
|
||||
size: format!("{}x{}│", width, state.size.h()),
|
||||
}
|
||||
});
|
||||
render!(Tui: (self: GrooveboxStatus) => Fixed::y(2, lay!(
|
||||
Self::help(),
|
||||
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
||||
)));
|
||||
impl GrooveboxStatus {
|
||||
fn help () -> impl Content<Tui> {
|
||||
let single = |binding, command|row!(" ", col!(
|
||||
Tui::fg(TuiTheme::yellow(), binding),
|
||||
command
|
||||
));
|
||||
let double = |(b1, c1), (b2, c2)|col!(
|
||||
row!(" ", Tui::fg(TuiTheme::yellow(), b1), " ", c1,),
|
||||
row!(" ", Tui::fg(TuiTheme::yellow(), b2), " ", c2,),
|
||||
);
|
||||
Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!(
|
||||
single("SPACE", "play/pause"),
|
||||
double(("▲▼▶◀", "cursor"), ("Ctrl", "scroll"), ),
|
||||
double(("a", "append"), ("s", "set note"),),
|
||||
double((",.", "length"), ("<>", "triplet"), ),
|
||||
double(("[]", "phrase"), ("{}", "order"), ),
|
||||
double(("q", "enqueue"), ("e", "edit"), ),
|
||||
double(("c", "color"), ("", ""),),
|
||||
))
|
||||
}
|
||||
fn stats (&self) -> impl Content<Tui> + use<'_> {
|
||||
row!(&self.cpu, &self.size)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use crate::*;
|
||||
use super::*;
|
||||
|
||||
|
||||
audio!(|self: Groovebox, client, scope|{
|
||||
audio!(|self: Groovebox<'a>, client, scope|{
|
||||
let t0 = self.perf.get_t0();
|
||||
if Control::Quit == ClockAudio(&mut self.player).process(client, scope) {
|
||||
return Control::Quit
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ pub enum GrooveboxCommand {
|
|||
Sampler(SamplerCommand),
|
||||
}
|
||||
|
||||
command!(|self: GrooveboxCommand, state: Groovebox|match self {
|
||||
command!(<'a>|self: GrooveboxCommand, state: Groovebox<'a>|match self {
|
||||
Self::Enqueue(phrase) => {
|
||||
state.player.enqueue_next(phrase.as_ref());
|
||||
None
|
||||
|
|
@ -46,8 +46,10 @@ command!(|self: GrooveboxCommand, state: Groovebox|match self {
|
|||
},
|
||||
});
|
||||
|
||||
handle!(<Tui>|self: Groovebox, input|GrooveboxCommand::execute_with_state(self, input.event()));
|
||||
keymap!(KEYS_GROOVEBOX = |state: Groovebox, input: Event| GrooveboxCommand {
|
||||
handle!(<Tui>|self: Groovebox<'static>, input|
|
||||
GrooveboxCommand::execute_with_state(self, input.event()));
|
||||
|
||||
keymap!(<'a> KEYS_GROOVEBOX = |state: Groovebox<'static>, input: Event| GrooveboxCommand {
|
||||
// Tab: Toggle compact mode
|
||||
key(Tab) => Cmd::Compact(!state.compact),
|
||||
// q: Enqueue currently edited phrase
|
||||
|
|
|
|||
|
|
@ -2,16 +2,16 @@ use crate::*;
|
|||
use super::*;
|
||||
use std::marker::ConstParamTy;
|
||||
use tek_engine::Render;
|
||||
use Item::*;
|
||||
use EdnItem::*;
|
||||
|
||||
render!(Tui: (self: Groovebox) => self.size.of(
|
||||
render!(Tui: (self: Groovebox<'a>) => self.size.of(
|
||||
Bsp::s(self.toolbar_view(),
|
||||
Bsp::n(self.selector_view(),
|
||||
Bsp::n(self.sample_view(),
|
||||
Bsp::n(self.status_view(),
|
||||
Bsp::w(self.pool_view(), Fill::xy(Bsp::e(self.sampler_view(), &self.editor)))))))));
|
||||
|
||||
impl Groovebox {
|
||||
impl<'a> Groovebox<'a> {
|
||||
fn toolbar_view (&self) -> impl Content<Tui> + use<'_> {
|
||||
Fill::x(Fixed::y(2, lay!(
|
||||
Align::w(Meter("L/", self.sampler.input_meter[0])),
|
||||
|
|
|
|||
|
|
@ -49,55 +49,6 @@ impl SequencerStatus {
|
|||
}
|
||||
}
|
||||
|
||||
/// Status bar for sequencer app
|
||||
#[derive(Clone)]
|
||||
pub struct GrooveboxStatus {
|
||||
pub(crate) width: usize,
|
||||
pub(crate) cpu: Option<String>,
|
||||
pub(crate) size: String,
|
||||
pub(crate) playing: bool,
|
||||
}
|
||||
from!(|state: &Groovebox|GrooveboxStatus = {
|
||||
let samples = state.clock().chunk.load(Relaxed);
|
||||
let rate = state.clock().timebase.sr.get();
|
||||
let buffer = samples as f64 / rate;
|
||||
let width = state.size.w();
|
||||
Self {
|
||||
width,
|
||||
playing: state.clock().is_rolling(),
|
||||
cpu: state.perf.percentage().map(|cpu|format!("│{cpu:.01}%")),
|
||||
size: format!("{}x{}│", width, state.size.h()),
|
||||
}
|
||||
});
|
||||
render!(Tui: (self: GrooveboxStatus) => Fixed::y(2, lay!(
|
||||
Self::help(),
|
||||
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
||||
)));
|
||||
impl GrooveboxStatus {
|
||||
fn help () -> impl Content<Tui> {
|
||||
let single = |binding, command|row!(" ", col!(
|
||||
Tui::fg(TuiTheme::yellow(), binding),
|
||||
command
|
||||
));
|
||||
let double = |(b1, c1), (b2, c2)|col!(
|
||||
row!(" ", Tui::fg(TuiTheme::yellow(), b1), " ", c1,),
|
||||
row!(" ", Tui::fg(TuiTheme::yellow(), b2), " ", c2,),
|
||||
);
|
||||
Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!(
|
||||
single("SPACE", "play/pause"),
|
||||
double(("▲▼▶◀", "cursor"), ("Ctrl", "scroll"), ),
|
||||
double(("a", "append"), ("s", "set note"),),
|
||||
double((",.", "length"), ("<>", "triplet"), ),
|
||||
double(("[]", "phrase"), ("{}", "order"), ),
|
||||
double(("q", "enqueue"), ("e", "edit"), ),
|
||||
double(("c", "color"), ("", ""),),
|
||||
))
|
||||
}
|
||||
fn stats (&self) -> impl Content<Tui> + use<'_> {
|
||||
row!(&self.cpu, &self.size)
|
||||
}
|
||||
}
|
||||
|
||||
/// Status bar for arranger app
|
||||
#[derive(Clone)]
|
||||
pub struct ArrangerStatus {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue