mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 03:36:41 +01:00
parent
08730df042
commit
0d7998a5a8
5 changed files with 389 additions and 279 deletions
3
Justfile
3
Justfile
|
|
@ -1,4 +1,5 @@
|
||||||
export RUSTFLAGS := "--cfg procmacro2_semver_exempt -Zmacro-backtrace"
|
export RUSTFLAGS := "--cfg procmacro2_semver_exempt -Zmacro-backtrace"
|
||||||
|
export RUST_BACKTRACE := "1"
|
||||||
|
|
||||||
debug := "reset && cargo run --"
|
debug := "reset && cargo run --"
|
||||||
release := "reset && cargo run --release --"
|
release := "reset && cargo run --release --"
|
||||||
|
|
@ -51,6 +52,8 @@ ftpush:
|
||||||
|
|
||||||
run:
|
run:
|
||||||
{{debug}}
|
{{debug}}
|
||||||
|
run-init:
|
||||||
|
rm -rf ~/.config/tek && {{debug}}
|
||||||
release:
|
release:
|
||||||
{{release}}
|
{{release}}
|
||||||
build-release:
|
build-release:
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,8 @@ pub struct App {
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub dirs: BaseDirectories,
|
pub dirs: BaseDirectories,
|
||||||
pub modes: Arc<RwLock<BTreeMap<Arc<str>, Arc<Mode<Arc<str>>>>>>,
|
pub modes: Arc<RwLock<BTreeMap<Arc<str>, Arc<Mode<Arc<str>>>>>>,
|
||||||
pub binds: Arc<RwLock<BTreeMap<Arc<str>, EventMap<Option<TuiEvent>, Arc<str>>>>>,
|
pub views: Arc<RwLock<BTreeMap<Arc<str>, Arc<str>>>>,
|
||||||
|
pub binds: Arc<RwLock<BTreeMap<Arc<str>, EventMap<TuiEvent, Arc<str>>>>>,
|
||||||
}
|
}
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Mode<D: Dsl + Ord> {
|
pub struct Mode<D: Dsl + Ord> {
|
||||||
|
|
@ -91,7 +92,7 @@ impl Config {
|
||||||
pub fn init () -> Usually<Self> {
|
pub fn init () -> Usually<Self> {
|
||||||
let mut cfgs: Self = Default::default();
|
let mut cfgs: Self = Default::default();
|
||||||
cfgs.dirs = BaseDirectories::with_profile("tek", "v0");
|
cfgs.dirs = BaseDirectories::with_profile("tek", "v0");
|
||||||
cfgs.init_file(Self::CONFIG, Self::DEFAULTS, |cfgs, dsl|cfgs.load_defs(dsl))?;
|
cfgs.init_file(Self::CONFIG, Self::DEFAULTS, |cfgs, dsl|cfgs.load(dsl))?;
|
||||||
Ok(cfgs)
|
Ok(cfgs)
|
||||||
}
|
}
|
||||||
pub fn init_file (
|
pub fn init_file (
|
||||||
|
|
@ -109,61 +110,76 @@ impl Config {
|
||||||
return Err(format!("{path}: not found").into())
|
return Err(format!("{path}: not found").into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn load_defs (&mut self, dsl: impl Dsl) -> Usually<()> {
|
pub fn load (&mut self, dsl: impl Dsl) -> Usually<()> {
|
||||||
dsl.each(|item|{
|
dsl.each(|item|if let Some(expr) = item.expr()? {
|
||||||
match item.exp().head() {
|
let head = expr.head()?;
|
||||||
Ok(Some("keys")) if let Some(id) = item.exp().tail().head()? =>
|
let tail = expr.tail()?;
|
||||||
self.load_bind(id.into(), item),
|
let name = tail.head()?;
|
||||||
Ok(Some("mode")) if let Some(id) = item.exp().tail().head()? =>
|
let body = tail.tail()?;
|
||||||
self.load_mode(id.into(), item),
|
println!("{} {} {}", head.unwrap_or_default(), name.unwrap_or_default(), body.unwrap_or_default());
|
||||||
_ => return Err(format!("load_defs: unexpected: {item:?}").into())
|
match head {
|
||||||
|
Some("view") if let Some(name) = name => self.load_view(name.into(), body),
|
||||||
|
Some("keys") if let Some(name) = name => self.load_bind(name.into(), body),
|
||||||
|
Some("mode") if let Some(name) = name => self.load_mode(name.into(), body),
|
||||||
|
_ => return Err(format!("Config::load: expected view/keys/mode, got: {item:?}").into())
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return Err(format!("Config::load: expected expr, got: {item:?}").into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn load_bind (&mut self, id: Arc<str>, item: impl Dsl) -> Usually<()> {
|
pub fn load_view (&mut self, id: Arc<str>, dsl: impl Dsl) -> Usually<()> {
|
||||||
let mut map = EventMap::new();
|
self.views.write().unwrap().insert(id, dsl.src()?.unwrap_or_default().into());
|
||||||
item.exp().tail().tail()?.each(|item|Self::load_bind_one(&mut map, item))?;
|
|
||||||
self.binds.write().unwrap().insert(id, map);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn load_bind_one (map: &mut EventMap<Option<TuiEvent>, Arc<str>>, item: impl Dsl) -> Usually<()> {
|
pub fn load_bind (&mut self, id: Arc<str>, dsl: impl Dsl) -> Usually<()> {
|
||||||
if let Ok(Some(sym)) = item.exp().head().sym() {
|
let mut map = EventMap::new();
|
||||||
map.add(TuiEvent::from_dsl(item.exp()?.head()?)?, Binding {
|
dsl.each(|item|if item.expr().head() == Ok(Some("see")) {
|
||||||
command: item.exp()?.tail()?.unwrap_or_default().into(),
|
// TODO
|
||||||
|
Ok(())
|
||||||
|
} else if let Ok(Some(word)) = item.expr().head().word() {
|
||||||
|
if let Some(key) = TuiEvent::from_dsl(item.expr()?.head()?)? {
|
||||||
|
map.add(key, Binding {
|
||||||
|
commands: [item.expr()?.tail()?.unwrap_or_default().into()].into(),
|
||||||
condition: None,
|
condition: None,
|
||||||
description: None,
|
description: None,
|
||||||
source: None
|
source: None
|
||||||
});
|
});
|
||||||
} else if item.exp().head() == Ok(Some("see")) {
|
Ok(())
|
||||||
|
} else if Some(":char") == item.expr()?.head()? {
|
||||||
// TODO
|
// TODO
|
||||||
|
return Ok(())
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("load_defs: unexpected: {item:?}").into())
|
return Err(format!("Config::load_bind: invalid key: {:?}", item.expr()?.head()?).into())
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return Err(format!("Config::load_bind: unexpected: {item:?}").into())
|
||||||
|
})?;
|
||||||
|
self.binds.write().unwrap().insert(id, map);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn load_mode (&mut self, id: Arc<str>, item: impl Dsl) -> Usually<()> {
|
pub fn load_mode (&mut self, id: Arc<str>, dsl: impl Dsl) -> Usually<()> {
|
||||||
let mut mode = Mode::default();
|
let mut mode = Mode::default();
|
||||||
item.exp().tail().tail()?.each(|item|Self::load_mode_one(&mut mode, item))?;
|
dsl.each(|item|Self::load_mode_one(&mut mode, item))?;
|
||||||
self.modes.write().unwrap().insert(id.into(), Arc::new(mode));
|
self.modes.write().unwrap().insert(id.into(), Arc::new(mode));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn load_mode_one (mode: &mut Mode<Arc<str>>, item: impl Dsl) -> Usually<()> {
|
pub fn load_mode_one (mode: &mut Mode<Arc<str>>, item: impl Dsl) -> Usually<()> {
|
||||||
Ok(if let Ok(Some(key)) = item.exp().head() {
|
Ok(if let Ok(Some(key)) = item.expr().head() {
|
||||||
match key {
|
match key {
|
||||||
"name" => mode.name.push(item.exp()?.tail()?.map(|x|x.trim()).unwrap_or("").into()),
|
"name" => mode.name.push(item.expr()?.tail()?.map(|x|x.trim()).unwrap_or("").into()),
|
||||||
"info" => mode.info.push(item.exp()?.tail()?.map(|x|x.trim()).unwrap_or("").into()),
|
"info" => mode.info.push(item.expr()?.tail()?.map(|x|x.trim()).unwrap_or("").into()),
|
||||||
"keys" => mode.keys.push(item.exp()?.tail()?.map(|x|x.trim()).unwrap_or("").into()),
|
"keys" => mode.keys.push(item.expr()?.tail()?.map(|x|x.trim()).unwrap_or("").into()),
|
||||||
"mode" => if let Some(id) = item.exp()?.tail()?.head()? {
|
"mode" => if let Some(id) = item.expr()?.tail()?.head()? {
|
||||||
let mut submode = Mode::default();
|
let mut submode = Mode::default();
|
||||||
Self::load_mode_one(&mut submode, item.exp()?.tail()?.tail()?)?;
|
Self::load_mode_one(&mut submode, item.expr()?.tail()?.tail()?)?;
|
||||||
mode.modes.insert(id.into(), submode);
|
mode.modes.insert(id.into(), submode);
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("load_mode_one: incomplete: {item:?}").into());
|
return Err(format!("load_mode_one: incomplete: {item:?}").into());
|
||||||
},
|
},
|
||||||
_ => mode.view.push(item.exp()?.unwrap().into()),
|
_ => mode.view.push(item.expr()?.unwrap().into()),
|
||||||
}
|
}
|
||||||
} else if let Ok(Some(sym)) = item.sym() {
|
} else if let Ok(Some(word)) = item.word() {
|
||||||
mode.view.push(sym.into());
|
mode.view.push(word.into());
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("load_mode_one: unexpected: {item:?}").into());
|
return Err(format!("load_mode_one: unexpected: {item:?}").into());
|
||||||
})
|
})
|
||||||
|
|
@ -210,20 +226,24 @@ fn render_dsl <'t> (
|
||||||
state: &'t impl DslNs<'t, Box<dyn Render<TuiOut>>>,
|
state: &'t impl DslNs<'t, Box<dyn Render<TuiOut>>>,
|
||||||
src: &str
|
src: &str
|
||||||
) -> Box<dyn Render<TuiOut>> {
|
) -> Box<dyn Render<TuiOut>> {
|
||||||
let sym_err: Option<Box<dyn Error>> = match state.from_sym(src) {
|
let err: Option<Box<dyn Error>> = match state.from(src) {
|
||||||
Ok(Some(value)) => return value, Ok(None) => None, Err(e) => Some(e),
|
Ok(Some(value)) => return value, Ok(None) => None, Err(e) => Some(e),
|
||||||
};
|
};
|
||||||
let exp_err = match state.from_exp(src) {
|
let (fg_1, bg_1) = (Color::Rgb(240, 160, 100), Color::Rgb(48, 0, 0));
|
||||||
Ok(Some(value)) => return value, Ok(None) => None, Err(e) => Some(e),
|
let (fg_2, bg_2) = (Color::Rgb(250, 200, 180), Color::Rgb(48, 0, 0));
|
||||||
};
|
let (fg_3, bg_3) = (Color::Rgb(250, 200, 120), Color::Rgb(0, 0, 0));
|
||||||
let (err_fg_1, err_bg_1) = (Color::Rgb(240, 160, 100), Color::Rgb(48, 0, 0));
|
let bg = Color::Rgb(24, 0, 0);
|
||||||
let (err_fg_2, err_bg_2) = (Color::Rgb(250, 200, 120), Color::Rgb(32, 0, 0));
|
Box::new(col! {
|
||||||
Box::new(Fill::x(col! {
|
Tui::fg(bg, Fixed::y(1, Fill::x(RepeatH("▄")))),
|
||||||
Fill::x(Margin::x(1, Align::w(Tui::bold(true, Tui::fg_bg(err_fg_1, err_bg_1, "Could not render:"))))),
|
Tui::bg(bg, col! {
|
||||||
Fill::x(Margin::x(1, Tui::fg_bg(err_fg_2, err_bg_2, format!("{src}")))),
|
Fill::x(Bsp::e(
|
||||||
Fill::x(Margin::x(1, Tui::fg_bg(err_fg_2, err_bg_2, format!("{sym_err:?}")))),
|
Tui::bold(true, Tui::fg_bg(fg_1, bg_1, " Render error: ")),
|
||||||
Fill::x(Margin::x(1, Tui::fg_bg(err_fg_2, err_bg_2, format!("{exp_err:?}")))),
|
Tui::fg_bg(fg_2, bg_2, err.map(|e|format!(" {e} "))),
|
||||||
}))
|
)),
|
||||||
|
Fill::x(Align::x(Tui::fg_bg(fg_3, bg_3, format!(" {src} ")))),
|
||||||
|
}),
|
||||||
|
Tui::fg(bg, Fixed::y(1, Fill::x(RepeatH("▀")))),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_dialog (dialog: impl Content<TuiOut>) -> impl Content<TuiOut> {
|
fn wrap_dialog (dialog: impl Content<TuiOut>) -> impl Content<TuiOut> {
|
||||||
|
|
@ -242,22 +262,19 @@ impl ScenesView for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handle!(TuiIn:|self: App, input|{
|
handle!(TuiIn:|self: App, input|{
|
||||||
panic!("wat: {:?}", self.mode);
|
for id in self.mode.keys.iter() {
|
||||||
for keys in self.mode.keys.iter() {
|
if let Some(event_map) = self.config.binds.read().unwrap().get(id.as_ref()) {
|
||||||
panic!("{keys} {:?}", self.config.binds.read().unwrap());
|
if let Some(bindings) = event_map.query(input.event()) {
|
||||||
if let Some(binding) = self.config.binds.read().unwrap().get(keys.as_ref()) {
|
for binding in bindings {
|
||||||
panic!("{binding:?}");
|
panic!("{binding:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
});
|
});
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AppCommand { /* TODO */ }
|
pub enum AppCommand { /* TODO */ }
|
||||||
impl App {
|
|
||||||
pub fn editor_focused (&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Dialog {
|
impl Dialog {
|
||||||
pub fn menu_selected (&self) -> Option<usize> {
|
pub fn menu_selected (&self) -> Option<usize> {
|
||||||
if let Self::Menu(selected) = self { Some(*selected) } else { None }
|
if let Self::Menu(selected) = self { Some(*selected) } else { None }
|
||||||
|
|
@ -293,6 +310,9 @@ impl Dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
pub fn editor_focused (&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
pub fn toggle_dialog (&mut self, mut dialog: Dialog) -> Dialog {
|
pub fn toggle_dialog (&mut self, mut dialog: Dialog) -> Dialog {
|
||||||
std::mem::swap(&mut self.dialog, &mut dialog);
|
std::mem::swap(&mut self.dialog, &mut dialog);
|
||||||
dialog
|
dialog
|
||||||
|
|
@ -371,28 +391,144 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dsl_ns!(|app: App|
|
macro_rules!dsl_words((|$state:ident|$({
|
||||||
|
$($word:literal => $body:expr),* $(,)?
|
||||||
u8;
|
})?)=>{
|
||||||
|
const WORDS: DslNsMap<'t, fn (&'t Self)->Perhaps<Box<dyn Render<TuiOut>>>> = DslNsMap::new(&[$(
|
||||||
isize;
|
$(($word, |$state: &Self|{Ok(Some($body))})),*
|
||||||
|
)? ]);
|
||||||
ItemTheme => {
|
});
|
||||||
":_theme_stub" => Default::default()
|
macro_rules!dsl_exprs((|$state:ident|$({
|
||||||
|
$($name:literal ($($arg:ident:$ty:ty),* $(,)?) => $body:expr),* $(,)?
|
||||||
|
})?)=>{
|
||||||
|
const EXPRS: DslNsMap<'t, fn (&'t Self, &str)->Perhaps<Box<dyn Render<TuiOut>>>> = DslNsMap::new(&[$(
|
||||||
|
$(($name, |$state: &Self, tail: &str|{
|
||||||
|
$(
|
||||||
|
let head = tail.head()?.unwrap_or_default();
|
||||||
|
let tail = tail.tail()?.unwrap_or_default();
|
||||||
|
let $arg: $ty = if let Some(arg) = $state.from(&head)? {
|
||||||
|
arg
|
||||||
|
} else {
|
||||||
|
return Err(format!("{}: arg \"{}\" ({}) got: {head}, remaining: {tail}",
|
||||||
|
$name,
|
||||||
|
stringify!($arg),
|
||||||
|
stringify!($ty),
|
||||||
|
).into())
|
||||||
};
|
};
|
||||||
|
)*
|
||||||
|
Ok(Some($body))
|
||||||
|
})),*
|
||||||
|
)? ]);
|
||||||
|
});
|
||||||
|
|
||||||
u16 => {
|
impl<'t> DslNs<'t, Box<dyn Render<TuiOut>>> for App {
|
||||||
":w/sidebar" => app.project.w_sidebar(app.editor().is_some()),
|
dsl_exprs!(|app|{
|
||||||
":h/sample-detail" => 6.max(app.height() as u16 * 3 / 9),
|
"fg" (color: Color, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::fg(color, x)),
|
||||||
};
|
"bg" (color: Color, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::bg(color, x)),
|
||||||
|
"fg/bg" (fg: Color, bg: Color, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::fg_bg(fg, bg, x)),
|
||||||
|
|
||||||
usize => {
|
"bsp/n" (a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::n(a, b)),
|
||||||
":scene-count" => app.scenes().len(),
|
"bsp/s" (a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::s(a, b)),
|
||||||
":track-count" => app.tracks().len(),
|
"bsp/e" (a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::e(a, b)),
|
||||||
":device-kind" => app.dialog.device_kind().unwrap_or(0),
|
"bsp/w" (a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::w(a, b)),
|
||||||
":device-kind/next" => app.dialog.device_kind_next().unwrap_or(0),
|
"bsp/a" (a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::a(a, b)),
|
||||||
":device-kind/prev" => app.dialog.device_kind_prev().unwrap_or(0),
|
"bsp/b" (a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::b(a, b)),
|
||||||
};
|
|
||||||
|
"align/n" (x: Box<dyn Render<TuiOut>>) => Box::new(Align::n(x)),
|
||||||
|
"align/s" (x: Box<dyn Render<TuiOut>>) => Box::new(Align::s(x)),
|
||||||
|
"align/e" (x: Box<dyn Render<TuiOut>>) => Box::new(Align::e(x)),
|
||||||
|
"align/w" (x: Box<dyn Render<TuiOut>>) => Box::new(Align::w(x)),
|
||||||
|
"align/x" (x: Box<dyn Render<TuiOut>>) => Box::new(Align::x(x)),
|
||||||
|
"align/y" (x: Box<dyn Render<TuiOut>>) => Box::new(Align::y(x)),
|
||||||
|
"align/c" (x: Box<dyn Render<TuiOut>>) => Box::new(Align::c(x)),
|
||||||
|
|
||||||
|
"fill/x" (x: Box<dyn Render<TuiOut>>) => Box::new(Fill::x(x)),
|
||||||
|
"fill/y" (x: Box<dyn Render<TuiOut>>) => Box::new(Fill::y(x)),
|
||||||
|
"fill/xy" (x: Box<dyn Render<TuiOut>>) => Box::new(Fill::xy(x)),
|
||||||
|
|
||||||
|
"fixed/x" (x: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Fixed::x(x, c)),
|
||||||
|
"fixed/y" (y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Fixed::y(y, c)),
|
||||||
|
"fixed/xy" (x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Fixed::xy(x, y, c)),
|
||||||
|
|
||||||
|
"min/x" (x: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Min::x(x, c)),
|
||||||
|
"min/y" (y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Min::y(y, c)),
|
||||||
|
"min/xy" (x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Min::xy(x, y, c)),
|
||||||
|
|
||||||
|
"max/x" (x: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Max::x(x, c)),
|
||||||
|
"max/y" (y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Max::y(y, c)),
|
||||||
|
"max/xy" (x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Max::xy(x, y, c)),
|
||||||
|
});
|
||||||
|
/// Resolve an expression if known.
|
||||||
|
fn from_expr <D: Dsl> (&'t self, dsl: D) -> Perhaps<Box<dyn Render<TuiOut>>> {
|
||||||
|
if let Some(head) = dsl.expr().head()? {
|
||||||
|
for (key, value) in Self::EXPRS.0.iter() {
|
||||||
|
if head == *key {
|
||||||
|
return value(self, dsl.expr().tail()?.unwrap_or(""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
dsl_words!(|app|{
|
||||||
|
":templates" => Box::new({
|
||||||
|
let modes = app.config.modes.clone();
|
||||||
|
let height = (modes.read().unwrap().len() * 2) as u16;
|
||||||
|
Min::x(30, Fixed::y(height, Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
|
||||||
|
for (index, (id, profile)) in modes.read().unwrap().iter().enumerate() {
|
||||||
|
let bg = if index == 0 { Rgb(48,64,32) } else { Rgb(16, 32, 24) };
|
||||||
|
let name = profile.name.get(0).map(|x|x.as_ref()).unwrap_or("<no name>");
|
||||||
|
let info = profile.info.get(0).map(|x|x.as_ref()).unwrap_or("<no info>");
|
||||||
|
let fg1 = Rgb(224, 192, 128);
|
||||||
|
let fg2 = Rgb(224, 128, 32);
|
||||||
|
add(&Fixed::y(2, Fill::x(Tui::bg(bg, Bsp::s(
|
||||||
|
Bsp::a(Fill::x(Align::w(Tui::fg(fg1, name))),
|
||||||
|
Fill::x(Align::e(Tui::fg(fg2, id)))),
|
||||||
|
Align::w(info))))));
|
||||||
|
}})))}),
|
||||||
|
":sessions" => Box::new(Min::x(30, Fixed::y(6, Stack::south(
|
||||||
|
move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
|
||||||
|
let fg = Rgb(224, 192, 128);
|
||||||
|
for (index, name) in ["session1", "session2", "session3"].iter().enumerate() {
|
||||||
|
let bg = if index == 0 { Rgb(48,64,32) } else { Rgb(16, 32, 24) };
|
||||||
|
add(&Fixed::y(2, Fill::x(Tui::bg(bg, Align::w(Tui::fg(fg, name))))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)))),
|
||||||
|
":browse/title" => Box::new(Fill::x(Align::w(FieldV(Default::default(),
|
||||||
|
match app.dialog.browser_target().unwrap() {
|
||||||
|
BrowserTarget::SaveProject => "Save project:",
|
||||||
|
BrowserTarget::LoadProject => "Load project:",
|
||||||
|
BrowserTarget::ImportSample(_) => "Import sample:",
|
||||||
|
BrowserTarget::ExportSample(_) => "Export sample:",
|
||||||
|
BrowserTarget::ImportClip(_) => "Import clip:",
|
||||||
|
BrowserTarget::ExportClip(_) => "Export clip:",
|
||||||
|
}, Shrink::x(3, Fixed::y(1, Tui::fg(Tui::g(96), RepeatH("🭻")))))))),
|
||||||
|
":device" => {
|
||||||
|
let selected = app.dialog.device_kind().unwrap();
|
||||||
|
Box::new(Bsp::s(Tui::bold(true, "Add device"), Map::south(1,
|
||||||
|
move||device_kinds().iter(),
|
||||||
|
move|label: &&'static str, i|{
|
||||||
|
let bg = if i == selected { Rgb(64,128,32) } else { Rgb(0,0,0) };
|
||||||
|
let lb = if i == selected { "[ " } else { " " };
|
||||||
|
let rb = if i == selected { " ]" } else { " " };
|
||||||
|
Fill::x(Tui::bg(bg, Bsp::e(lb, Bsp::w(rb, "FIXME device name")))) }))) },
|
||||||
|
});
|
||||||
|
/// Resolve a symbol if known.
|
||||||
|
fn from_word <D: Dsl> (&'t self, dsl: D) -> Perhaps<Box<dyn Render<TuiOut>>> {
|
||||||
|
if let Some(dsl) = dsl.word()? {
|
||||||
|
let views = self.config.views.read().unwrap();
|
||||||
|
if let Some(view) = views.get(dsl) {
|
||||||
|
let view = view.clone();
|
||||||
|
std::mem::drop(views);
|
||||||
|
return Ok(Some(render_dsl(self, view.as_ref())))
|
||||||
|
}
|
||||||
|
for (word, get) in Self::WORDS.0 { if dsl == *word { return get(self) } }
|
||||||
|
}
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dsl_ns! { |app: App|
|
||||||
|
|
||||||
bool => {
|
bool => {
|
||||||
":focused/editor" => app.project.editor.is_some(),
|
":focused/editor" => app.project.editor.is_some(),
|
||||||
|
|
@ -414,6 +550,10 @@ dsl_ns!(|app: App|
|
||||||
Selection::Mix),
|
Selection::Mix),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ItemTheme => {
|
||||||
|
":_theme_stub" => Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
Dialog => {
|
Dialog => {
|
||||||
":dialog/none" => Dialog::None,
|
":dialog/none" => Dialog::None,
|
||||||
":dialog/options" => Dialog::Options,
|
":dialog/options" => Dialog::Options,
|
||||||
|
|
@ -446,7 +586,9 @@ dsl_ns!(|app: App|
|
||||||
};
|
};
|
||||||
|
|
||||||
Option<u7> => {
|
Option<u7> => {
|
||||||
":editor/pitch" => Some((app.editor().as_ref().map(|e|e.get_note_pos()).unwrap() as u8).into())
|
":editor/pitch" => Some(
|
||||||
|
(app.editor().as_ref().map(|e|e.get_note_pos()).unwrap() as u8).into()
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
Option<usize> => {
|
Option<usize> => {
|
||||||
|
|
@ -490,99 +632,49 @@ dsl_ns!(|app: App|
|
||||||
|
|
||||||
Color => {
|
Color => {
|
||||||
("g", n: u8) => Color::Rgb(n, n, n),
|
("g", n: u8) => Color::Rgb(n, n, n),
|
||||||
("rgb", r: u8, g: u8, b: u8) => Color::Rgb(r, g, b),
|
("rgb", red: u8, green: u8, blue: u8) => Color::Rgb(red, green, blue),
|
||||||
|
":color/bg" => Color::Rgb(28, 32, 36),
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Box<dyn Render<TuiOut>> => {
|
}
|
||||||
("bold", value: bool, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::bold(value, x)),
|
|
||||||
|
|
||||||
("fg", color: Color, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::fg(color, x)),
|
dsl_ns! { num |app: App|
|
||||||
("bg", color: Color, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::bg(color, x)),
|
u8;
|
||||||
("fg/bg", fg: Color, bg: Color, x: Box<dyn Render<TuiOut>>) => Box::new(Tui::fg_bg(fg, bg, x)),
|
u16 => {
|
||||||
|
":w/sidebar" => app.project.w_sidebar(app.editor().is_some()),
|
||||||
("bsp/n", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::n(a, b)),
|
":h/sample-detail" => 6.max(app.height() as u16 * 3 / 9),
|
||||||
("bsp/s", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::s(a, b)),
|
|
||||||
("bsp/e", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::e(a, b)),
|
|
||||||
("bsp/w", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::w(a, b)),
|
|
||||||
("bsp/a", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::a(a, b)),
|
|
||||||
("bsp/b", a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::b(a, b)),
|
|
||||||
|
|
||||||
("align/n", x: Box<dyn Render<TuiOut>>) => Box::new(Align::n(x)),
|
|
||||||
("align/s", x: Box<dyn Render<TuiOut>>) => Box::new(Align::s(x)),
|
|
||||||
("align/e", x: Box<dyn Render<TuiOut>>) => Box::new(Align::e(x)),
|
|
||||||
("align/w", x: Box<dyn Render<TuiOut>>) => Box::new(Align::w(x)),
|
|
||||||
("align/x", x: Box<dyn Render<TuiOut>>) => Box::new(Align::x(x)),
|
|
||||||
("align/y", x: Box<dyn Render<TuiOut>>) => Box::new(Align::y(x)),
|
|
||||||
("align/c", x: Box<dyn Render<TuiOut>>) => Box::new(Align::c(x)),
|
|
||||||
|
|
||||||
("fill/x", x: Box<dyn Render<TuiOut>>) => Box::new(Fill::x(x)),
|
|
||||||
("fill/y", x: Box<dyn Render<TuiOut>>) => Box::new(Fill::y(x)),
|
|
||||||
("fill/xy", x: Box<dyn Render<TuiOut>>) => Box::new(Fill::xy(x)),
|
|
||||||
|
|
||||||
("fixed/x", x: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Fixed::x(x, c)),
|
|
||||||
("fixed/y", y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Fixed::y(y, c)),
|
|
||||||
("fixed/xy", x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Fixed::xy(x, y, c)),
|
|
||||||
|
|
||||||
("min/x", x: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Min::x(x, c)),
|
|
||||||
("min/y", y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Min::y(y, c)),
|
|
||||||
("min/xy", x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Min::xy(x, y, c)),
|
|
||||||
|
|
||||||
("max/x", x: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Max::x(x, c)),
|
|
||||||
("max/y", y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Max::y(y, c)),
|
|
||||||
("max/xy", x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Max::xy(x, y, c)),
|
|
||||||
|
|
||||||
":view/menu" =>
|
|
||||||
app.view(stringify!((bg (rgb 0 0 0)
|
|
||||||
(bsp/s :view/ports/outs (bsp/s (bg (rgb 33 33 33) (bold :true "tek 0.3.0-rc.0"))
|
|
||||||
(bsp/n :view/ports/ins (bsp/n
|
|
||||||
(bg (rgb 33 33 33) (bsp/e (fg (rgb 255 192 48) "[Enter]") " new session"))
|
|
||||||
(align/n (fill/xy :view/modes))))))))),
|
|
||||||
":view/modes" => Box::new({
|
|
||||||
let modes = app.config.modes.clone();
|
|
||||||
Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
|
|
||||||
for (index, (id, profile)) in modes.read().unwrap().iter().enumerate() {
|
|
||||||
let bg = if index == 0 { Rgb(64,64,64) } else { Rgb(32,32,32) };
|
|
||||||
let name = profile.name.get(0).map(|x|x.as_ref()).unwrap_or("<no name>");
|
|
||||||
let info = profile.info.get(0).map(|x|x.as_ref()).unwrap_or("<no info>");
|
|
||||||
//add(&app.view(stringify!((fixed/y 2 (bg #bg (bsp/s
|
|
||||||
//(fill/x (bsp/a (fill/x (align/w (fg (rgb 224 192 128) #name)))
|
|
||||||
//(fill/x (align/e (fg (rgb 224 128 32) #id)))))
|
|
||||||
//(fill/x (align/w #info))))))));
|
|
||||||
}
|
|
||||||
}) }),
|
|
||||||
":view/ports/outs" =>
|
|
||||||
app.view(stringify!((fill/x (fixed/y 3
|
|
||||||
(bsp/a (fill/x (align/w "L AUDIO OUT")
|
|
||||||
(bsp/a "MIDI OUT" (fill/x (align/e "AUDIO OUT R"))))))))),
|
|
||||||
":view/ports/ins" =>
|
|
||||||
app.view(stringify!(fill/x (fixed/y 3
|
|
||||||
(bsp/a (fill/x (align/w "L AUDIO IN ")
|
|
||||||
(bsp/a "MIDI IN " (fill/x (align/e "AUDIO IN R")))))))),
|
|
||||||
":view/browse" =>
|
|
||||||
app.view(stringify!(bsp/s
|
|
||||||
(padding/xy 3 1 :view/browse-title) (enclose (fg (g 96)) :view/browser))),
|
|
||||||
":view/browse/title" =>
|
|
||||||
Box::new(Fill::x(Align::w(FieldV(Default::default(),
|
|
||||||
match app.dialog.browser_target().unwrap() {
|
|
||||||
BrowserTarget::SaveProject => "Save project:",
|
|
||||||
BrowserTarget::LoadProject => "Load project:",
|
|
||||||
BrowserTarget::ImportSample(_) => "Import sample:",
|
|
||||||
BrowserTarget::ExportSample(_) => "Export sample:",
|
|
||||||
BrowserTarget::ImportClip(_) => "Import clip:",
|
|
||||||
BrowserTarget::ExportClip(_) => "Export clip:",
|
|
||||||
}, Shrink::x(3, Fixed::y(1, Tui::fg(Tui::g(96), RepeatH("🭻")))))))),
|
|
||||||
":view/device" => {
|
|
||||||
let selected = app.dialog.device_kind().unwrap();
|
|
||||||
Box::new(Bsp::s(Tui::bold(true, "Add device"), Map::south(1,
|
|
||||||
move||device_kinds().iter(),
|
|
||||||
move|label: &&'static str, i|{
|
|
||||||
let bg = if i == selected { Rgb(64,128,32) } else { Rgb(0,0,0) };
|
|
||||||
let lb = if i == selected { "[ " } else { " " };
|
|
||||||
let rb = if i == selected { " ]" } else { " " };
|
|
||||||
Fill::x(Tui::bg(bg, Bsp::e(lb, Bsp::w(rb, "FIXME device name")))) }))) },
|
|
||||||
//(":view/options", view_options),
|
|
||||||
};
|
};
|
||||||
);
|
usize => {
|
||||||
|
":scene-count" => app.scenes().len(),
|
||||||
|
":track-count" => app.tracks().len(),
|
||||||
|
":device-kind" => app.dialog.device_kind().unwrap_or(0),
|
||||||
|
":device-kind/next" => app.dialog.device_kind_next().unwrap_or(0),
|
||||||
|
":device-kind/prev" => app.dialog.device_kind_prev().unwrap_or(0),
|
||||||
|
};
|
||||||
|
isize;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#[dizzle::ns]
|
||||||
|
//#[dizzle::ns::include(TuiOut)]
|
||||||
|
//trait AppApi {
|
||||||
|
//#[dizzle::ns::word("sessions")]
|
||||||
|
//fn view_sessions (&self) -> Box<dyn Render<TuiOut>> {
|
||||||
|
//Min::x(30, Fixed::y(6, Stack::south(
|
||||||
|
//move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
|
||||||
|
//let fg = Rgb(224, 192, 128);
|
||||||
|
//for (index, name) in ["session1", "session2", "session3"].iter().enumerate() {
|
||||||
|
//let bg = if index == 0 { Rgb(48,64,32) } else { Rgb(16, 32, 24) };
|
||||||
|
//add(&Fixed::y(2, Fill::x(Tui::bg(bg, Align::w(Tui::fg(fg, name))))));
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//))).into()
|
||||||
|
//}
|
||||||
|
//#[dizzle::ns::expr("bold")]
|
||||||
|
//fn view_bold (&self, value: bool, x: Box<dyn Render<TuiOut>>) -> Box<dyn Render<TuiOut>> {
|
||||||
|
//Box::new(Tui::bold(value, x))
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,8 @@ impl Cli {
|
||||||
color: ItemTheme::random(),
|
color: ItemTheme::random(),
|
||||||
dialog: Dialog::Menu(0),
|
dialog: Dialog::Menu(0),
|
||||||
mode: Mode {
|
mode: Mode {
|
||||||
view: vec![":view/menu".into()],
|
view: vec![":menu".into()],
|
||||||
keys: vec![":keys/axis/y".into()],
|
keys: vec![":y".into()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
project: Arrangement {
|
project: Arrangement {
|
||||||
|
|
|
||||||
2
deps/tengri
vendored
2
deps/tengri
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4fc0db577737ad1ce2601aa99d45248dba9a2d5f
|
Subproject commit 1ef898ac32a8292d8c4ecf056f8d8c7d957f2991
|
||||||
115
tek.edn
115
tek.edn
|
|
@ -1,53 +1,60 @@
|
||||||
|
(view :menu (bg :color/bg (bsp/s :ports/out (bsp/n :ports/in (bsp/e :sessions :templates)))))
|
||||||
|
(view :ports/out (fill/x (fixed/y 3 (bsp/a (fill/x (align/w "L AUDIO OUT")
|
||||||
|
(bsp/a "MIDI OUT" (fill/x (align/e "AUDIO OUT R"))))))))
|
||||||
|
(view :ports/in (fill/x (fixed/y 3 (bsp/a (fill/x (align/w "L AUDIO IN ")
|
||||||
|
(bsp/a "MIDI IN " (fill/x (align/e "AUDIO IN R"))))))))
|
||||||
|
(view :browse (bsp/s (padding/xy 3 1 :browse-title) (enclose (fg (g 96)) browser)))
|
||||||
|
|
||||||
(keys :help (@f1 dialog :help))
|
(keys :help (@f1 dialog :help))
|
||||||
(keys :back (@escape back))
|
(keys :back (@escape back))
|
||||||
(keys :confirm (@enter confirm))
|
(keys :confirm (@enter confirm))
|
||||||
(keys :page (@pgup page/up) (@pgdn page/down))
|
(keys :page (@pgup page/up) (@pgdn page/down))
|
||||||
(keys :axis/x (@left x/prev) (@right x/next))
|
(keys :x (@left x/prev) (@right x/next))
|
||||||
(keys :axis/x2 (@shift/left x2/prev) (@shift/right x2/next))
|
(keys :x2 (@shift/left x2/prev) (@shift/right x2/next))
|
||||||
(keys :axis/y (@up y/prev) (@down y/next))
|
(keys :y (@up y/prev) (@down y/next))
|
||||||
(keys :axis/y2 (@shift/up y2/prev) (@shift/down y2/next))
|
(keys :y2 (@shift/up y2/prev) (@shift/down y2/next))
|
||||||
(keys :axis/z (@minus z/prev) (@equal z/next))
|
(keys :z (@minus z/prev) (@equal z/next))
|
||||||
(keys :axis/z2 (@underscore z2/prev) (@plus z2/next))
|
(keys :z2 (@underscore z2/prev) (@plus z2/next))
|
||||||
(keys :axis/i (@comma i/prev) (@period z/next))
|
(keys :i (@comma i/prev) (@period z/next))
|
||||||
(keys :axis/i2 (@lt i2/prev) (@gt z2/next))
|
(keys :i2 (@lt i2/prev) (@gt z2/next))
|
||||||
(keys :axis/w (@openbracket w/prev) (@closebracket w/next))
|
(keys :w (@openbracket w/prev) (@closebracket w/next))
|
||||||
(keys :axis/w2 (@openbrace w2/prev) (@closebrace w2/next))
|
(keys :w2 (@openbrace w2/prev) (@closebrace w2/next))
|
||||||
(keys :delete (@delete delete))
|
(keys :delete (@delete delete) (@backspace delete/back))
|
||||||
(keys :backspace (@backspace delete/back))
|
(keys :input (see :x :delete) (:char input))
|
||||||
(keys :input (see :delete :axis/x :backspace) (:char input))
|
(keys :list (see :y :confirm))
|
||||||
(keys :length (see :confirm :axis/y :axis/x))
|
(keys :length (see :x :y :confirm))
|
||||||
(keys :list (see :confirm :axis/y))
|
|
||||||
(keys :browse (see :list :input :focus))
|
(keys :browse (see :list :input :focus))
|
||||||
(keys :focus)
|
|
||||||
(keys :history (@u undo 1) (@r redo 1))
|
(keys :history (@u undo 1) (@r redo 1))
|
||||||
(keys :clock (@space clock/toggle 0) (@shift/space clock/toggle 0))
|
(keys :clock (@space clock/toggle 0) (@shift/space clock/toggle 0))
|
||||||
(keys :color (@c color))
|
(keys :color (@c color))
|
||||||
(keys :launch (@q launch))
|
(keys :launch (@q launch))
|
||||||
(keys :saveload (@f6 dialog :save) (@f9 dialog :load))
|
(keys :saveload (@f6 dialog :save) (@f9 dialog :load))
|
||||||
(keys :global (see :history :saveload) (@f8 dialog :options) (@f10 dialog :quit))
|
(keys :global (see :history :saveload) (@f8 dialog :options) (@f10 dialog :quit))
|
||||||
|
(keys :focus)
|
||||||
|
|
||||||
(mode :transport
|
(mode :transport (name Transport)
|
||||||
(name Transport)
|
|
||||||
(info A JACK transport controller.)
|
(info A JACK transport controller.)
|
||||||
(keys :clock :global)
|
(keys :clock :global)
|
||||||
:transport)
|
(view :transport))
|
||||||
|
|
||||||
(mode :arranger
|
(mode :arranger
|
||||||
(name Arranger)
|
(name Arranger)
|
||||||
(info A grid of launchable clips arranged by track and scene.)
|
(info A grid of launchable clips arranged by track and scene.)
|
||||||
(when :mode/editor (keys :editor))
|
(mode :editor (keys :editor))
|
||||||
(when :mode/dialog (keys :dialog))
|
(mode :dialog (keys :dialog))
|
||||||
(when :mode/message (keys :message))
|
(mode :message (keys :message))
|
||||||
(when :mode/add-device (keys :add-device))
|
(mode :add-device (keys :add-device))
|
||||||
(when :mode/browse (keys :browse))
|
(mode :browse (keys :browse))
|
||||||
(when :mode/rename (keys :pool/rename))
|
(mode :rename (keys :input))
|
||||||
(when :mode/length (keys :pool/length))
|
(mode :length (keys :rename))
|
||||||
(when :mode/clip (keys :clip))
|
(mode :clip (keys :clip))
|
||||||
(when :mode/track (keys :track))
|
(mode :track (keys :track))
|
||||||
(when :mode/scene (keys :scene))
|
(mode :scene (keys :scene))
|
||||||
(when :mode/mix (keys :mix))
|
(mode :mix (keys :mix))
|
||||||
(keys :clock :arranger :global)
|
(keys :clock :arranger :global)
|
||||||
(bsp/w :meters/output (bsp/e :meters/input (stack/n
|
(view :arranger))
|
||||||
|
|
||||||
|
(view :arranger (bsp/w :meters/output (bsp/e :meters/input (stack/n
|
||||||
(fixed/y 2 :status/h2) :tracks/inputs (stack/s
|
(fixed/y 2 :status/h2) :tracks/inputs (stack/s
|
||||||
:tracks/devices :tracks/outputs :tracks/names
|
:tracks/devices :tracks/outputs :tracks/names
|
||||||
(fill/xy (either :mode/editor (bsp/e :scenes/names :editor) :scenes)))))))
|
(fill/xy (either :mode/editor (bsp/e :scenes/names :editor) :scenes)))))))
|
||||||
|
|
@ -63,30 +70,32 @@
|
||||||
(@left select :select/track/prev)
|
(@left select :select/track/prev)
|
||||||
(@right select :select/track/next))
|
(@right select :select/track/next))
|
||||||
|
|
||||||
(keys :track (see :color :launch :axis/z :axis/z2 :delete)
|
|
||||||
(@r toggle :rec)
|
|
||||||
(@m toggle :mon)
|
|
||||||
(@p toggle :play)
|
|
||||||
(@P toggle :solo))
|
|
||||||
|
|
||||||
(keys :scenes
|
(keys :scenes
|
||||||
(@s select :select/scene)
|
(@s select :select/scene)
|
||||||
(@up select :select/scene/prev)
|
(@up select :select/scene/prev)
|
||||||
(@down select :select/scene/next))
|
(@down select :select/scene/next))
|
||||||
|
|
||||||
(keys :scene (see :color :launch :axis/z :axis/z2 :delete))
|
(keys :track (see :color :launch :z :z2 :delete)
|
||||||
|
(@r toggle :rec)
|
||||||
|
(@m toggle :mon)
|
||||||
|
(@p toggle :play)
|
||||||
|
(@P toggle :solo))
|
||||||
|
|
||||||
(keys :clip (see :color :launch :axis/z :axis/z2 :delete)
|
(keys :scene (see :color :launch :z :z2 :delete))
|
||||||
|
|
||||||
|
(keys :clip (see :color :launch :z :z2 :delete)
|
||||||
(@l toggle :loop))
|
(@l toggle :loop))
|
||||||
|
|
||||||
(mode :groovebox
|
(mode :groovebox
|
||||||
(name Groovebox)
|
(name Groovebox)
|
||||||
(info A sequencer with built-in sampler.)
|
(info A sequencer with built-in sampler.)
|
||||||
(when :mode/browse (keys :browse))
|
(mode browse (keys :browse))
|
||||||
(when :mode/rename (keys :pool-rename))
|
(mode rename (keys :pool-rename))
|
||||||
(when :mode/length (keys :pool-length))
|
(mode length (keys :pool-length))
|
||||||
(keys :clock :editor :sampler :global)
|
(keys :clock :editor :sampler :global)
|
||||||
(bsp/w :meters/output (bsp/e :meters/input (bsp/w
|
(view :groovebox))
|
||||||
|
|
||||||
|
(view :groovebox (bsp/w :meters/output (bsp/e :meters/input (bsp/w
|
||||||
(fill/y (align/n
|
(fill/y (align/n
|
||||||
(stack/s :midi-ins/status :midi-outs/status :audio-ins/status :audio-outs/status :pool)))
|
(stack/s :midi-ins/status :midi-outs/status :audio-ins/status :audio-outs/status :pool)))
|
||||||
(bsp/n
|
(bsp/n
|
||||||
|
|
@ -100,6 +109,9 @@
|
||||||
(name Sampler)
|
(name Sampler)
|
||||||
(info A sampling soundboard.)
|
(info A sampling soundboard.)
|
||||||
(keys :sampler :global)
|
(keys :sampler :global)
|
||||||
|
(view :sampler))
|
||||||
|
|
||||||
|
(view :sampler
|
||||||
(bsp/s (fixed/y 1 :transport) (bsp/n (fixed/y 1 :status) (fill/xy :samples/grid))))
|
(bsp/s (fixed/y 1 :transport) (bsp/n (fixed/y 1 :status) (fill/xy :samples/grid))))
|
||||||
|
|
||||||
(keys :sampler
|
(keys :sampler
|
||||||
|
|
@ -122,11 +134,14 @@
|
||||||
(mode :sequencer
|
(mode :sequencer
|
||||||
(name Sequencer)
|
(name Sequencer)
|
||||||
(info A MIDI sequencer.)
|
(info A MIDI sequencer.)
|
||||||
(when :mode/browse (keys :browse))
|
(mode browse (keys :browse))
|
||||||
(when :mode/rename (keys :pool/rename))
|
(mode rename (keys :pool/rename))
|
||||||
(when :mode/length (keys :pool/length))
|
(mode length (keys :pool/length))
|
||||||
(keys :editor :clock :global)
|
(keys :editor :clock :global)
|
||||||
(bsp/s (fixed/y 1 :transport)
|
(view :sequencer))
|
||||||
|
|
||||||
|
(view :sequencer (bsp/s
|
||||||
|
(fixed/y 1 :transport)
|
||||||
(bsp/n (fixed/y 1 :status)
|
(bsp/n (fixed/y 1 :status)
|
||||||
(fill/xy (bsp/a (fill/xy (align/e :pool))
|
(fill/xy (bsp/a (fill/xy (align/e :pool))
|
||||||
:editor)))))
|
:editor)))))
|
||||||
|
|
@ -134,13 +149,13 @@
|
||||||
(keys :sequencer (see :color :launch)
|
(keys :sequencer (see :color :launch)
|
||||||
(@shift/I input/add)
|
(@shift/I input/add)
|
||||||
(@shift/O output/add))
|
(@shift/O output/add))
|
||||||
(keys :pool (see :axis-y :axis-w :axis/z2 :color :delete)
|
(keys :pool (see :axis-y :axis-w :z2 :color :delete)
|
||||||
(@n rename/begin) (@t length/begin) (@m import/begin) (@x export/begin)
|
(@n rename/begin) (@t length/begin) (@m import/begin) (@x export/begin)
|
||||||
(@shift/A clip/add :after :new/clip)
|
(@shift/A clip/add :after :new/clip)
|
||||||
(@shift/D clip/add :after :cloned/clip))
|
(@shift/D clip/add :after :cloned/clip))
|
||||||
(keys :editor (see :editor/view :editor/note))
|
(keys :editor (see :editor/view :editor/note))
|
||||||
(keys :editor/view (see :axis/x :axis/x2 :axis/z :axis/z2)
|
(keys :editor/view (see :x :x2 :z :z2)
|
||||||
(@z toggle :lock))
|
(@z toggle :lock))
|
||||||
(keys :editor/note (see :axis/i :axis/i2 :axis/y :axis/page)
|
(keys :editor/note (see :i :i2 :y :page)
|
||||||
(@a editor/append :true) (@enter editor/append :false)
|
(@a editor/append :true) (@enter editor/append :false)
|
||||||
(@del editor/delete/note) (@shift/del editor/delete/note))
|
(@del editor/delete/note) (@shift/del editor/delete/note))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue