restruct: 45e

This commit is contained in:
i do not exist 2026-06-24 06:39:43 +03:00
parent b3e9cde8a1
commit 027f69cd50
9 changed files with 264 additions and 270 deletions

View file

@ -3,6 +3,7 @@ pub mod bind; pub use self::bind::*;
pub mod cli; pub use self::cli::*; pub mod cli; pub use self::cli::*;
pub mod config; pub use self::config::*; pub mod config; pub use self::config::*;
pub mod view; pub use self::view::*; pub mod view; pub use self::view::*;
pub mod mode; pub use self::mode::*;
/// Total application state. /// Total application state.
/// ///

View file

@ -36,6 +36,13 @@ impl Config {
const CONFIG_SUB: &'static str = "v0"; const CONFIG_SUB: &'static str = "v0";
const CONFIG: &'static str = "tek.edn"; const CONFIG: &'static str = "tek.edn";
const DEFAULTS: &'static str = include_str!("../tek.edn"); const DEFAULTS: &'static str = include_str!("../tek.edn");
pub fn watch <T> (callback: impl Fn(Self)->T) -> Usually<T> {
let config = Self::init_new(None)?;
let result = callback(config);
Ok(result)
}
pub fn init_new (dirs: Option<BaseDirectories>) -> Usually<Self> { pub fn init_new (dirs: Option<BaseDirectories>) -> Usually<Self> {
let mut config = Self::new(None); let mut config = Self::new(None);
config.init()?; config.init()?;
@ -98,95 +105,3 @@ impl Config {
self.modes.clone().read().unwrap().get(mode.as_ref()).cloned() self.modes.clone().read().unwrap().get(mode.as_ref()).cloned()
} }
} }
pub(crate) fn load_mode (modes: &Modes, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
let mut mode = Mode::default();
body.each(|item|mode.add(item))?;
modes.write().unwrap().insert(name.as_ref().into(), Arc::new(mode));
Ok(())
}
/// Collection of interaction modes.
pub type Modes = Arc<RwLock<BTreeMap<Arc<str>, Arc<Mode<Arc<str>>>>>>;
impl Mode<Arc<str>> {
/// Add a definition to the mode.
///
/// Supported definitions:
///
/// - (name ...) -> name
/// - (info ...) -> description
/// - (keys ...) -> key bindings
/// - (mode ...) -> submode
/// - ... -> view
///
/// ```
/// let mut mode: tek::Mode<std::sync::Arc<str>> = Default::default();
/// mode.add("(name hello)").unwrap();
/// ```
pub fn add (&mut self, dsl: impl Language) -> Usually<()> {
Ok(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() {
//println!("Mode::add: {head} {:?}", expr.tail());
let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
match head {
"name" => self.add_name(tail)?,
"info" => self.add_info(tail)?,
"keys" => self.add_keys(tail)?,
"mode" => self.add_mode(tail)?,
_ => self.add_view(tail)?,
};
} else if let Ok(Some(word)) = dsl.word() {
self.add_view(word);
} else {
return Err(format!("Mode::add: unexpected: {dsl:?}").into());
})
//DslParse(dsl, ||Err(format!("Mode::add: unexpected: {dsl:?}").into()))
//.word(|word|self.add_view(word))
//.expr(|expr|expr.head(|head|{
////println!("Mode::add: {head} {:?}", expr.tail());
//let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
//match head {
//"name" => self.add_name(tail),
//"info" => self.add_info(tail),
//"keys" => self.add_keys(tail)?,
//"mode" => self.add_mode(tail)?,
//_ => self.add_view(tail),
//};
//}))
}
fn add_name (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(dsl.src()?.map(|src|self.name.push(src.into())))
}
fn add_info (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(dsl.src()?.map(|src|self.info.push(src.into())))
}
fn add_view (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(dsl.src()?.map(|src|self.view.push(src.into())))
}
fn add_keys (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(Some(dsl.each(|expr|{ self.keys.push(expr.trim().into()); Ok(()) })?))
}
fn add_mode (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(Some(if let Some(id) = dsl.head()? {
load_mode(&self.modes, &id, &dsl.tail())?;
} else {
return Err(format!("Mode::add: self: incomplete: {dsl:?}").into());
}))
}
}
/// Group of view and keys definitions.
///
/// ```
/// let mode = tek::Mode::<std::sync::Arc<str>>::default();
/// ```
#[derive(Default, Debug)] pub struct Mode<D: Language + Ord> {
pub path: PathBuf,
pub name: Vec<D>,
pub info: Vec<D>,
pub view: Vec<D>,
pub keys: Vec<D>,
pub modes: Modes,
}

93
src/app/mode.rs Normal file
View file

@ -0,0 +1,93 @@
use crate::*;
pub(crate) fn load_mode (modes: &Modes, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
let mut mode = Mode::default();
body.each(|item|mode.add(item))?;
modes.write().unwrap().insert(name.as_ref().into(), Arc::new(mode));
Ok(())
}
/// Collection of interaction modes.
pub type Modes = Arc<RwLock<BTreeMap<Arc<str>, Arc<Mode<Arc<str>>>>>>;
impl Mode<Arc<str>> {
/// Add a definition to the mode.
///
/// Supported definitions:
///
/// - (name ...) -> name
/// - (info ...) -> description
/// - (keys ...) -> key bindings
/// - (mode ...) -> submode
/// - ... -> view
///
/// ```
/// let mut mode: tek::Mode<std::sync::Arc<str>> = Default::default();
/// mode.add("(name hello)").unwrap();
/// ```
pub fn add (&mut self, dsl: impl Language) -> Usually<()> {
Ok(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() {
//println!("Mode::add: {head} {:?}", expr.tail());
let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
match head {
"name" => self.add_name(tail)?,
"info" => self.add_info(tail)?,
"keys" => self.add_keys(tail)?,
"mode" => self.add_mode(tail)?,
_ => self.add_view(tail)?,
};
} else if let Ok(Some(word)) = dsl.word() {
self.add_view(word);
} else {
return Err(format!("Mode::add: unexpected: {dsl:?}").into());
})
//DslParse(dsl, ||Err(format!("Mode::add: unexpected: {dsl:?}").into()))
//.word(|word|self.add_view(word))
//.expr(|expr|expr.head(|head|{
////println!("Mode::add: {head} {:?}", expr.tail());
//let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
//match head {
//"name" => self.add_name(tail),
//"info" => self.add_info(tail),
//"keys" => self.add_keys(tail)?,
//"mode" => self.add_mode(tail)?,
//_ => self.add_view(tail),
//};
//}))
}
fn add_name (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(dsl.src()?.map(|src|self.name.push(src.into())))
}
fn add_info (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(dsl.src()?.map(|src|self.info.push(src.into())))
}
fn add_view (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(dsl.src()?.map(|src|self.view.push(src.into())))
}
fn add_keys (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(Some(dsl.each(|expr|{ self.keys.push(expr.trim().into()); Ok(()) })?))
}
fn add_mode (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(Some(if let Some(id) = dsl.head()? {
load_mode(&self.modes, &id, &dsl.tail())?;
} else {
return Err(format!("Mode::add: self: incomplete: {dsl:?}").into());
}))
}
}
/// Group of view and keys definitions.
///
/// ```
/// let mode = tek::Mode::<std::sync::Arc<str>>::default();
/// ```
#[derive(Default, Debug)] pub struct Mode<D: Language + Ord> {
pub path: PathBuf,
pub name: Vec<D>,
pub info: Vec<D>,
pub view: Vec<D>,
pub keys: Vec<D>,
pub modes: Modes,
}

View file

@ -30,7 +30,7 @@ pub fn view_logo () -> impl Draw<Tui> {
pub fn view_transport (play: bool, bpm: &str, beat: &str, time: &str) -> impl Draw<Tui> { pub fn view_transport (play: bool, bpm: &str, beat: &str, time: &str) -> impl Draw<Tui> {
let theme = ItemTheme::G[96]; let theme = ItemTheme::G[96];
bg(Black, east!(above( bg(Black, east!(above(
wh_full(origin_w(button_play_pause(play))), wh_full(origin_w(button_play_pause(play, false))),
wh_full(origin_e(east!( wh_full(origin_e(east!(
field_h(theme, "BPM", bpm), field_h(theme, "BPM", bpm),
field_h(theme, "Beat", beat), field_h(theme, "Beat", beat),
@ -56,10 +56,12 @@ pub fn view_status (sel: Option<&str>, sr: &str, buf: &str, lat: &str) -> impl D
} }
/// ``` /// ```
/// let _ = tek::button_play_pause(true); /// let _ = tek::button_play_pause(true, true);
/// let _ = tek::button_play_pause(true, false);
/// let _ = tek::button_play_pause(false, true);
/// let _ = tek::button_play_pause(false, false);
/// ``` /// ```
pub fn button_play_pause (playing: bool) -> impl Draw<Tui> { pub fn button_play_pause (playing: bool, compact: bool) -> impl Draw<Tui> {
let compact = true;//self.is_editing();
bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) },
either(compact, either(compact,
thunk(move|to: &mut Tui|w_exact(9, either(playing, thunk(move|to: &mut Tui|w_exact(9, either(playing,
@ -202,87 +204,84 @@ pub fn view_io_ports <'a, T: PortsSizes<'a>> (
}) })
} }
pub fn view_scenes_clips <'a> ( pub fn view_scenes_clips <'a, S: ScenesSizes<'a>> (
scenes: impl ScenesSizes<'a>, scenes: impl Fn()->S,
tracks: impl TracksSizes<'a>, tracks: impl TracksSizes<'a>,
select: &Selection, select: &Selection,
editor: Option<&MidiEditor>, editor: Option<&MidiEditor>,
size: &Size, size: &Size,
is_editing: bool, editing: bool,
) -> impl Draw<Tui> { ) -> impl Draw<Tui> {
size.of(wh_full(above(wh_full(origin_se(fg(Green, format!("{}x{}", size.w(), size.h())))), let status = wh_full(origin_se(fg(Green, format!("{}x{}", size.w(), size.h()))));
thunk(move |to: &mut Tui|{ let tracks = iter_once(tracks, move|(track_index, track, _, _), _| {
for (index, track, _, _) in tracks { let scenes = iter_once(scenes(), move|(scene_index, scene, _, _), _| {
let clips = view_track_clips(&scenes, select, editor, index, track, is_editing); let (name, theme): (Arc<str>, ItemTheme) = scene_name_theme(scene, track_index);
w_exact(track.width as u16, h_full(clips)).draw(to); let f = theme.lightest.term;
} let (b, o) = scene_bg(theme, select, track_index, scene_index);
Ok(XYWH(0, 0, 0, 0)) let w = scene_w(track, select, track_index, editor);
})))) let y = scene_y(select, scene_index, editor);
} let is_selected = scene_sel(select, track_index, scene_index, editing);
pub fn view_track_clips <'a> (
scenes: &impl ScenesSizes<'a>,
select: &Selection,
editor: Option<&MidiEditor>,
index: usize,
track: &Track,
is_editing: bool,
) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|{
for (scene_index, scene, ..) in scenes {
let (
name, theme
): (Arc<str>, ItemTheme) = if let Some(Some(clip)) = &scene.clips.get(index) {
let clip = clip.read().unwrap();
(format!("{}", &clip.name).into(), clip.color)
} else {
(" ⏹ -- ".into(), ItemTheme::G[32])
};
let fg = theme.lightest.term;
let mut outline = theme.base.term;
let bg = if select.track() == Some(index) && select.scene() == Some(scene_index) {
outline = theme.lighter.term;
theme.light.term
} else if select.track() == Some(index) || select.scene() == Some(scene_index) {
outline = theme.darkest.term;
theme.base.term
} else {
theme.dark.term
};
let w = if select.track() == Some(index) && let Some(editor) = editor {
(editor.size.w() as usize).max(24).max(track.width)
} else {
track.width
} as u16;
let y = if select.scene() == Some(scene_index) && let Some(editor) = editor {
(editor.size.h() as usize).max(12)
} else {
Self::H_SCENE as usize
} as u16;
let is_selected = is_editing && select.track() == Some(index) && select.scene() == Some(scene_index);
wh_exact(Some(w), Some(y), below( wh_exact(Some(w), Some(y), below(
wh_full(Outer(true, Style::default().fg(outline))), wh_full(Outer(true, Style::default().fg(o))),
wh_full(below( wh_full(below(
below( below(
fg_bg(outline, bg, wh_full("")), fg_bg(o, b, wh_full("")),
wh_full(origin_nw(fg_bg(fg, bg, bold(true, &name)))), wh_full(origin_nw(fg_bg(f, b, bold(true, name)))),
), ),
wh_full(when(is_selected, editor.map(|e|e.view())))))) wh_full(when(is_selected, editor.map(|e|e.view())))))))
).draw(to); });
w_exact(track.width as u16, h_full(scenes))
});
return size.of(wh_full(above(status, tracks)));
fn scene_name_theme (scene: &Scene, track_index: usize) -> (Arc<str>, ItemTheme) {
if let Some(Some(clip)) = &scene.clips.get(track_index) {
let clip = clip.read().unwrap();
(format!("{}", &clip.name).into(), clip.color)
} else {
(" ⏹ -- ".into(), ItemTheme::G[32])
} }
}
Ok(XYWH(0, 0, 0, 0)) fn scene_bg (
}) theme: ItemTheme, select: &Selection, track_index: usize, scene_index: usize
) -> (Color, Color) {
let mut outline = theme.base.term;
(if select.track() == Some(track_index) && select.scene() == Some(scene_index) {
outline = theme.lighter.term;
theme.light.term
} else if select.track() == Some(track_index) || select.scene() == Some(scene_index) {
outline = theme.darkest.term;
theme.base.term
} else {
theme.dark.term
}, outline)
}
fn scene_w (
track: &Track, select: &Selection, track_index: usize, editor: Option<&MidiEditor>
) -> u16 {
if select.track() == Some(track_index) && let Some(editor) = editor {
(editor.size.w() as usize).max(24).max(track.width) as u16
} else {
track.width as u16
}
}
fn scene_y (
select: &Selection, scene_index: usize, editor: Option<&MidiEditor>
) -> u16 {
if select.scene() == Some(scene_index) && let Some(editor) = editor {
editor.size.h().max(12)
} else {
H_SCENE as u16
}
}
fn scene_sel (select: &Selection, track_index: usize, scene_index: usize, editing: bool) -> bool {
editing && select.track() == Some(track_index) && select.scene() == Some(scene_index)
}
} }
pub fn view_track_names ( pub fn view_track_names (
@ -318,9 +317,7 @@ pub fn view_track_names (
} }
pub fn view_track_outputs ( pub fn view_track_outputs (
theme: ItemTheme, theme: ItemTheme, tracks: impl TracksSizes<'_>, midi_outs: impl Iterator<Item = &MidiOutput>,
tracks: impl TracksSizes<'_>,
midi_outs: impl Iterator<Item = &MidiOutput>,
) -> impl Draw<Tui> { ) -> impl Draw<Tui> {
view_track_row_section(theme, view_track_row_section(theme,
south(w_full(origin_w(button_2("o", "utput", false))), south(w_full(origin_w(button_2("o", "utput", false))),
@ -345,9 +342,7 @@ pub fn view_track_outputs (
} }
pub fn view_track_inputs ( pub fn view_track_inputs (
theme: ItemTheme, theme: ItemTheme, tracks: impl TracksSizes<'_>, height: u16,
tracks: impl TracksSizes<'_>,
height: u16,
) -> impl Draw<Tui> { ) -> impl Draw<Tui> {
view_track_row_section(theme, button_2("i", "nput", false), button_2("I", "+", false), view_track_row_section(theme, button_2("i", "nput", false), button_2("I", "+", false),
bg(theme.darker.term, origin_w(thunk(move|to: &mut Tui|{ bg(theme.darker.term, origin_w(thunk(move|to: &mut Tui|{
@ -393,7 +388,7 @@ pub fn view_scene_name (
let h = if select.scene() == Some(index) && let Some(_editor) = editor { let h = if select.scene() == Some(index) && let Some(_editor) = editor {
7 7
} else { } else {
Self::H_SCENE as u16 H_SCENE as u16
}; };
let a = w_full(origin_w(east(format!("·s{index:02} "), let a = w_full(origin_w(east(format!("·s{index:02} "),
fg(g(255), bold(true, &scene.name))))); fg(g(255), bold(true, &scene.name)))));
@ -442,43 +437,34 @@ pub fn view_per_track () -> impl Draw<Tui> {}
pub fn view_per_track_top () -> impl Draw<Tui> {} pub fn view_per_track_top () -> impl Draw<Tui> {}
pub fn view_inputs (tracks: impl TracksSizes<'_>, midi_ins: &[MidiInput]) -> impl Draw<Tui> { pub fn view_inputs (tracks: impl TracksSizes<'_>, midi_ins: &[MidiInput]) -> impl Draw<Tui> {
let header = h_exact(1, view_inputs_header(tracks, midi_ins)); let title_1 = wh_exact(Some(20), Some(1), origin_w(button_3("i", "nput ", format!("{}", midi_ins.len()), false)));
south(header, thunk(move |to: &mut Tui|{ let title_2 = wh_exact(Some(4), Some(1), w_exact(4, button_2("I", "+", false)));
for (index, port) in midi_ins.iter().enumerate() { east(title_1, west(title_2, thunk(move|to: &mut Tui|{
x_push(index as u16 * 10, h_exact(1, view_inputs_row(tracks, port))).draw(to); for (_index, track, x1, _x2) in tracks {
} south(
todo!()
}))
}
pub fn view_inputs_header (tracks: impl TracksSizes<'_>, midi_ins: &[MidiInput]) -> impl Draw<Tui> {
east(w_exact(20, origin_w(button_3("i", "nput ", format!("{}", midi_ins.len()), false))),
west(w_exact(4, button_2("I", "+", false)), thunk(move|to: &mut Tui|{
for (_index, track, x1, _x2) in tracks {
#[cfg(feature = "track")]
x_push(x1 as u16, bg(track.color.dark.term, origin_w(w_exact(track.width as u16, east!( x_push(x1 as u16, bg(track.color.dark.term, origin_w(w_exact(track.width as u16, east!(
either(track.sequencer.monitoring, fg(Green, "mon "), "mon "), either(track.sequencer.monitoring, fg(Green, "mon "), "mon "),
either(track.sequencer.recording, fg(Red, "rec "), "rec "), either(track.sequencer.recording, fg(Red, "rec "), "rec "),
either(track.sequencer.overdub, fg(Yellow, "dub "), "dub "), either(track.sequencer.overdub, fg(Yellow, "dub "), "dub "),
))))).draw(to); ))))),
} thunk(move |to: &mut Tui|{
todo!() for (index, port) in midi_ins.iter().enumerate() {
}))) x_push(index as u16 * 10, h_exact(1, east(w_exact(20, origin_w(east("", bold(true, fg(Rgb(255,255,255), port.port_name()))))),
} west(w_exact(4, ()), thunk(move|to: &mut Tui|{
bg(track.color.darker.term, origin_w(w_exact(track.width as u16, east!(
pub fn view_inputs_row (tracks: impl TracksSizes<'_>, port: &MidiInput) -> impl Draw<Tui> { either(track.sequencer.monitoring, fg(Green, ""), " · "),
east(w_exact(20, origin_w(east("", bold(true, fg(Rgb(255,255,255), port.port_name()))))), either(track.sequencer.recording, fg(Red, ""), " · "),
west(w_exact(4, ()), thunk(move|to: &mut Tui|{ either(track.sequencer.overdub, fg(Yellow, ""), " · "),
for (_index, track, _x1, _x2) in tracks { )))).draw(to);
#[cfg(feature = "track")] todo!()
bg(track.color.darker.term, origin_w(w_exact(track.width as u16, east!( }))))).draw(to);
either(track.sequencer.monitoring, fg(Green, ""), " · "), }
either(track.sequencer.recording, fg(Red, ""), " · "), todo!()
either(track.sequencer.overdub, fg(Yellow, ""), " · "), })
)))).draw(to); ).draw(to);
} }
todo!() todo!()
}))) })))
} }
pub fn view_outputs ( pub fn view_outputs (
@ -539,14 +525,14 @@ pub fn view_outputs (
pub fn view_track_devices ( pub fn view_track_devices (
theme: ItemTheme, theme: ItemTheme,
tracks: &impl TracksSizes<'_>, tracks: impl TracksSizes<'_>,
track: Option<&Track>, track: Option<&Track>,
h: u16, h: u16,
) -> impl Draw<Tui> { ) -> impl Draw<Tui> {
view_track_row_section(theme, view_track_row_section(theme,
button_3("d", "evice", format!("{}", track.map(|t|t.devices.len()).unwrap_or(0)), false), button_3("d", "evice", format!("{}", track.map(|t|t.devices.len()).unwrap_or(0)), false),
button_2("D", "+", false), button_2("D", "+", false),
iter(tracks, move|(_, track, _x1, _x2), index| wh_exact( iter_once(tracks, move|(_, track, _x1, _x2), index| wh_exact(
Some(track_width(index, track)), Some(track_width(index, track)),
Some(h + 1), Some(h + 1),
bg(track.color.dark.term, origin_nw(iter_south(move||0..h, bg(track.color.dark.term, origin_nw(iter_south(move||0..h,

View file

@ -27,7 +27,7 @@ pub trait ClipsView: TracksView + ScenesView {
/// Draw clips per scene /// Draw clips per scene
fn view_scenes_clips <'a> (&'a self) -> impl Draw<Tui> + 'a { fn view_scenes_clips <'a> (&'a self) -> impl Draw<Tui> + 'a {
crate::view::view_scenes_clips( crate::view::view_scenes_clips(
self.scenes_with_sizes(), ||self.scenes_with_sizes(),
self.tracks_with_sizes(), self.tracks_with_sizes(),
self.selection(), self.selection(),
self.editor(), self.editor(),

View file

@ -1,5 +1,11 @@
use crate::*; use crate::*;
/// Default scene height.
pub const H_SCENE: usize = 2;
/// Default editor height.
pub const H_EDITOR: usize = 15;
/// A scene consists of a set of clips to play together. /// A scene consists of a set of clips to play together.
/// ///
/// ``` /// ```
@ -51,10 +57,6 @@ impl Scene {
} }
pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize + Send + Sync { pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize + Send + Sync {
/// Default scene height.
const H_SCENE: usize = 2;
/// Default editor height.
const H_EDITOR: usize = 15;
fn h_scenes (&self) -> u16; fn h_scenes (&self) -> u16;
fn w_side (&self) -> u16; fn w_side (&self) -> u16;
fn w_mid (&self) -> u16; fn w_mid (&self) -> u16;
@ -70,7 +72,7 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize +
let height = if self.selection().scene() == Some(s) && self.editor().is_some() { let height = if self.selection().scene() == Some(s) && self.editor().is_some() {
8 8
} else { } else {
Self::H_SCENE H_SCENE
}; };
if y + height <= self.clips_size().h() as usize { if y + height <= self.clips_size().h() as usize {
let data = (s, scene, y, y + height); let data = (s, scene, y, y + height);

View file

@ -223,71 +223,66 @@ impl HasTrackScroll for Arrangement {
def_command!(TrackCommand: |track: Track| { def_command!(TrackCommand: |track: Track| {
Stop => { track.sequencer.enqueue_next(None); Ok(None) }, Stop => { track.sequencer.enqueue_next(None); Ok(None) },
SetMute { mute: Option<bool> } => todo!(), SetRec { rec: Option<bool> } => toggle_bool(&mut track.sequencer.recording, rec, |rec|Self::SetRec { rec }),
SetSolo { solo: Option<bool> } => todo!(), SetMon { mon: Option<bool> } => toggle_bool(&mut track.sequencer.monitoring, mon, |mon|Self::SetMon { mon }),
SetSize { size: usize } => todo!(), SetMute { mute: Option<bool> } => todo!(),
SetZoom { zoom: usize } => todo!(), SetSolo { solo: Option<bool> } => todo!(),
SetName { name: Arc<str> } => SetSize { size: usize } => todo!(),
swap_value(&mut track.name, name, |name|Self::SetName { name }), SetZoom { zoom: usize } => todo!(),
SetColor { color: ItemTheme } => SetName { name: Arc<str> } => swap_value(&mut track.name, name, |name|Self::SetName { name }),
swap_value(&mut track.color, color, |color|Self::SetColor { color }), SetColor { color: ItemTheme } => swap_value(&mut track.color, color, |color|Self::SetColor { color }),
SetRec { rec: Option<bool> } =>
toggle_bool(&mut track.sequencer.recording, rec, |rec|Self::SetRec { rec }),
SetMon { mon: Option<bool> } =>
toggle_bool(&mut track.sequencer.monitoring, mon, |mon|Self::SetMon { mon }),
}); });
impl<T: AsRef<Vec<Track>>+AsMut<Vec<Track>>> HasTracks for T {} impl<T: AsRef<Vec<Track>>+AsMut<Vec<Track>>> HasTracks for T {}
impl<T: AsRefOpt<Track>+AsMutOpt<Track>+Send+Sync> HasTrack for T {} impl<T: AsRefOpt<Track>+AsMutOpt<Track>+Send+Sync> HasTrack for T {}
impl<T: ScenesView+HasMidiIns+HasMidiOuts+HasTrackScroll> TracksView for T {} impl<T: ScenesView+HasMidiIns+HasMidiOuts+HasTrackScroll> TracksView for T {}
impl_has!(Vec<Track>: |self: Arrangement| self.tracks);
impl_as_ref_opt!(Track: |self: Arrangement| self.selected_track());
impl_as_mut_opt!(Track: |self: Arrangement| self.selected_track_mut());
impl_as_ref!(Vec<Track>: |self: App| self.project.as_ref()); impl_as_ref!(Vec<Track>: |self: App| self.project.as_ref());
impl_as_mut!(Vec<Track>: |self: App| self.project.as_mut()); impl_as_mut!(Vec<Track>: |self: App| self.project.as_mut());
#[cfg(feature = "select")] impl_as_ref_opt!(Track: |self: App| self.project.as_ref_opt()); #[cfg(feature = "select")] impl_as_ref_opt!(Track: |self: App| self.project.as_ref_opt());
#[cfg(feature = "select")] impl_as_mut_opt!(Track: |self: App| self.project.as_mut_opt()); #[cfg(feature = "select")] impl_as_mut_opt!(Track: |self: App| self.project.as_mut_opt());
impl_has!(Vec<Track>: |self: Arrangement| self.tracks);
impl_as_ref_opt!(Track: |self: Arrangement| self.selected_track());
impl_as_mut_opt!(Track: |self: Arrangement| self.selected_track_mut());
impl Arrangement { impl Arrangement {
#[cfg(feature = "track")]
pub fn view_inputs (&self, _theme: ItemTheme) -> impl Draw<Tui> + '_ { pub fn view_inputs (&self, _theme: ItemTheme) -> impl Draw<Tui> + '_ {
crate::view::view_inputs(self.tracks_with_sizes(), self.midi_ins().as_slice()) crate::view::view_inputs(
self.tracks_with_sizes(), self.midi_ins().as_slice()
)
} }
#[cfg(feature = "track")]
pub fn view_inputs_header (&self) -> impl Draw<Tui> + '_ {
crate::view::view_inputs_header(self.tracks_with_sizes(), self.midi_ins().as_slice())
}
#[cfg(feature = "track")]
pub fn view_inputs_row (&self, port: &MidiInput) -> impl Draw<Tui> {
crate::view::view_inputs_row(port)
}
#[cfg(feature = "track")]
pub fn view_outputs (&self, theme: ItemTheme) -> impl Draw<Tui> { pub fn view_outputs (&self, theme: ItemTheme) -> impl Draw<Tui> {
crate::view::view_outputs(
theme, self.tracks_with_sizes(), self.midi_outs(), self.outputs_height()
)
}
pub fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<Tui> {
crate::view::view_track_devices(
theme, self.tracks_with_sizes(), self.track(), self.devices_height()
)
}
fn devices_height (&self) -> u16 {
let mut h = 2;
for track in self.tracks().iter() {
h = h.max(track.devices.len() * 2);
}
h as u16
}
fn outputs_height (&self) -> u16 {
let mut h = 1; let mut h = 1;
for output in self.midi_outs().iter() { for output in self.midi_outs().iter() {
h += 1 + output.connections.len(); h += 1 + output.connections.len();
} }
let h = h as u16; h as u16
crate::view::view_outputs(self.tracks_with_sizes())
}
#[cfg(feature = "track")]
pub fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<Tui> {
let mut h = 2u16;
for track in self.tracks().iter() {
h = h.max(track.devices.len() as u16 * 2);
}
crate::view::view_track_devices(||self.tracks_with_sizes(), self.track(), h)
} }
/// Add multiple tracks /// Add multiple tracks
#[cfg(feature = "track")] pub fn tracks_add ( pub fn tracks_add (
&mut self, &mut self, count: usize, width: Option<usize>, mins: &[Connect], mouts: &[Connect],
count: usize, width: Option<usize>,
mins: &[Connect], mouts: &[Connect],
) -> Usually<()> { ) -> Usually<()> {
let track_color_1 = ItemColor::random(); let track_color_1 = ItemColor::random();
let track_color_2 = ItemColor::random(); let track_color_2 = ItemColor::random();
@ -302,10 +297,12 @@ impl Arrangement {
} }
/// Add a track /// Add a track
#[cfg(feature = "track")] pub fn track_add ( pub fn track_add (
&mut self, &mut self,
name: Option<&str>, color: Option<ItemTheme>, name: Option<&str>,
mins: &[Connect], mouts: &[Connect], color: Option<ItemTheme>,
mins: &[Connect],
mouts: &[Connect],
) -> Usually<(usize, &mut Track)> { ) -> Usually<(usize, &mut Track)> {
let name: Arc<str> = name.map_or_else( let name: Arc<str> = name.map_or_else(
||format!("trk{:02}", self.track_last).into(), ||format!("trk{:02}", self.track_last).into(),

View file

@ -89,9 +89,9 @@ pub(crate) use tengri::{
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
pub fn main () -> Usually<()> { pub fn main () -> Usually<()> {
Config::watch(|config|{ Config::watch(|config|{
Exit::enter(|exit|{ Exit::run(|exit|{
Jack::connect("tek", |jack|{ Jack::new_run("tek", |jack|{
let project = Arrangement::new(&jack, &Clock::new(&jack, 51)); let project = Arrangement::new(&jack, &Clock::new(&jack, Some(51.)));
let state = App::new_shared(jack, config, project, ":menu"); let state = App::new_shared(jack, config, project, ":menu");
let keyboard = tui_input(&exit, &state, Duration::from_millis(100))?; let keyboard = tui_input(&exit, &state, Duration::from_millis(100))?;
let terminal = tui_output(&exit, &state, Duration::from_millis(10))?; let terminal = tui_output(&exit, &state, Duration::from_millis(10))?;

2
tengri

@ -1 +1 @@
Subproject commit e0781571c42cb72e7445a2adbdb7a99e37600562 Subproject commit 0273d2ac75b8a144ea03b4bdd94d08003f3589df