almost back in commission

This commit is contained in:
🪞👃🪞 2025-08-23 12:19:35 +03:00
parent fa5f67f010
commit d5865d941f
7 changed files with 219 additions and 216 deletions

View file

@ -1,38 +1,12 @@
// ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
//██Let me play the world's tiniest piano for you. ██
//█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
//█▙▙█▙▙▙█▙▙█▙▙▙█▙▙█▙▙▙█▙▙█▙▙▙█▙▙█▙▙▙█▙▙█▙▙▙█▙▙█▙▙▙██
//█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█
//███████████████████████████████████████████████████
//█ ▀ ▀ ▀ █
#![allow(unused, clippy::unit_arg)]
#![feature(adt_const_params, associated_type_defaults, if_let_guard, impl_trait_in_assoc_type,
type_alias_impl_trait, trait_alias, type_changing_struct_update, closure_lifetime_binder)]
pub use ::{
tek_engine::*,
tek_device,
tek_device::*,
tengri::{
Usually, Perhaps, Has, MaybeHas, has, maybe_has,
dsl::*, input::*, output::*, tui::*,
tui::ratatui,
tui::ratatui::prelude::buffer::Cell,
tui::ratatui::prelude::Color::{self, *},
tui::ratatui::prelude::{Style, Stylize, Buffer, Modifier},
tui::crossterm,
tui::crossterm::event::{Event, KeyCode::{self, *}},
},
std::{
path::{Path, PathBuf},
sync::{Arc, RwLock},
sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed},
error::Error,
collections::BTreeMap,
fmt::Write,
},
xdg::BaseDirectories,
};
mod app_deps; pub use self::app_deps::*;
mod app_jack; pub use self::app_jack::*;
mod app_bind; pub use self::app_bind::*;
mod app_data; pub use self::app_data::*;
mod app_view; pub use self::app_view::*;
#[cfg(test)] mod app_test;
/// Total state
#[derive(Default, Debug)]
@ -228,7 +202,7 @@ fn render_dsl <'t> (
state: &'t impl DslNs<'t, Box<dyn Render<TuiOut>>>,
src: &str
) -> Box<dyn Render<TuiOut>> {
let err: Option<Box<dyn Error>> = match state.from(src) {
let err: Option<Box<dyn Error>> = match state.from(&src) {
Ok(Some(value)) => return value, Ok(None) => None, Err(e) => Some(e),
};
let (fg_1, bg_1) = (Color::Rgb(240, 160, 100), Color::Rgb(48, 0, 0));
@ -265,8 +239,6 @@ handle!(TuiIn:|self: App, input|{
}
Ok(None)
});
#[derive(Debug)]
pub enum AppCommand { /* TODO */ }
impl Dialog {
pub fn menu_selected (&self) -> Option<usize> {
if let Self::Menu(selected) = self { Some(*selected) } else { None }
@ -290,17 +262,6 @@ impl Dialog {
todo!()
}
}
#[tengri_proc::command(Option<Dialog>)] impl DialogCommand {
fn open (dialog: &mut Option<Dialog>, new: Dialog) -> Perhaps<Self> {
*dialog = Some(new);
Ok(None)
}
fn close (dialog: &mut Option<Dialog>) -> Perhaps<Self> {
*dialog = None;
Ok(None)
}
}
impl App {
pub fn editor_focused (&self) -> bool {
false
@ -384,107 +345,6 @@ impl App {
}
dsl_ns! { |app: App|
bool => {
":focused/editor" => app.project.editor.is_some(),
":focused/dialog" => !matches!(app.dialog, Dialog::None),
":focused/message" => matches!(app.dialog, Dialog::Message(..)),
":focused/add_device" => matches!(app.dialog, Dialog::Device(..)),
":focused/browser" => app.dialog.browser().is_some(),
":focused/pool/import" => matches!(app.pool.mode, Some(PoolMode::Import(..))),
":focused/pool/export" => matches!(app.pool.mode, Some(PoolMode::Export(..))),
":focused/pool/rename" => matches!(app.pool.mode, Some(PoolMode::Rename(..))),
":focused/pool/length" => matches!(app.pool.mode, Some(PoolMode::Length(..))),
":focused/clip" => !app.editor_focused() && matches!(app.selection(),
Selection::TrackClip{..}),
":focused/track" => !app.editor_focused() && matches!(app.selection(),
Selection::Track(..)),
":focused/scene" => !app.editor_focused() && matches!(app.selection(),
Selection::Scene(..)),
":focused/mix" => !app.editor_focused() && matches!(app.selection(),
Selection::Mix),
};
ItemTheme => {
":_theme_stub" => Default::default()
};
Dialog => {
":dialog/none" => Dialog::None,
":dialog/options" => Dialog::Options,
":dialog/device" => Dialog::Device(0),
":dialog/device/prev" => Dialog::Device(0),
":dialog/device/next" => Dialog::Device(0),
":dialog/help" => Dialog::Help(0),
":dialog/menu" => Dialog::Menu(0),
":dialog/save" => Dialog::Browser(BrowserTarget::SaveProject,
Browser::new(None).unwrap().into()),
":dialog/load" => Dialog::Browser(BrowserTarget::LoadProject,
Browser::new(None).unwrap().into()),
":dialog/import/clip" => Dialog::Browser(BrowserTarget::ImportClip(Default::default()),
Browser::new(None).unwrap().into()),
":dialog/export/clip" => Dialog::Browser(BrowserTarget::ExportClip(Default::default()),
Browser::new(None).unwrap().into()),
":dialog/import/sample" => Dialog::Browser(BrowserTarget::ImportSample(Default::default()),
Browser::new(None).unwrap().into()),
":dialog/export/sample" => Dialog::Browser(BrowserTarget::ExportSample(Default::default()),
Browser::new(None).unwrap().into()),
};
Selection => {
":select/scene" => app.selection().select_scene(app.tracks().len()),
":select/scene/next" => app.selection().select_scene_next(app.scenes().len()),
":select/scene/prev" => app.selection().select_scene_prev(),
":select/track" => app.selection().select_track(app.tracks().len()),
":select/track/next" => app.selection().select_track_next(app.tracks().len()),
":select/track/prev" => app.selection().select_track_prev(),
};
Option<u7> => {
":editor/pitch" => Some(
(app.editor().as_ref().map(|e|e.get_note_pos()).unwrap() as u8).into()
)
};
Option<usize> => {
":selected/scene" => app.selection().scene(),
":selected/track" => app.selection().track(),
};
Option<Arc<RwLock<MidiClip>>> => {
":selected/clip" => if let Selection::TrackClip { track, scene } = app.selection() {
app.scenes()[*scene].clips[*track].clone()
} else {
None
}
};
Color => {
("g", n: u8) => Color::Rgb(n, n, n),
("rgb", red: u8, green: u8, blue: u8) => Color::Rgb(red, green, blue),
":color/bg" => Color::Rgb(28, 32, 36),
};
}
dsl_ns! { num |app: App|
u8;
u16 => {
":w/sidebar" => app.project.w_sidebar(app.editor().is_some()),
":h/sample-detail" => 6.max(app.height() as u16 * 3 / 9),
};
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;
}
fn wrap_dialog (dialog: impl Content<TuiOut>) -> impl Content<TuiOut> {
Fixed::xy(70, 23, Tui::fg_bg(Rgb(255,255,255), Rgb(16,16,16), Bsp::b(
Repeat(" "), Outer(true, Style::default().fg(Tui::g(96))).enclose(dialog))))
@ -495,38 +355,6 @@ impl ScenesView for App {
fn h_scenes (&self) -> u16 { (self.height() as u16).saturating_sub(20) }
}
macro_rules!dsl_words((|$state:ident|->$Type:ty$({
$($word:literal => $body:expr),* $(,)?
})?)=>{
const WORDS: DslNsMap<'t, fn (&'t Self)->Perhaps<$Type>> = DslNsMap::new(&[$(
$(($word, |$state: &Self|{Ok(Some($body))})),*
)? ]);
});
macro_rules!dsl_exprs((|$state:ident|->$Type:ty$({
$($name:literal ($($arg:ident:$ty:ty),* $(,)?) => $body:expr),* $(,)?
})?)=>{
const EXPRS: DslNsMap<'t, fn (&'t Self, &str)->Perhaps<$Type>> = 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))
})),*
)? ]);
});
mod app_view; pub use self::app_view::*;
mod app_bind; pub use self::app_bind::*;
//#[dizzle::ns]
//#[dizzle::ns::include(TuiOut)]

View file

@ -1,8 +1,7 @@
use crate::*;
impl<'t> DslNs<'t, AppCommand> for App {
dsl_exprs!(|app| -> AppCommand {
});
dsl_exprs!(|app| -> AppCommand { /* TODO */ });
dsl_words!(|app| -> AppCommand {
"x/inc" => todo!(),
"x/dec" => todo!(),
@ -11,6 +10,21 @@ impl<'t> DslNs<'t, AppCommand> for App {
"confirm" => todo!(),
});
}
#[derive(Debug)]
pub enum AppCommand { /* TODO */ }
#[tengri_proc::command(Option<Dialog>)]
impl DialogCommand {
fn open (dialog: &mut Option<Dialog>, new: Dialog) -> Perhaps<Self> {
*dialog = Some(new);
Ok(None)
}
fn close (dialog: &mut Option<Dialog>) -> Perhaps<Self> {
*dialog = None;
Ok(None)
}
}
//AppCommand => {
//("x/inc" /

122
crates/app/app_data.rs Normal file
View file

@ -0,0 +1,122 @@
use crate::*;
impl<'t> DslNs<'t, Arc<str>> for App {
dsl_exprs!(|app| -> Arc<str> {});
dsl_words!(|app| -> Arc<str> {});
fn from_literal <D: Dsl> (&self, dsl: &D) -> Perhaps<Arc<str>> {
Ok(dsl.src()?.map(|x|x.into()))
}
}
impl<'t> DslNs<'t, bool> for App {
dsl_words!(|app| -> bool {
":focused/editor" => app.project.editor.is_some(),
":focused/dialog" => !matches!(app.dialog, Dialog::None),
":focused/message" => matches!(app.dialog, Dialog::Message(..)),
":focused/add_device" => matches!(app.dialog, Dialog::Device(..)),
":focused/browser" => app.dialog.browser().is_some(),
":focused/pool/import" => matches!(app.pool.mode, Some(PoolMode::Import(..))),
":focused/pool/export" => matches!(app.pool.mode, Some(PoolMode::Export(..))),
":focused/pool/rename" => matches!(app.pool.mode, Some(PoolMode::Rename(..))),
":focused/pool/length" => matches!(app.pool.mode, Some(PoolMode::Length(..))),
":focused/clip" => !app.editor_focused() && matches!(app.selection(),
Selection::TrackClip{..}),
":focused/track" => !app.editor_focused() && matches!(app.selection(),
Selection::Track(..)),
":focused/scene" => !app.editor_focused() && matches!(app.selection(),
Selection::Scene(..)),
":focused/mix" => !app.editor_focused() && matches!(app.selection(),
Selection::Mix),
});
//fn from_literal <D: Dsl> (&self, dsl: &D) -> Perhaps<Arc<str>> {
//TODO
//}
}
impl<'t> DslNs<'t, ItemTheme> for App {}
impl<'t> DslNs<'t, Dialog> for App {
dsl_words!(|app| -> Dialog {
":dialog/none" => Dialog::None,
":dialog/options" => Dialog::Options,
":dialog/device" => Dialog::Device(0),
":dialog/device/prev" => Dialog::Device(0),
":dialog/device/next" => Dialog::Device(0),
":dialog/help" => Dialog::Help(0),
":dialog/menu" => Dialog::Menu(0),
":dialog/save" => Dialog::Browser(BrowserTarget::SaveProject,
Browser::new(None).unwrap().into()),
":dialog/load" => Dialog::Browser(BrowserTarget::LoadProject,
Browser::new(None).unwrap().into()),
":dialog/import/clip" => Dialog::Browser(BrowserTarget::ImportClip(Default::default()),
Browser::new(None).unwrap().into()),
":dialog/export/clip" => Dialog::Browser(BrowserTarget::ExportClip(Default::default()),
Browser::new(None).unwrap().into()),
":dialog/import/sample" => Dialog::Browser(BrowserTarget::ImportSample(Default::default()),
Browser::new(None).unwrap().into()),
":dialog/export/sample" => Dialog::Browser(BrowserTarget::ExportSample(Default::default()),
Browser::new(None).unwrap().into()),
});
}
impl<'t> DslNs<'t, Selection> for App {
dsl_words!(|app| -> Selection {
":select/scene" => app.selection().select_scene(app.tracks().len()),
":select/scene/next" => app.selection().select_scene_next(app.scenes().len()),
":select/scene/prev" => app.selection().select_scene_prev(),
":select/track" => app.selection().select_track(app.tracks().len()),
":select/track/next" => app.selection().select_track_next(app.tracks().len()),
":select/track/prev" => app.selection().select_track_prev(),
});
}
impl<'t> DslNs<'t, Color> for App {
dsl_words!(|app| -> Color {
":color/bg" => Color::Rgb(28, 32, 36),
});
dsl_exprs!(|app| -> Color {
"g" (n: u8) => Color::Rgb(n, n, n),
"rgb" (r: u8, g: u8, b: u8) => Color::Rgb(r, g, b),
});
}
impl<'t> DslNs<'t, Option<u7>> for App {
dsl_words!(|app| -> Option<u7> {
":editor/pitch" => Some(
(app.editor().as_ref().map(|e|e.get_note_pos()).unwrap() as u8).into()
)
});
}
impl<'t> DslNs<'t, Option<usize>> for App {
dsl_words!(|app| -> Option<usize> {
":selected/scene" => app.selection().scene(),
":selected/track" => app.selection().track(),
});
}
impl<'t> DslNs<'t, Option<Arc<RwLock<MidiClip>>>> for App {
dsl_words!(|app| -> Option<Arc<RwLock<MidiClip>>> {
":selected/clip" => if let Selection::TrackClip { track, scene } = app.selection() {
app.scenes()[*scene].clips[*track].clone()
} else {
None
}
});
}
dsl_ns! { num |app: App|
u8;
u16 => {
":w/sidebar" => app.project.w_sidebar(app.editor().is_some()),
":h/sample-detail" => 6.max(app.height() as u16 * 3 / 9),
};
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;
}

24
crates/app/app_deps.rs Normal file
View file

@ -0,0 +1,24 @@
pub use ::{
tek_engine::*,
tek_device,
tek_device::*,
tengri::{
Usually, Perhaps, Has, MaybeHas, has, maybe_has,
dsl::*, input::*, output::*, tui::*,
tui::ratatui,
tui::ratatui::prelude::buffer::Cell,
tui::ratatui::prelude::Color::{self, *},
tui::ratatui::prelude::{Style, Stylize, Buffer, Modifier},
tui::crossterm,
tui::crossterm::event::{Event, KeyCode::{self, *}},
},
std::{
path::{Path, PathBuf},
sync::{Arc, RwLock},
sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed},
error::Error,
collections::BTreeMap,
fmt::Write,
},
xdg::BaseDirectories,
};

View file

@ -2,17 +2,21 @@ use crate::*;
impl<'t> DslNs<'t, Box<dyn Render<TuiOut>>> for App {
dsl_exprs!(|app| -> Box<dyn Render<TuiOut>> {
"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)),
"text" (tail: Arc<str>) => Box::new(tail),
"bsp/n" (a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::n(a, b)),
"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)),
"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)),
"bsp/n" (a: Box<dyn Render<TuiOut>>, b: Box<dyn Render<TuiOut>>) => Box::new(Bsp::n(a, b)),
"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/nw" (x: Box<dyn Render<TuiOut>>) => Box::new(Align::nw(x)),
"align/ne" (x: Box<dyn Render<TuiOut>>) => Box::new(Align::ne(x)),
"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)),
@ -21,43 +25,46 @@ impl<'t> DslNs<'t, Box<dyn Render<TuiOut>>> for App {
"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)),
"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)),
"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)),
"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)),
});
dsl_words!(|app| -> Box<dyn Render<TuiOut>> {
":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>)|{
Fixed::y(height, Min::x(30, 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 bg = if index == 0 { Rgb(70,70,70) } else { Rgb(50,50,50) };
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);
let field_name = Fill::x(Align::w(Tui::fg(fg1, name)));
let field_id = Fill::x(Align::e(Tui::fg(fg2, id)));
let field_info = Fill::x(Align::w(info));
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(
Bsp::a(field_name, field_id), field_info)))));
}
})))
}),
":sessions" => Box::new(Fixed::y(6, Min::x(30, 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) };
let bg = if index == 0 { Rgb(50,50,50) } else { Rgb(40,40,40) };
add(&Fixed::y(2, Fill::x(Tui::bg(bg, Align::w(Tui::fg(fg, name))))));
}
}
@ -82,7 +89,7 @@ impl<'t> DslNs<'t, Box<dyn Render<TuiOut>>> for App {
Fill::x(Tui::bg(bg, Bsp::e(lb, Bsp::w(rb, "FIXME device name")))) }))) },
});
/// Resolve an expression if known.
fn from_expr <D: Dsl> (&'t self, dsl: D) -> Perhaps<Box<dyn Render<TuiOut>>> {
fn from_expr <D: Dsl> (&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 {
@ -93,7 +100,7 @@ impl<'t> DslNs<'t, Box<dyn Render<TuiOut>>> for App {
return Ok(None)
}
/// Resolve a symbol if known.
fn from_word <D: Dsl> (&'t self, dsl: D) -> Perhaps<Box<dyn Render<TuiOut>>> {
fn from_word <D: Dsl> (&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) {
@ -101,7 +108,11 @@ impl<'t> DslNs<'t, Box<dyn Render<TuiOut>>> for App {
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) } }
for (word, get) in Self::WORDS.0 {
if dsl == *word {
return get(self)
}
}
}
return Ok(None)
}

2
deps/tengri vendored

@ -1 +1 @@
Subproject commit 2557a0d253dfe45eab001dcc08ebc66c2c6715d3
Subproject commit d92e5efdd0e009ba0492a75b3aad0b3c142b9056

16
tek.edn
View file

@ -1,12 +1,16 @@
(mode :menu (keys :x :y :confirm) :menu)
(keys :x (@left x/dec) (@right x/inc))
(keys :y (@up y/dec) (@down y/inc))
(keys :confirm (@enter confirm))
(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"))))))))
(keys :confirm (@enter confirm))
(view :menu (bg (g 40) (bsp/s :ports/out (bsp/n :ports/in
(bg (g 30) (fill/xy (align/c (bg (g 40) (bsp/s
(text CONTINUE-SESSION)
(bsp/s (text LOAD-OTHER-SESSION)
(text BEGIN-NEW-SESSION)))))))))))
(view :ports/out (fill/x (fixed/y 3 (bsp/a (fill/x (align/w (text L-AUDIO-OUT)))
(bsp/a (text MIDI-OUT) (fill/x (align/e (text AUDIO-OUT R))))))))
(view :ports/in (fill/x (fixed/y 3 (bsp/a (fill/x (align/w (text L-AUDIO-IN)))
(bsp/a (text MIDI-IN) (fill/x (align/e (text AUDIO-IN-R))))))))
(view :browse (bsp/s (padding/xy 3 1 :browse-title) (enclose (fg (g 96)) browser)))
(keys :help (@f1 dialog :help))