fix: runs, somehow

This commit is contained in:
i do not exist 2026-07-02 07:29:31 +03:00
parent 89f8d073eb
commit 3b1c31c78d
5 changed files with 209 additions and 189 deletions

View file

@ -4,6 +4,7 @@ pub mod bind; pub use self::bind::*;
pub mod cli; pub use self::cli::*;
pub mod config; pub use self::config::*;
pub mod view; pub use self::view::*;
pub mod draw; pub use self::draw::*;
pub mod mode; pub use self::mode::*;
/// Total application state.
@ -278,42 +279,6 @@ impl HasJack<'static> for App {
fn jack (&self) -> &Jack<'static> { &self.jack }
}
//impl_handle!(TuiIn: |self: App, input|{
//let commands = tek_collect_commands(self, input)?;
//let history = tek_execute_commands(self, commands)?;
//self.history.extend(history.into_iter());
//Ok(None)
//});
//fn tek_collect_commands (app: &App, input: &TuiIn) -> Usually<Vec<AppCommand>> {
//let mut commands = vec![];
//for id in app.mode.keys.iter() {
//if let Some(event_map) = app.config.binds.clone().read().unwrap().get(id.as_ref())
//&& let Some(bindings) = event_map.query(input.event()) {
//for binding in bindings {
//for command in binding.commands.iter() {
//if let Some(command) = app.namespace(command)? as Option<AppCommand> {
//commands.push(command)
//}
//}
//}
//}
//}
//Ok(commands)
//}
//fn tek_execute_commands (
//app: &mut App, commands: Vec<AppCommand>
//) -> Usually<Vec<(AppCommand, Option<AppCommand>)>> {
//let mut history = vec![];
//for command in commands.into_iter() {
//let result = command.act(app);
//match result { Err(err) => { history.push((command, None)); return Err(err) }
//Ok(undo) => { history.push((command, undo)); } };
//}
//Ok(history)
//}
def_command!(AppCommand: |app: App| {
Nop => Ok(None),
Cancel => todo!(), // TODO delegate:
@ -430,140 +395,6 @@ impl<'a> Namespace<'a, AppCommand> for App {
});
}
impl Interpret<Tui, XYWH<u16>> for App {
fn interpret_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression) -> Usually<XYWH<u16>> {
tek_expr(self, to, lang)
}
fn interpret_word <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression) -> Usually<XYWH<u16>> {
tek_word(self, to, lang)
}
}
fn tek_expr (state: &App, to: &mut Tui, lang: &impl Expression) -> Usually<XYWH<u16>> {
if let Some(area) = eval_view(state, to, lang)? {
Ok(area)
} else if let Some(area) = eval_view_tui(state, to, lang)? {
Ok(area)
} else {
Err(format!("App::interpret_expr: unexpected: {lang:?}").into())
}
}
fn tek_word (state: &App, to: &mut Tui, dsl: &impl Expression) -> Usually<XYWH<u16>> {
let mut frags = dsl.src()?.unwrap().split("/");
match frags.next() {
Some(":logo") => view_logo().draw(to),
Some(":status") => h_exact(1, "TODO: Status Bar").draw(to),
Some(":meters") => match frags.next() {
Some("input") => bg(Rgb(30, 30, 30), h_full(origin_s("Input Meters"))).draw(to),
Some("output") => bg(Rgb(30, 30, 30), h_full(origin_s("Output Meters"))).draw(to),
_ => panic!()
},
Some(":tracks") => match frags.next() {
None => "TODO tracks".draw(to),
Some("names") => state.project.view_track_names(state.color.clone()).draw(to),//bg(Rgb(40, 40, 40), w_full(origin_w("Track Names")))),
Some("inputs") => bg(Rgb(40, 40, 40), w_full(origin_w("Track Inputs"))).draw(to),
Some("devices") => bg(Rgb(40, 40, 40), w_full(origin_w("Track Devices"))).draw(to),
Some("outputs") => bg(Rgb(40, 40, 40), w_full(origin_w("Track Outputs"))).draw(to),
_ => panic!()
},
Some(":scenes") => match frags.next() {
None => "TODO scenes".draw(to),
Some(":scenes/names") => "TODO Scene Names".draw(to),
_ => panic!()
},
Some(":editor") => "TODO Editor".draw(to),
Some(":dialog") => match frags.next() {
Some("menu") => if let Dialog::Menu(selected, items) = &state.dialog {
let items = items.clone();
let selected = selected;
Some(wh_full(thunk(move|to: &mut Tui|{
for (index, MenuItem(item, _)) in items.0.iter().enumerate() {
y_push((2 * index) as u16,fg_bg(
if *selected == index { Rgb(240,200,180) } else { Rgb(200, 200, 200) },
if *selected == index { Rgb(80, 80, 50) } else { Rgb(30, 30, 30) },
h_exact(2, origin_n(w_full(item)))
)).draw(to);
}
Ok(to.area().into())
})))
} else {
None
}.draw(to),
_ => unimplemented!("App::interpret_word: {dsl:?} ({frags:?})"),
},
Some(":templates") => {
let modes = state.config.modes.clone();
let height = (modes.read().unwrap().len() * 2) as u16;
h_exact(height, w_min(Some(30), thunk(move |to: &mut Tui|{
for (index, (id, profile)) in modes.read().unwrap().iter().enumerate() {
let b = 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 = w_full(origin_w(fg(fg1, name)));
let field_id = w_full(origin_e(fg(fg2, id)));
let field_info = w_full(origin_w(info));
y_push((2 * index) as u16,
h_exact(2, w_full(bg(b, south(
above(field_name, field_id),
field_info
))))).draw(to);
}
Ok(to.area().into())
})))
}.draw(to),
Some(":sessions") => view_sessions().draw(to),
Some(":browse/title") => w_full(origin_w(field_v(ItemTheme::default(),
match state.dialog.browser_target().unwrap() {
BrowseTarget::SaveProject => "Save project:",
BrowseTarget::LoadProject => "Load project:",
BrowseTarget::ImportSample(_) => "Import sample:",
BrowseTarget::ExportSample(_) => "Export sample:",
BrowseTarget::ImportClip(_) => "Import clip:",
BrowseTarget::ExportClip(_) => "Export clip:",
}, h_exact(1, fg(g(96), x_repeat("🭻")))
))).draw(to),
Some(":device") => {
let selected = state.dialog.device_kind().unwrap();
south(bold(true, "Add device"), iter_south(
move||device_kinds().iter(),
move|_label: &&'static str, i|{
let b = if i == selected { Rgb(64,128,32) } else { Rgb(0,0,0) };
let l = if i == selected { "[ " } else { " " };
let r = if i == selected { " ]" } else { " " };
w_full(bg(b, east(l, west(r, "FIXME device name")))) })).draw(to)
},
Some(":debug") => h_exact(1, format!("[{:?}]", to.area())).draw(to),
Some(_) => {
let views = state.config.views.read().unwrap();
if let Some(dsl) = views.get(dsl.src()?.unwrap()) {
let dsl = dsl.clone();
std::mem::drop(views);
state.interpret(to, &dsl)
} else {
unimplemented!("{dsl:?}");
}
},
_ => unreachable!()
}
}
impl HasTrackScroll for App {
fn track_scroll (&self) -> usize {
self.project.track_scroll()
@ -587,3 +418,39 @@ impl ScenesView for App {
(self.size.h() as u16).saturating_sub(20)
}
}
//impl_handle!(TuiIn: |self: App, input|{
//let commands = tek_collect_commands(self, input)?;
//let history = tek_execute_commands(self, commands)?;
//self.history.extend(history.into_iter());
//Ok(None)
//});
//fn tek_collect_commands (app: &App, input: &TuiIn) -> Usually<Vec<AppCommand>> {
//let mut commands = vec![];
//for id in app.mode.keys.iter() {
//if let Some(event_map) = app.config.binds.clone().read().unwrap().get(id.as_ref())
//&& let Some(bindings) = event_map.query(input.event()) {
//for binding in bindings {
//for command in binding.commands.iter() {
//if let Some(command) = app.namespace(command)? as Option<AppCommand> {
//commands.push(command)
//}
//}
//}
//}
//}
//Ok(commands)
//}
//fn tek_execute_commands (
//app: &mut App, commands: Vec<AppCommand>
//) -> Usually<Vec<(AppCommand, Option<AppCommand>)>> {
//let mut history = vec![];
//for command in commands.into_iter() {
//let result = command.act(app);
//match result { Err(err) => { history.push((command, None)); return Err(err) }
//Ok(undo) => { history.push((command, undo)); } };
//}
//Ok(history)
//}

139
src/app/draw.rs Normal file
View file

@ -0,0 +1,139 @@
use crate::*;
impl Interpret<Tui, XYWH<u16>> for App {
fn interpret_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression)
-> Usually<XYWH<u16>>
{
tek_draw_expr(self, to, lang)
}
fn interpret_word <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression)
-> Usually<XYWH<u16>>
{
tek_draw_word(self, to, lang)
}
}
fn tek_draw_expr (state: &App, to: &mut Tui, lang: &impl Expression)
-> Usually<XYWH<u16>>
{
if let Some(area) = eval_view(state, to, lang)? {
Ok(area)
} else if let Some(area) = eval_view_tui(state, to, lang)? {
Ok(area)
} else {
Err(format!("App::interpret_expr: unexpected: {lang:?}").into())
}
}
fn tek_draw_word (state: &App, to: &mut Tui, dsl: &impl Expression)
-> Usually<XYWH<u16>>
{
let mut frags = dsl.src()?.unwrap().split("/");
match frags.next() {
Some(":logo") => view_logo().draw(to),
Some(":meters") => draw_meter_section(to, frags),
Some(":tracks") => draw_tracks(to, frags, state),
Some(":scenes") => draw_scenes(to, frags),
Some(":dialog") => draw_dialog(to, frags, state, dsl),
Some(":templates") => draw_templates(to, frags, state),
Some(":sessions") => view_sessions().draw(to),
Some(":browse/title") => view_browse_title(state).draw(to),
Some(":device") => view_device(state).draw(to),
Some(":status") => h_exact(1, "TODO: Status Bar").draw(to),
Some(":editor") => "TODO Editor".draw(to),
Some(":debug") => h_exact(1, format!("[{:?}]", to.area())).draw(to),
Some(_) => {
let views = state.config.views.read().unwrap();
if let Some(dsl) = views.get(dsl.src()?.unwrap()) {
let dsl = dsl.clone();
std::mem::drop(views);
state.interpret(to, &dsl)
} else {
unimplemented!("{dsl:?}");
}
},
_ => unreachable!()
}
}
pub fn draw_meter_section (to: &mut Tui, mut frags: std::str::Split<&str>)
-> Usually<XYWH<u16>>
{
match frags.next() {
Some("input") => bg(Rgb(30, 30, 30), h_full(origin_s("Input Meters"))).draw(to),
Some("output") => bg(Rgb(30, 30, 30), h_full(origin_s("Output Meters"))).draw(to),
_ => panic!()
}
}
pub fn draw_tracks (to: &mut Tui, mut frags: std::str::Split<&str>, state: &App)
-> Usually<XYWH<u16>>
{
match frags.next() {
None => "TODO tracks".draw(to),
Some("names") => state.project.view_track_names(state.color.clone()).draw(to),//bg(Rgb(40, 40, 40), w_full(origin_w("Track Names")))),
Some("inputs") => bg(Rgb(40, 40, 40), w_full(origin_w("Track Inputs"))).draw(to),
Some("devices") => bg(Rgb(40, 40, 40), w_full(origin_w("Track Devices"))).draw(to),
Some("outputs") => bg(Rgb(40, 40, 40), w_full(origin_w("Track Outputs"))).draw(to),
_ => panic!()
}
}
pub fn draw_scenes (to: &mut Tui, mut frags: std::str::Split<&str>)
-> Usually<XYWH<u16>>
{
match frags.next() {
None => "TODO Scenes".draw(to),
Some(":scenes/names") => "TODO Scene Names".draw(to),
_ => panic!()
}
}
pub fn draw_dialog (
to: &mut Tui, mut frags: std::str::Split<&str>, state: &App, dsl: &impl Expression
) -> Usually<XYWH<u16>> {
match frags.next() {
Some("menu") => if let Dialog::Menu(selected, items) = &state.dialog {
let items = items.clone();
let selected = selected;
Some(wh_full(thunk(move|to: &mut Tui|{
for (index, MenuItem(item, _)) in items.0.iter().enumerate() {
y_push((2 * index) as u16,fg_bg(
if *selected == index { Rgb(240,200,180) } else { Rgb(200, 200, 200) },
if *selected == index { Rgb(80, 80, 50) } else { Rgb(30, 30, 30) },
h_exact(2, origin_n(w_full(item)))
)).draw(to);
}
Ok(to.area().into())
})))
} else {
None
}.draw(to),
_ => unimplemented!("App::interpret_word: {dsl:?} ({frags:?})"),
}
}
pub fn draw_templates (to: &mut Tui, frags: std::str::Split<&str>, state: &App)
-> Usually<XYWH<u16>>
{
let modes = state.config.modes.clone();
let height = (modes.read().unwrap().len() * 2) as u16;
h_exact(height, w_min(Some(30), thunk(move |to: &mut Tui|{
for (index, (id, profile)) in modes.read().unwrap().iter().enumerate() {
let b = 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 = w_full(origin_w(fg(fg1, name)));
let field_id = w_full(origin_e(fg(fg2, id)));
let field_info = w_full(origin_w(info));
y_push((2 * index) as u16,
h_exact(2, w_full(bg(b, south(
above(field_name, field_id),
field_info
))))).draw(to);
}
Ok(to.area().into())
}))).draw(to)
}

View file

@ -584,3 +584,27 @@ pub fn view_sessions () -> impl Draw<Tui> {
Ok(to.area().into())
})))
}
pub fn view_browse_title (state: &App) -> impl Draw<Tui> {
w_full(origin_w(field_v(ItemTheme::default(),
match state.dialog.browser_target().unwrap() {
BrowseTarget::SaveProject => "Save project:",
BrowseTarget::LoadProject => "Load project:",
BrowseTarget::ImportSample(_) => "Import sample:",
BrowseTarget::ExportSample(_) => "Export sample:",
BrowseTarget::ImportClip(_) => "Import clip:",
BrowseTarget::ExportClip(_) => "Export clip:",
}, h_exact(1, fg(g(96), x_repeat("🭻")))
)))
}
pub fn view_device (state: &App) -> impl Draw<Tui> {
let selected = state.dialog.device_kind().unwrap();
south(bold(true, "Add device"), iter_south(
move||device_kinds().iter(),
move|_label: &&'static str, i|{
let b = if i == selected { Rgb(64,128,32) } else { Rgb(0,0,0) };
let l = if i == selected { "[ " } else { " " };
let r = if i == selected { " ]" } else { " " };
w_full(bg(b, east(l, west(r, "FIXME device name")))) }))
}

View file

