mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: namespaces
This commit is contained in:
parent
525a455f7a
commit
f2d6e7724b
4 changed files with 174 additions and 169 deletions
|
|
@ -40,6 +40,12 @@ use std::collections::BTreeMap;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use ::tengri::tui::ratatui::prelude::Position;
|
use ::tengri::tui::ratatui::prelude::Position;
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
mod app_dsl; pub use self::app_dsl::*;
|
||||||
|
macro_rules!dsl_sym(
|
||||||
|
(|$state:ident:$State:ty| -> $type:ty {$($lit:literal => $exp:expr),* $(,)?})=>{
|
||||||
|
impl<'t> DslSymNs<'t, $type> for $State {
|
||||||
|
const NS: DslNs<'t, fn (&'t $State)->$type> =
|
||||||
|
DslNs(&[$(($lit, |$state: &$State|$exp)),*]); } });
|
||||||
mod app_view; pub use self::app_view::*;
|
mod app_view; pub use self::app_view::*;
|
||||||
mod app_ctrl; pub use self::app_ctrl::*;
|
mod app_ctrl; pub use self::app_ctrl::*;
|
||||||
mod app_jack; pub use self::app_jack::*;
|
mod app_jack; pub use self::app_jack::*;
|
||||||
|
|
@ -202,20 +208,13 @@ impl Profile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Various possible dialog modes.
|
/// Various possible dialog modes.
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub enum Dialog {
|
|
||||||
#[default] None,
|
|
||||||
Help(usize),
|
|
||||||
Menu(usize),
|
|
||||||
Device(usize),
|
|
||||||
Message(Arc<str>),
|
|
||||||
Browser(BrowserTarget, Arc<Browser>),
|
|
||||||
Options,
|
|
||||||
}
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn update_clock (&self) {
|
pub fn update_clock (&self) {
|
||||||
ViewCache::update_clock(&self.project.clock.view_cache, self.clock(), self.size.w() > 80)
|
ViewCache::update_clock(&self.project.clock.view_cache, self.clock(), self.size.w() > 80)
|
||||||
}
|
}
|
||||||
|
pub fn focused_editor (&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
has!(Jack<'static>: |self: App|self.jack);
|
has!(Jack<'static>: |self: App|self.jack);
|
||||||
has!(Pool: |self: App|self.pool);
|
has!(Pool: |self: App|self.pool);
|
||||||
|
|
@ -242,12 +241,30 @@ fn unquote (x: &str) -> &str {
|
||||||
//chars.next_back();
|
//chars.next_back();
|
||||||
chars.as_str()
|
chars.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub enum Dialog {
|
||||||
|
#[default] None,
|
||||||
|
Help(usize),
|
||||||
|
Menu(usize),
|
||||||
|
Device(usize),
|
||||||
|
Message(Arc<str>),
|
||||||
|
Browser(BrowserTarget, Arc<Browser>),
|
||||||
|
Options,
|
||||||
|
}
|
||||||
|
|
||||||
impl Dialog {
|
impl Dialog {
|
||||||
fn menu_selected (&self) -> Option<usize> {
|
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 }
|
||||||
}
|
}
|
||||||
fn device_selected (&self) -> Option<usize> {
|
fn device_kind (&self) -> Option<usize> {
|
||||||
if let Self::Device(selected) = self { Some(*selected) } else { None }
|
if let Self::Device(index) = self { Some(*index) } else { None }
|
||||||
|
}
|
||||||
|
fn device_kind_next (&self) -> Option<usize> {
|
||||||
|
self.device_kind().map(|index|(index + 1) % device_kinds().len())
|
||||||
|
}
|
||||||
|
fn device_kind_prev (&self) -> Option<usize> {
|
||||||
|
self.device_kind().map(|index|index.overflowing_sub(1).0.min(device_kinds().len().saturating_sub(1)))
|
||||||
}
|
}
|
||||||
fn message (&self) -> Option<&str> {
|
fn message (&self) -> Option<&str> {
|
||||||
todo!()
|
todo!()
|
||||||
|
|
|
||||||
|
|
@ -1,93 +1,87 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
handle!(TuiIn:|self: App, input|{
|
||||||
//handle!(TuiIn:|self: App, input|self.
|
panic!("{input:?}");
|
||||||
|
//Ok(if let Some(binding) = self.profile.as_ref()
|
||||||
impl App {
|
//.map(|c|c.keys.dispatch(input.event())).flatten()
|
||||||
fn handle_tui_key_with_history (&mut self, input: &TuiIn) -> Perhaps<bool> {
|
//{
|
||||||
panic!("{input:?}");
|
//let binding = binding.clone();
|
||||||
//Ok(if let Some(binding) = self.profile.as_ref()
|
//let undo = binding.command.clone().execute(self)?;
|
||||||
//.map(|c|c.keys.dispatch(input.event())).flatten()
|
//// FIXME failed commands are not persisted in undo history
|
||||||
//{
|
////self.history.push((binding.command.clone(), undo));
|
||||||
//let binding = binding.clone();
|
//Some(true)
|
||||||
//let undo = binding.command.clone().execute(self)?;
|
//} else {
|
||||||
//// FIXME failed commands are not persisted in undo history
|
//None
|
||||||
////self.history.push((binding.command.clone(), undo));
|
//})
|
||||||
//Some(true)
|
})
|
||||||
//} else {
|
dsl_sym!(|app: App| -> isize {
|
||||||
//None
|
":_isize_stub" => -1
|
||||||
//})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type MaybeClip = Option<Arc<RwLock<MidiClip>>>;
|
|
||||||
|
|
||||||
macro_rules!dsl_expose(($Struct:ident { $($fn:ident: $ret:ty = |$self:ident|$body:expr);* $(;)? })=>{
|
|
||||||
#[tengri_proc::expose] impl $Struct { $(fn $fn (&$self) -> $ret { $body })* }
|
|
||||||
});
|
});
|
||||||
|
dsl_sym!(|app: App| -> ItemTheme {
|
||||||
dsl_expose!(App {
|
":_theme_stub" => Default::default()
|
||||||
_isize_stub: isize = |self|todo!();
|
|
||||||
_item_theme_stub: ItemTheme = |self|todo!();
|
|
||||||
w_sidebar: u16 = |self|self.project.w_sidebar(self.editor().is_some());
|
|
||||||
h_sample_detail: u16 = |self|6.max(self.height() as u16 * 3 / 9);
|
|
||||||
focus_editor: bool = |self|self.project.editor.is_some();
|
|
||||||
focus_dialog: bool = |self|!matches!(self.dialog, Dialog::None);
|
|
||||||
focus_message: bool = |self|matches!(self.dialog, Dialog::Message(..));
|
|
||||||
focus_add_device: bool = |self|matches!(self.dialog, Dialog::Device(..));
|
|
||||||
focus_browser: bool = |self|self.dialog.browser().is_some();
|
|
||||||
focus_clip: bool = |self|!self.focus_editor() && matches!(self.selection(), Selection::TrackClip{..});
|
|
||||||
focus_track: bool = |self|!self.focus_editor() && matches!(self.selection(), Selection::Track(..));
|
|
||||||
focus_scene: bool = |self|!self.focus_editor() && matches!(self.selection(), Selection::Scene(..));
|
|
||||||
focus_mix: bool = |self|!self.focus_editor() && matches!(self.selection(), Selection::Mix);
|
|
||||||
focus_pool_import: bool = |self|matches!(self.pool.mode, Some(PoolMode::Import(..)));
|
|
||||||
focus_pool_export: bool = |self|matches!(self.pool.mode, Some(PoolMode::Export(..)));
|
|
||||||
focus_pool_rename: bool = |self|matches!(self.pool.mode, Some(PoolMode::Rename(..)));
|
|
||||||
focus_pool_length: bool = |self|matches!(self.pool.mode, Some(PoolMode::Length(..)));
|
|
||||||
dialog_none: Option<Dialog> = |self|None;
|
|
||||||
dialog_device: Option<Dialog> = |self|Some(Dialog::Device(0)); // TODO
|
|
||||||
dialog_device_prev: Option<Dialog> = |self|Some(Dialog::Device(0)); // TODO
|
|
||||||
dialog_device_next: Option<Dialog> = |self|Some(Dialog::Device(0)); // TODO
|
|
||||||
dialog_help: Option<Dialog> = |self|Some(Dialog::Help(0));
|
|
||||||
dialog_menu: Option<Dialog> = |self|Some(Dialog::Menu(0));
|
|
||||||
dialog_save: Option<Dialog> = |self|Some(Dialog::Browser(BrowserTarget::SaveProject, Browser::new(None).unwrap().into()));
|
|
||||||
dialog_load: Option<Dialog> = |self|Some(Dialog::Browser(BrowserTarget::LoadProject, Browser::new(None).unwrap().into()));
|
|
||||||
dialog_import_clip: Option<Dialog> = |self|Some(Dialog::Browser(BrowserTarget::ImportClip(Default::default()), Browser::new(None).unwrap().into()));
|
|
||||||
dialog_export_clip: Option<Dialog> = |self|Some(Dialog::Browser(BrowserTarget::ExportClip(Default::default()), Browser::new(None).unwrap().into()));
|
|
||||||
dialog_import_sample: Option<Dialog> = |self|Some(Dialog::Browser(BrowserTarget::ImportSample(Default::default()), Browser::new(None).unwrap().into()));
|
|
||||||
dialog_export_sample: Option<Dialog> = |self|Some(Dialog::Browser(BrowserTarget::ExportSample(Default::default()), Browser::new(None).unwrap().into()));
|
|
||||||
dialog_options: Option<Dialog> = |self|Some(Dialog::Options);
|
|
||||||
editor_pitch: Option<u7> = |self|Some((self.editor().as_ref().map(|e|e.get_note_pos()).unwrap() as u8).into());
|
|
||||||
scene_count: usize = |self|self.scenes().len();
|
|
||||||
scene_selected: Option<usize> = |self|self.selection().scene();
|
|
||||||
track_count: usize = |self|self.tracks().len();
|
|
||||||
track_selected: Option<usize> = |self|self.selection().track();
|
|
||||||
select_scene: Selection = |self|self.selection().select_scene(self.tracks().len());
|
|
||||||
select_scene_next: Selection = |self|self.selection().select_scene_next(self.scenes().len());
|
|
||||||
select_scene_prev: Selection = |self|self.selection().select_scene_prev();
|
|
||||||
select_track: Selection = |self|self.selection().select_track(self.tracks().len());
|
|
||||||
select_track_next: Selection = |self|self.selection().select_track_next(self.tracks().len());
|
|
||||||
select_track_prev: Selection = |self|self.selection().select_track_prev();
|
|
||||||
clip_selected: Option<Arc<RwLock<MidiClip>>> = |self|match self.selection() {
|
|
||||||
Selection::TrackClip { track, scene } => self.scenes()[*scene].clips[*track].clone(),
|
|
||||||
_ => None };
|
|
||||||
device_kind: usize = |self|if let Dialog::Device(index) = self.dialog {
|
|
||||||
index } else { 0 };
|
|
||||||
device_kind_next: usize = |self|if let Dialog::Device(index) = self.dialog {
|
|
||||||
(index + 1) % device_kinds().len() } else { 0 };
|
|
||||||
device_kind_prev: usize = |self|if let Dialog::Device(index) = self.dialog {
|
|
||||||
index.overflowing_sub(1).0.min(device_kinds().len().saturating_sub(1))
|
|
||||||
} else { 0 };
|
|
||||||
});
|
});
|
||||||
|
dsl_sym!(|app: App| -> u16{
|
||||||
macro_rules!dsl_bind(($Command:ident: $State:ident {
|
":w/sidebar" => app.project.w_sidebar(app.editor().is_some()),
|
||||||
$($fn:ident = |$state:ident $(, $arg:ident:$ty:ty)*|$body:expr);* $(;)?
|
":h/sample-detail" => 6.max(app.height() as u16 * 3 / 9),
|
||||||
})=>{
|
});
|
||||||
handle!(TuiIn: |self: $State, input|self.handle_tui_key_with_history(input));
|
dsl_sym!(|app: App| -> usize {
|
||||||
#[tengri_proc::command(App)] impl $Command {
|
":scene-count" => app.scenes().len(),
|
||||||
$(fn $fn ($state: &mut $State $(, $arg:$ty)*) -> Perhaps<Self> { $body })*
|
":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),
|
||||||
|
});
|
||||||
|
dsl_sym!(|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/clip" => !app.focused_editor() && matches!(app.selection(), Selection::TrackClip{..}),
|
||||||
|
":focused/track" => !app.focused_editor() && matches!(app.selection(), Selection::Track(..)),
|
||||||
|
":focused/scene" => !app.focused_editor() && matches!(app.selection(), Selection::Scene(..)),
|
||||||
|
":focused/mix" => !app.focused_editor() && matches!(app.selection(), Selection::Mix),
|
||||||
|
":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(..))),
|
||||||
|
});
|
||||||
|
dsl_sym!(|app: 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()),
|
||||||
|
});
|
||||||
|
dsl_sym!(|app: 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(),
|
||||||
|
});
|
||||||
|
dsl_sym!(|app: App| -> Option<u7> {
|
||||||
|
":editor/pitch" => Some((app.editor().as_ref().map(|e|e.get_note_pos()).unwrap() as u8).into())
|
||||||
|
})
|
||||||
|
dsl_sym!(|app: App| -> Option<usize> {
|
||||||
|
":selected/scene" => app.selection().scene(),
|
||||||
|
":selected/track" => app.selection().track(),
|
||||||
|
});
|
||||||
|
dsl_sym!(|app: App| -> Option<Arc<RwLock<MidiClip>>> {
|
||||||
|
":selected/clip" => if let Selection::TrackClip { track, scene } = app.selection() {
|
||||||
|
app.scenes()[*scene].clips[*track].clone(),
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
dsl_bind!(AppCommand: App {
|
dsl_bind!(AppCommand: App {
|
||||||
enqueue = |app, clip: Option<Arc<RwLock<MidiClip>>>| { todo!() };
|
enqueue = |app, clip: Option<Arc<RwLock<MidiClip>>>| { todo!() };
|
||||||
history = |app, delta: isize| { todo!() };
|
history = |app, delta: isize| { todo!() };
|
||||||
|
|
|
||||||
32
crates/app/app_dsl.rs
Normal file
32
crates/app/app_dsl.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
macro_rules!dsl_sym(
|
||||||
|
(|$state:ident:$State:ty| -> $type:ty {$($lit:literal => $exp:expr),* $(,)?})=>{
|
||||||
|
impl<'t> DslSymNs<'t, $type> for $State {
|
||||||
|
const NS: DslNs<'t, fn (&'t $State)->$type> =
|
||||||
|
DslNs(&[$(($lit, |$state: &$State|$exp)),*]); } });
|
||||||
|
|
||||||
|
pub trait DslSymNs<'t, T: 't>: 't {
|
||||||
|
const NS: DslNs<'t, fn (&'t Self)->T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DslNs<'t, T: 't>(pub &'t [(&'t str, T)]);
|
||||||
|
|
||||||
|
pub type DslCb = fn (&App) -> Box<dyn Render<TuiOut>>;
|
||||||
|
|
||||||
|
impl<'t, D: Dsl> std::ops::Index<D> for DslNs<'t, DslCb> {
|
||||||
|
type Output = DslCb;
|
||||||
|
fn index (&self, index: D) -> &Self::Output {
|
||||||
|
if let Ok(Some(symbol)) = index.src() {
|
||||||
|
for (key, value) in self.0.iter() {
|
||||||
|
if symbol == *key {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&(view_nil as DslCb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn view_nil (_: &App) -> Box<dyn Render<TuiOut>> {
|
||||||
|
Box::new(Fill::xy("·"))
|
||||||
|
}
|
||||||
|
|
@ -1,78 +1,21 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
content!(TuiOut:|self: App|VIEW[":view"](self));//if let Ok(Some(view)) = VIEW[":view"] { view(self) } else { panic!() });
|
content!(TuiOut:|self: App|VIEW[":view"](self));//if let Ok(Some(view)) = VIEW[":view"] { view(self) } else { panic!() });
|
||||||
struct DslNs<'t, T: 't>(&'t [(&'t str, T)]);
|
|
||||||
type DslCb = fn (&App) -> Box<dyn Render<TuiOut>>;
|
|
||||||
impl<'t, D: Dsl> std::ops::Index<D> for DslNs<'t, DslCb> {
|
|
||||||
type Output = DslCb;
|
|
||||||
fn index (&self, index: D) -> &Self::Output {
|
|
||||||
if let Ok(Some(symbol)) = index.src() {
|
|
||||||
for (key, value) in self.0.iter() {
|
|
||||||
if symbol == *key {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&(view_nil as DslCb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub const VIEW: DslNs<'static, DslCb> = DslNs(&[
|
pub const VIEW: DslNs<'static, DslCb> = DslNs(&[
|
||||||
|
(":view", |state|VIEW[":view/menu"](state)),
|
||||||
(":view", |state|Box::new(
|
|
||||||
Fill::xy(Bsp::a(VIEW[":view/content"](state), VIEW[":view/overlay"](state))))),
|
|
||||||
|
|
||||||
(":view/content", |state|Box::new(
|
|
||||||
Fill::xy(ErrorBoundary::new(Ok(Some("·")))))),
|
|
||||||
|
|
||||||
(":view/overlay", |state|match &state.dialog {
|
|
||||||
Dialog::Menu(_) => VIEW[":view/menu"](state),
|
|
||||||
Dialog::Device(_) => VIEW[":view/device"](state),
|
|
||||||
Dialog::Browser(_, _) => VIEW[":view/browse"](state),
|
|
||||||
//Dialog::Help(_) => view_help,
|
|
||||||
//Dialog::Message(_) => view_message,
|
|
||||||
//Dialog::Options => view_options,
|
|
||||||
_ => unimplemented!() }),
|
|
||||||
|
|
||||||
(":view/browse", |state|{
|
|
||||||
let browser = state.dialog.browser().cloned().unwrap();
|
|
||||||
Box::new(Bsp::s(
|
|
||||||
Padding::xy(3, 1, VIEW[":view/browse-title"](state)),
|
|
||||||
Outer(true, Style::default().fg(Tui::g(96)))
|
|
||||||
.enclose(Fill::xy(browser)))) }),
|
|
||||||
|
|
||||||
(":view/browse-title", |state: &App|{
|
|
||||||
let target = state.dialog.browser_target().unwrap();
|
|
||||||
Box::new(Fill::x(Align::w(FieldV(Default::default(), match target {
|
|
||||||
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", |state: &App|{
|
|
||||||
let selected = state.dialog.device_selected().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/menu", |state|{
|
(":view/menu", |state|{
|
||||||
let selected = state.dialog.menu_selected();
|
let selected = state.dialog.menu_selected();
|
||||||
let outputs = Fill::x(Fixed::y(3,
|
let outputs = VIEW[":view/ports/outs"](state);
|
||||||
Bsp::a(Fill::x(Align::w(" L AUDIO OUTS")), Bsp::a("MIDI OUT", Fill::x(Align::e("AUDIO OUTS R "))))));
|
let inputs = VIEW[":view/ports/ins"](state);
|
||||||
let inputs = Fill::x(Fixed::y(3,
|
|
||||||
Bsp::a(Fill::x(Align::w(" L AUDIO INS")), Bsp::a("MIDI INS", Fill::x(Align::e("AUDIO INS R "))))));
|
|
||||||
Box::new(Tui::bg(Rgb(0,0,0), Bsp::s(outputs, Bsp::s(
|
Box::new(Tui::bg(Rgb(0,0,0), Bsp::s(outputs, Bsp::s(
|
||||||
Fill::x(Fixed::y(3, Tui::bg(Rgb(33,33,33), Tui::bold(true, "tek 0.3.0-rc0")))),
|
Fill::x(Fixed::y(3, Tui::bg(Rgb(33,33,33), Tui::bold(true, "tek 0.3.0-rc0")))),
|
||||||
Bsp::n(inputs, Bsp::n(
|
Bsp::n(inputs, Bsp::n(
|
||||||
Fill::x(Fixed::y(3, Tui::bg(Rgb(33,33,33), Bsp::e(Tui::fg(Rgb(255,192,48), "[Enter]"), " new session")))),
|
Fill::x(Fixed::y(3, Tui::bg(Rgb(33,33,33), Bsp::e(Tui::fg(Rgb(255,192,48), "[Enter]"), " new session")))),
|
||||||
Fill::y(Align::n(Fill::x(VIEW[":view/profiles"](state)))))))))) }),
|
Fill::y(Align::n(Fill::x(VIEW[":view/profiles"](state)))))))))) }),
|
||||||
|
(":view/ports/outs", |state|Box::new(Fill::x(Fixed::y(3,
|
||||||
(":view/profiles", |state|Box::new({
|
Bsp::a(Fill::x(Align::w(" L AUDIO OUTS")), Bsp::a("MIDI OUT", Fill::x(Align::e("AUDIO OUTS R ")))))))),
|
||||||
|
(":view/ports/ins", |state|Box::new(Fill::x(Fixed::y(3,
|
||||||
|
Bsp::a(Fill::x(Align::w(" L AUDIO INS")), Bsp::a("MIDI INS", Fill::x(Align::e("AUDIO INS R ")))))))),
|
||||||
|
(":view/profiles", |state: &App|Box::new({
|
||||||
let views = state.config.views.clone();
|
let views = state.config.views.clone();
|
||||||
Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
|
Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
|
||||||
for (index, (id, profile)) in views.read().unwrap().iter().enumerate() {
|
for (index, (id, profile)) in views.read().unwrap().iter().enumerate() {
|
||||||
|
|
@ -83,15 +26,34 @@ pub const VIEW: DslNs<'static, DslCb> = DslNs(&[
|
||||||
Fill::x(Bsp::a(
|
Fill::x(Bsp::a(
|
||||||
Fill::x(Align::w(Tui::fg(Rgb(224,192,128), name))),
|
Fill::x(Align::w(Tui::fg(Rgb(224,192,128), name))),
|
||||||
Fill::x(Align::e(Tui::fg(Rgb(224,128,32), &id))))),
|
Fill::x(Align::e(Tui::fg(Rgb(224,128,32), &id))))),
|
||||||
Fill::x(Align::w(info)))))); } })}))
|
Fill::x(Align::w(info)))))); } })})),
|
||||||
|
(":view/browse", |state: &App|{
|
||||||
|
let browser = state.dialog.browser().cloned().unwrap();
|
||||||
|
Box::new(Bsp::s(
|
||||||
|
Padding::xy(3, 1, VIEW[":view/browse-title"](state)),
|
||||||
|
Outer(true, Style::default().fg(Tui::g(96)))
|
||||||
|
.enclose(Fill::xy(browser)))) }),
|
||||||
|
(":view/browse-title", |state: &App|{
|
||||||
|
let target = state.dialog.browser_target().unwrap();
|
||||||
|
Box::new(Fill::x(Align::w(FieldV(Default::default(), match target {
|
||||||
|
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", |state: &App|{
|
||||||
|
let selected = state.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),
|
//(":view/options", view_options),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
fn view_nil (_: &App) -> Box<dyn Render<TuiOut>> {
|
|
||||||
"nil".boxed()
|
|
||||||
}
|
|
||||||
fn wrap_dialog (dialog: impl Content<TuiOut>) -> impl Content<TuiOut> {
|
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(
|
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))))
|
Repeat(" "), Outer(true, Style::default().fg(Tui::g(96))).enclose(dialog))))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue