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 config; pub use self::config::*;
pub mod view; pub use self::view::*;
pub mod mode; pub use self::mode::*;
/// Total application state.
///

View file

@ -36,6 +36,13 @@ impl Config {
const CONFIG_SUB: &'static str = "v0";
const CONFIG: &'static 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> {
let mut config = Self::new(None);
config.init()?;
@ -98,95 +105,3 @@ impl Config {
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> {
let theme = ItemTheme::G[96];
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!(
field_h(theme, "BPM", bpm),
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> {
let compact = true;//self.is_editing();
pub fn button_play_pause (playing: bool, compact: bool) -> impl Draw<Tui> {
bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) },
either(compact,
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> (
scenes: impl ScenesSizes<'a>,
tracks: impl TracksSizes<'a>,
select: &Selection,
editor: Option<&MidiEditor>,
size: &Size,
is_editing: bool,
pub fn view_scenes_clips <'a, S: ScenesSizes<'a>> (
scenes: impl Fn()->S,
tracks: impl TracksSizes<'a>,
select: &Selection,
editor: Option<&MidiEditor>,
size: &Size,
editing: bool,
) -> impl Draw<Tui> {
size.of(wh_full(above(wh_full(origin_se(fg(Green, format!("{}x{}", size.w(), size.h())))),
thunk(move |to: &mut Tui|{
for (index, track, _, _) in tracks {
let clips = view_track_clips(&scenes, select, editor, index, track, is_editing);
w_exact(track.width as u16, h_full(clips)).draw(to);
}
Ok(XYWH(0, 0, 0, 0))
}))))
}
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);
let status = wh_full(origin_se(fg(Green, format!("{}x{}", size.w(), size.h()))));
let tracks = iter_once(tracks, move|(track_index, track, _, _), _| {
let scenes = iter_once(scenes(), move|(scene_index, scene, _, _), _| {
let (name, theme): (Arc<str>, ItemTheme) = scene_name_theme(scene, track_index);
let f = theme.lightest.term;
let (b, o) = scene_bg(theme, select, track_index, scene_index);
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);
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(
below(
fg_bg(outline, bg, wh_full("")),
wh_full(origin_nw(fg_bg(fg, bg, bold(true, &name)))),
fg_bg(o, b, wh_full("")),
wh_full(origin_nw(fg_bg(f, b, bold(true, name)))),
),
wh_full(when(is_selected, editor.map(|e|e.view()))))))
).draw(to);
wh_full(when(is_selected, editor.map(|e|e.view())))))))
});
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 (
@ -318,9 +317,7 @@ pub fn view_track_names (
}
pub fn view_track_outputs (
theme: ItemTheme,
tracks: impl TracksSizes<'_>,
midi_outs: impl Iterator<Item = &MidiOutput>,
theme: ItemTheme, tracks: impl TracksSizes<'_>, midi_outs: impl Iterator<Item = &MidiOutput>,
) -> impl Draw<Tui> {
view_track_row_section(theme,
south(w_full(origin_w(button_2("o", "utput", false))),
@ -345,9 +342,7 @@ pub fn view_track_outputs (
}
pub fn view_track_inputs (
theme: ItemTheme,
tracks: impl TracksSizes<'_>,
height: u16,
theme: ItemTheme, tracks: impl TracksSizes<'_>, height: u16,
) -> impl Draw<Tui> {
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|{
@ -393,7 +388,7 @@ pub fn view_scene_name (
let h = if select.scene() == Some(index) && let Some(_editor) = editor {
7
} else {
Self::H_SCENE as u16
H_SCENE as u16
};
let a = w_full(origin_w(east(format!("·s{index:02} "),
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_inputs (tracks: impl TracksSizes<'_>, midi_ins: &[MidiInput]) -> impl Draw<Tui> {
let header = h_exact(1, view_inputs_header(tracks, midi_ins));
south(header, thunk(move |to: &mut Tui|{
for (index, port) in midi_ins.iter().enumerate() {
x_push(index as u16 * 10, h_exact(1, view_inputs_row(tracks, port))).draw(to);
}
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")]
let title_1 = wh_exact(Some(20), Some(1), origin_w(button_3("i", "nput ", format!("{}", midi_ins.len()), false)));
let title_2 = wh_exact(Some(4), Some(1), w_exact(4, button_2("I", "+", false)));
east(title_1, west(title_2, thunk(move|to: &mut Tui|{
for (_index, track, x1, _x2) in tracks {
south(
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.recording, fg(Red, "rec "), "rec "),
either(track.sequencer.overdub, fg(Yellow, "dub "), "dub "),
))))).draw(to);
}
todo!()
})))
}
pub fn view_inputs_row (tracks: impl TracksSizes<'_>, port: &MidiInput) -> impl Draw<Tui> {
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|{
for (_index, track, _x1, _x2) in tracks {
#[cfg(feature = "track")]
bg(track.color.darker.term, origin_w(w_exact(track.width as u16, east!(
either(track.sequencer.monitoring, fg(Green, ""), " · "),
either(track.sequencer.recording, fg(Red, ""), " · "),
either(track.sequencer.overdub, fg(Yellow, ""), " · "),
)))).draw(to);
}
todo!()
})))
))))),
thunk(move |to: &mut Tui|{
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!(
either(track.sequencer.monitoring, fg(Green, ""), " · "),
either(track.sequencer.recording, fg(Red, ""), " · "),
either(track.sequencer.overdub, fg(Yellow, ""), " · "),
)))).draw(to);
todo!()
}))))).draw(to);
}
todo!()
})
).draw(to);
}
todo!()
})))
}
pub fn view_outputs (
@ -539,14 +525,14 @@ pub fn view_outputs (
pub fn view_track_devices (
theme: ItemTheme,
tracks: &impl TracksSizes<'_>,
tracks: impl TracksSizes<'_>,
track: Option<&Track>,
h: u16,
) -> impl Draw<Tui> {
view_track_row_section(theme,
button_3("d", "evice", format!("{}", track.map(|t|t.devices.len()).unwrap_or(0)), 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(h + 1),
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
fn view_scenes_clips <'a> (&'a self) -> impl Draw<Tui> + 'a {
crate::view::view_scenes_clips(
self.scenes_with_sizes(),
||self.scenes_with_sizes(),
self.tracks_with_sizes(),
self.selection(),
self.editor(),

View file

@ -1,5 +1,11 @@
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.
///
/// ```
@ -51,10 +57,6 @@ impl Scene {
}
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 w_side (&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() {
8
} else {
Self::H_SCENE
H_SCENE
};
if y + height <= self.clips_size().h() as usize {
let data = (s, scene, y, y + height);

View file

@ -223,71 +223,66 @@ impl HasTrackScroll for Arrangement {
def_command!(TrackCommand: |track: Track| {
Stop => { track.sequencer.enqueue_next(None); Ok(None) },
SetMute { mute: Option<bool> } => todo!(),
SetSolo { solo: Option<bool> } => todo!(),
SetSize { size: usize } => todo!(),
SetZoom { zoom: usize } => todo!(),
SetName { name: Arc<str> } =>
swap_value(&mut track.name, name, |name|Self::SetName { name }),
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 }),
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 }),
SetMute { mute: Option<bool> } => todo!(),
SetSolo { solo: Option<bool> } => todo!(),
SetSize { size: usize } => todo!(),
SetZoom { zoom: usize } => todo!(),
SetName { name: Arc<str> } => swap_value(&mut track.name, name, |name|Self::SetName { name }),
SetColor { color: ItemTheme } => swap_value(&mut track.color, color, |color|Self::SetColor { color }),
});
impl<T: AsRef<Vec<Track>>+AsMut<Vec<Track>>> HasTracks for T {}
impl<T: AsRefOpt<Track>+AsMutOpt<Track>+Send+Sync> HasTrack for T {}
impl<T: ScenesView+HasMidiIns+HasMidiOuts+HasTrackScroll> TracksView 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: 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_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_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 {
#[cfg(feature = "track")]
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> {
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;
for output in self.midi_outs().iter() {
h += 1 + output.connections.len();
}
let h = 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)
h as u16
}
/// Add multiple tracks
#[cfg(feature = "track")] pub fn tracks_add (
&mut self,
count: usize, width: Option<usize>,
mins: &[Connect], mouts: &[Connect],
pub fn tracks_add (
&mut self, count: usize, width: Option<usize>, mins: &[Connect], mouts: &[Connect],
) -> Usually<()> {
let track_color_1 = ItemColor::random();
let track_color_2 = ItemColor::random();
@ -302,10 +297,12 @@ impl Arrangement {
}
/// Add a track
#[cfg(feature = "track")] pub fn track_add (
pub fn track_add (
&mut self,
name: Option<&str>, color: Option<ItemTheme>,
mins: &[Connect], mouts: &[Connect],
name: Option<&str>,
color: Option<ItemTheme>,
mins: &[Connect],
mouts: &[Connect],
) -> Usually<(usize, &mut Track)> {
let name: Arc<str> = name.map_or_else(
||format!("trk{:02}", self.track_last).into(),

View file

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

2
tengri

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