@ -87,25 +87,15 @@ pub(crate) use tengri::{
/// Command-line entrypoint.
#[cfg(feature = "cli")]
pub fn main () -> Usually<()> {
tui_setup_panic();
Config::watch(|config|{
Exit::run(|exit|{
let state = Jack::new_run("tek", move|jack|{
let clock = Clock::new(&jack, Some(51.))?;
let project = Arrangement::new(&jack, None, clock, vec![], vec![], vec![], vec![]);
let app = App::new(&jack, project, config, ":menu");
Ok(app)
})?;
let (keyboard, terminal) = tui_io(
exit.as_ref(),
&state,
Duration::from_millis(100),
Duration::from_millis(10),
std::io::stdout(),
)?;
Ok(())
})
})?;
Ok(())
tui_run_main(Jack::new_run("tek", move|jack|{
let clock = Clock::new(&jack, Some(51.))?;
let project = Arrangement::new(&jack, None, clock, vec![], vec![], vec![], vec![]);
let app = App::new(&jack, project, config, ":menu");
Ok(app)
})?)
}).map(|_|())
}
tui_keys!(|self: App, input| {

2
tengri

@ -1 +1 @@
Subproject commit 2ab871d10b26b40d129d62f68ecbd9e960aa4c86
Subproject commit 90ebae0f26fc4524659704df274d0aec815dba2e