diff --git a/src/app.rs b/src/app.rs index fc86e571..1c3738d3 100644 --- a/src/app.rs +++ b/src/app.rs @@ -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. /// diff --git a/src/app/config.rs b/src/app/config.rs index cceefff8..172a2b55 100644 --- a/src/app/config.rs +++ b/src/app/config.rs @@ -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 (callback: impl Fn(Self)->T) -> Usually { + let config = Self::init_new(None)?; + let result = callback(config); + Ok(result) + } + pub fn init_new (dirs: Option) -> Usually { 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, 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, Arc>>>>>; - -impl Mode> { - /// Add a definition to the mode. - /// - /// Supported definitions: - /// - /// - (name ...) -> name - /// - (info ...) -> description - /// - (keys ...) -> key bindings - /// - (mode ...) -> submode - /// - ... -> view - /// - /// ``` - /// let mut mode: tek::Mode> = 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::>::default(); -/// ``` -#[derive(Default, Debug)] pub struct Mode { - pub path: PathBuf, - pub name: Vec, - pub info: Vec, - pub view: Vec, - pub keys: Vec, - pub modes: Modes, -} diff --git a/src/app/mode.rs b/src/app/mode.rs new file mode 100644 index 00000000..e5c5f8ea --- /dev/null +++ b/src/app/mode.rs @@ -0,0 +1,93 @@ +use crate::*; + +pub(crate) fn load_mode (modes: &Modes, name: &impl AsRef, 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, Arc>>>>>; + +impl Mode> { + /// Add a definition to the mode. + /// + /// Supported definitions: + /// + /// - (name ...) -> name + /// - (info ...) -> description + /// - (keys ...) -> key bindings + /// - (mode ...) -> submode + /// - ... -> view + /// + /// ``` + /// let mut mode: tek::Mode> = 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::>::default(); +/// ``` +#[derive(Default, Debug)] pub struct Mode { + pub path: PathBuf, + pub name: Vec, + pub info: Vec, + pub view: Vec, + pub keys: Vec, + pub modes: Modes, +} diff --git a/src/app/view.rs b/src/app/view.rs index 21699419..b11832a8 100644 --- a/src/app/view.rs +++ b/src/app/view.rs @@ -30,7 +30,7 @@ pub fn view_logo () -> impl Draw { pub fn view_transport (play: bool, bpm: &str, beat: &str, time: &str) -> impl Draw { 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 { - let compact = true;//self.is_editing(); +pub fn button_play_pause (playing: bool, compact: bool) -> impl Draw { 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 { - 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 { - thunk(move|to: &mut Tui|{ - - for (scene_index, scene, ..) in scenes { - - let ( - name, theme - ): (Arc, 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, 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, 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, + theme: ItemTheme, tracks: impl TracksSizes<'_>, midi_outs: impl Iterator, ) -> impl Draw { 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 { 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 {} pub fn view_per_track_top () -> impl Draw {} pub fn view_inputs (tracks: impl TracksSizes<'_>, midi_ins: &[MidiInput]) -> impl Draw { - 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 { - 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 { - 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 { 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, diff --git a/src/device/arrange/clip.rs b/src/device/arrange/clip.rs index 4dc537c1..05c7b9c5 100644 --- a/src/device/arrange/clip.rs +++ b/src/device/arrange/clip.rs @@ -27,7 +27,7 @@ pub trait ClipsView: TracksView + ScenesView { /// Draw clips per scene fn view_scenes_clips <'a> (&'a self) -> impl Draw + 'a { crate::view::view_scenes_clips( - self.scenes_with_sizes(), + ||self.scenes_with_sizes(), self.tracks_with_sizes(), self.selection(), self.editor(), diff --git a/src/device/arrange/scene.rs b/src/device/arrange/scene.rs index f2b15aa2..2d1c284e 100644 --- a/src/device/arrange/scene.rs +++ b/src/device/arrange/scene.rs @@ -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); diff --git a/src/device/arrange/track.rs b/src/device/arrange/track.rs index e35a4dd7..5ea42948 100644 --- a/src/device/arrange/track.rs +++ b/src/device/arrange/track.rs @@ -223,71 +223,66 @@ impl HasTrackScroll for Arrangement { def_command!(TrackCommand: |track: Track| { Stop => { track.sequencer.enqueue_next(None); Ok(None) }, - SetMute { mute: Option } => todo!(), - SetSolo { solo: Option } => todo!(), - SetSize { size: usize } => todo!(), - SetZoom { zoom: usize } => todo!(), - SetName { name: Arc } => - 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 } => - toggle_bool(&mut track.sequencer.recording, rec, |rec|Self::SetRec { rec }), - SetMon { mon: Option } => - toggle_bool(&mut track.sequencer.monitoring, mon, |mon|Self::SetMon { mon }), + SetRec { rec: Option } => toggle_bool(&mut track.sequencer.recording, rec, |rec|Self::SetRec { rec }), + SetMon { mon: Option } => toggle_bool(&mut track.sequencer.monitoring, mon, |mon|Self::SetMon { mon }), + SetMute { mute: Option } => todo!(), + SetSolo { solo: Option } => todo!(), + SetSize { size: usize } => todo!(), + SetZoom { zoom: usize } => todo!(), + SetName { name: Arc } => swap_value(&mut track.name, name, |name|Self::SetName { name }), + SetColor { color: ItemTheme } => swap_value(&mut track.color, color, |color|Self::SetColor { color }), }); -impl>+AsMut>> HasTracks for T {} -impl+AsMutOpt+Send+Sync> HasTrack for T {} -impl TracksView for T {} +impl>+AsMut>> HasTracks for T {} +impl+AsMutOpt+Send+Sync> HasTrack for T {} +impl TracksView for T {} +impl_has!(Vec: |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: |self: App| self.project.as_ref()); impl_as_mut!(Vec: |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: |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 + '_ { - 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 + '_ { - 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 { - crate::view::view_inputs_row(port) - } - - #[cfg(feature = "track")] pub fn view_outputs (&self, theme: ItemTheme) -> impl Draw { + 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 { + 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 { - 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, - mins: &[Connect], mouts: &[Connect], + pub fn tracks_add ( + &mut self, count: usize, width: Option, 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, - mins: &[Connect], mouts: &[Connect], + name: Option<&str>, + color: Option, + mins: &[Connect], + mouts: &[Connect], ) -> Usually<(usize, &mut Track)> { let name: Arc = name.map_or_else( ||format!("trk{:02}", self.track_last).into(), diff --git a/src/tek.rs b/src/tek.rs index d5060d73..115d37c4 100644 --- a/src/tek.rs +++ b/src/tek.rs @@ -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))?; diff --git a/tengri b/tengri index e0781571..0273d2ac 160000 --- a/tengri +++ b/tengri @@ -1 +1 @@ -Subproject commit e0781571c42cb72e7445a2adbdb7a99e37600562 +Subproject commit 0273d2ac75b8a144ea03b4bdd94d08003f3589df