This commit is contained in:
i do not exist 2026-09-23 12:06:38 +03:00
parent 0a89fab28a
commit 9e3cc1139d
18 changed files with 450 additions and 414 deletions

View file

@ -1,13 +1,9 @@
#![allow(unused)] #![allow(unused)]
use crate::*; use crate::*;
pub trait HasEditor: AsRef<Option<MidiEditor>> + AsMut<Option<MidiEditor>> { impl<T: HasEditor> EditorModel for T {}
fn editor (&self) -> &Option<MidiEditor> {
self.as_ref() pub trait EditorModel: HasEditor {
}
fn editor_mut (&mut self) -> &mut Option<MidiEditor> {
self.as_mut()
}
fn is_editing (&self) -> bool { fn is_editing (&self) -> bool {
self.editor().is_some() self.editor().is_some()
} }
@ -19,8 +15,6 @@ pub trait HasEditor: AsRef<Option<MidiEditor>> + AsMut<Option<MidiEditor>> {
} }
} }
impl<T> HasEditor for T where T: AsRef<Option<MidiEditor>> + AsMut<Option<MidiEditor>> {}
/// Contains state for viewing and editing a clip. /// Contains state for viewing and editing a clip.
/// ///
/// ``` /// ```

View file

@ -1,10 +1,5 @@
use crate::*; use crate::*;
pub trait HasMidiBuffers {
fn note_buf_mut (&mut self) -> &mut Vec<u8>;
fn midi_buf_mut (&mut self) -> &mut Vec<Vec<Vec<u8>>>;
}
pub trait MidiMonitor: HasMidiIns + HasMidiBuffers { pub trait MidiMonitor: HasMidiIns + HasMidiBuffers {
/// Input note flags. /// Input note flags.
fn notes_in (&self) -> &Arc<RwLock<[bool;128]>>; fn notes_in (&self) -> &Arc<RwLock<[bool;128]>>;

View file

@ -1,12 +1,25 @@
use crate::*; use crate::*;
pub trait HasPlayClip: HasClock { #[cfg(feature="clip")] impl HasPlayClip for Sequencer {
fn reset (&self) -> bool; fn reset (&self) -> bool { self.reset }
fn reset_mut (&mut self) -> &mut bool; fn reset_mut (&mut self) -> &mut bool { &mut self.reset }
fn play_clip (&self) -> &Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>; fn play_clip (&self) -> &Option<(Moment, Option<Arc<RwLock<MidiClip>>>)> {
fn play_clip_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>; &self.play_clip
fn next_clip (&self) -> &Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>; }
fn next_clip_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>; fn play_clip_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<MidiClip>>>)> {
&mut self.play_clip
}
fn next_clip (&self) -> &Option<(Moment, Option<Arc<RwLock<MidiClip>>>)> {
&self.next_clip
}
fn next_clip_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<MidiClip>>>)> {
&mut self.next_clip
}
}
impl<T: HasPlayClip> PlaybackModel for T {}
pub trait PlaybackModel: HasPlayClip {
fn pulses_since_start (&self) -> Option<f64> { fn pulses_since_start (&self) -> Option<f64> {
if let Some((started, Some(_))) = self.play_clip().as_ref() { if let Some((started, Some(_))) = self.play_clip().as_ref() {
@ -88,20 +101,3 @@ pub trait HasPlayClip: HasClock {
field_v(color, "Next:", format!("{} {}", time, name)) field_v(color, "Next:", format!("{} {}", time, name))
} }
} }
#[cfg(feature="clip")] impl HasPlayClip for Sequencer {
fn reset (&self) -> bool { self.reset }
fn reset_mut (&mut self) -> &mut bool { &mut self.reset }
fn play_clip (&self) -> &Option<(Moment, Option<Arc<RwLock<MidiClip>>>)> {
&self.play_clip
}
fn play_clip_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<MidiClip>>>)> {
&mut self.play_clip
}
fn next_clip (&self) -> &Option<(Moment, Option<Arc<RwLock<MidiClip>>>)> {
&self.next_clip
}
fn next_clip_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<MidiClip>>>)> {
&mut self.next_clip
}
}

View file

@ -1,12 +1,5 @@
use crate::*; use crate::*;
impl <T: AsRef<Sequencer>+AsMut<Sequencer>> HasSequencer for T {}
pub trait HasSequencer: AsRef<Sequencer> + AsMut<Sequencer> {
fn sequencer_mut (&mut self) -> &mut Sequencer { self.as_mut() }
fn sequencer (&self) -> &Sequencer { self.as_ref() }
}
/// Contains state for playing a clip /// Contains state for playing a clip
/// ///
/// ``` /// ```

View file

@ -1,11 +1,10 @@
use crate::*; use crate::*;
pub trait HasProject: AsRef<Option<Project>> + AsMut<Option<Project>> {}
impl<T: AsRef<Option<Project>> + AsMut<Option<Project>>> HasProject for T {}
/// Arranger. /// Arranger.
/// ///
/// ``` /// ```
/// let arranger = tek::Project::default(); /// let arranger = tek::Project::default();
/// ``` /// ```
#[namespace(ClipPool ClipPool::eval)]
#[derive(Default, Debug)] pub struct Project { #[derive(Default, Debug)] pub struct Project {
/// JACK client handle. /// JACK client handle.
pub jack: Jack<'static>, pub jack: Jack<'static>,
@ -204,6 +203,35 @@ impl ClipLength {
self.pulses % self.ppq self.pulses % self.ppq
} }
} }
impl HasTrackScroll for &mut Project {
fn track_scroll (&self) -> usize {
self.track_scroll
}
fn tracks_last (&self) -> usize {
self.track_last
}
fn tracks_last_mut (&mut self) -> &mut usize {
&mut self.track_last
}
}
impl HasTrackScroll for Project {
fn track_scroll (&self) -> usize {
self.track_scroll
}
fn tracks_last (&self) -> usize {
self.track_last
}
fn tracks_last_mut (&mut self) -> &mut usize {
&mut self.track_last
}
}
impl HasSceneScroll for Project {
fn scene_scroll (&self) -> usize { self.scene_scroll }
}
//impl_from!(Pool<MidiClip>: |clip:&Arc<RwLock<MidiClip>>|{ //impl_from!(Pool<MidiClip>: |clip:&Arc<RwLock<MidiClip>>|{
//let model = Self::default(); //let model = Self::default();
//model.clips.try_write().unwrap().push(clip.clone()); //model.clips.try_write().unwrap().push(clip.clone());

View file

@ -46,17 +46,6 @@ impl App {
#[cfg(feature = "port")] pub click_out: Arc<RwLock<Option<AudioOutput>>>, #[cfg(feature = "port")] pub click_out: Arc<RwLock<Option<AudioOutput>>>,
} }
impl<T: AsRef<Clock> + AsMut<Clock>> HasClock for T {}
pub trait HasClock: AsRef<Clock> + AsMut<Clock> {
fn clock (&self) -> &Clock {
self.as_ref()
}
fn clock_mut (&mut self) -> &mut Clock {
self.as_mut()
}
}
//impl<T: HasClock> Act<T> for ClockCmd { //impl<T: HasClock> Act<T> for ClockCmd {
//fn act (&self, state: &mut T) -> Perhaps<Self> { //fn act (&self, state: &mut T) -> Perhaps<Self> {
//self.act(state.clock_mut()) // awesome //self.act(state.clock_mut()) // awesome

View file

@ -189,12 +189,3 @@ impl<T: AsRef<Vec<Device>> + AsMut<Vec<Device>>> HasDevices for T {
self.as_mut() self.as_mut()
} }
} }
pub trait HasDevices: AsRef<Vec<Device>> + AsMut<Vec<Device>> {
fn devices (&self) -> &Vec<Device> {
self.as_ref()
}
fn devices_mut (&mut self) -> &mut Vec<Device> {
self.as_mut()
}
}

View file

@ -48,17 +48,6 @@ impl Scene {
} }
impl<T: AsRefOpt<Scene> + AsMutOpt<Scene> + Send + Sync> HasScene for T {}
pub trait HasScene: AsRefOpt<Scene> + AsMutOpt<Scene> {
fn scene (&self) -> Option<&Scene> {
self.as_ref_opt()
}
fn scene_mut (&mut self) -> Option<&mut Scene> {
self.as_mut_opt()
}
}
pub type SceneWith<'a, T> = (usize, &'a Scene, usize, usize, T); pub type SceneWith<'a, T> = (usize, &'a Scene, usize, usize, T);
def_sizes_iter!(ScenesSizes => Scene); def_sizes_iter!(ScenesSizes => Scene);
@ -66,53 +55,3 @@ impl_as_ref!(Vec<Scene>: |self: App| self.project.as_ref());
impl_as_mut!(Vec<Scene>: |self: App| self.project.as_mut()); impl_as_mut!(Vec<Scene>: |self: App| self.project.as_mut());
impl_as_ref_opt!(Scene: |self: App| self.project.as_ref_opt()); impl_as_ref_opt!(Scene: |self: App| self.project.as_ref_opt());
impl_as_mut_opt!(Scene: |self: App| self.project.as_mut_opt()); impl_as_mut_opt!(Scene: |self: App| self.project.as_mut_opt());
impl<T: AsRef<Vec<Scene>> + AsMut<Vec<Scene>>> HasScenes for T {}
pub trait HasScenes: AsRef<Vec<Scene>> + AsMut<Vec<Scene>> {
fn scenes (&self) -> &Vec<Scene> {
self.as_ref()
}
fn scenes_mut (&mut self) -> &mut Vec<Scene> {
self.as_mut()
}
/// Generate the default name for a new scene
fn scenes_default_name (&self) -> Arc<str> {
format!("s{:3>}", self.scenes().len() + 1).into()
}
fn scenes_longest_name (&self) -> usize {
self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max)
}
/// Add multiple scenes
fn scenes_add_many (&mut self, n: usize)
-> Usually<()> where Self: HasTracks
{
let scene_color_1 = ItemColor::random();
let scene_color_2 = ItemColor::random();
for i in 0..n {
let _ = self.scenes_add_one(None, Some(
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
))?;
}
Ok(())
}
/// Add a scene
fn scenes_add_one (&mut self, name: Option<Arc<str>>, color: Option<ItemTheme>)
-> Usually<(usize, &mut Scene)>
where
Self: HasTracks
{
let scene = Scene {
name: name.map_or_else(||self.scenes_default_name(), |x|x.to_string().into()),
clips: Pool::from(vec![None;self.tracks().len()]),
color: color.unwrap_or_else(ItemTheme::random),
};
self.scenes_mut().push(scene);
let index = self.scenes().len() - 1;
Ok((index, &mut self.scenes_mut()[index]))
}
}
pub trait HasSceneScroll: HasScenes { fn scene_scroll (&self) -> usize; }
impl HasSceneScroll for Project { fn scene_scroll (&self) -> usize { self.scene_scroll } }
impl HasSceneScroll for App { fn scene_scroll (&self) -> usize { self.project.scene_scroll() } }

View file

@ -4,40 +4,6 @@ impl_as_ref_opt!(Track: |self: Project| self.selected_track());
impl_as_mut_opt!(Track: |self: Project| self.selected_track_mut()); impl_as_mut_opt!(Track: |self: Project| self.selected_track_mut());
impl_as_ref_opt!(Track: |self: App| self.project.as_ref_opt()); impl_as_ref_opt!(Track: |self: App| self.project.as_ref_opt());
impl_as_mut_opt!(Track: |self: App| self.project.as_mut_opt()); impl_as_mut_opt!(Track: |self: App| self.project.as_mut_opt());
impl <T: AsRef<Selection>+AsMut<Selection>> HasSelection for T {}
pub trait HasSelection: AsRef<Selection> + AsMut<Selection> {
fn selection (&self) -> &Selection {
self.as_ref()
}
fn selection_mut (&mut self) -> &mut Selection {
self.as_mut()
}
/// Get the active track
#[cfg(feature = "track")] fn selected_track (&self) -> Option<&Track> where Self: HasTracks {
let index = self.selection().track()?;
self.tracks().get(index)
}
/// Get a mutable reference to the active track
#[cfg(feature = "track")] fn selected_track_mut (&mut self) -> Option<&mut Track> where Self: HasTracks {
let index = self.selection().track()?;
self.tracks_mut().get_mut(index)
}
/// Get the active scene
#[cfg(feature = "scene")] fn selected_scene (&self) -> Option<&Scene> where Self: HasScenes {
let index = self.selection().scene()?;
self.scenes().get(index)
}
/// Get a mutable reference to the active scene
#[cfg(feature = "scene")] fn selected_scene_mut (&mut self) -> Option<&mut Scene> where Self: HasScenes {
let index = self.selection().scene()?;
self.scenes_mut().get_mut(index)
}
/// Get the active clip
#[cfg(feature = "clip")] fn selected_clip (&self) -> Option<Slot<MidiClip>> where Self: HasScenes + HasTracks {
self.selected_scene()?.clips.read().get(self.selection().track()?).cloned()
}
}
/// Represents the current user selection in the arranger /// Represents the current user selection in the arranger
#[derive(PartialEq, Clone, Copy, Debug, Default)] #[derive(PartialEq, Clone, Copy, Debug, Default)]
@ -188,3 +154,32 @@ impl Selection {
} }
} }
impl<T: HasSelection> SelectionModel for T {}
pub trait SelectionModel: HasSelection {
/// Get the active track
#[cfg(feature = "track")] fn selected_track (&self) -> Option<&Track> where Self: HasTracks {
let index = self.selection().track()?;
self.tracks().get(index)
}
/// Get a mutable reference to the active track
#[cfg(feature = "track")] fn selected_track_mut (&mut self) -> Option<&mut Track> where Self: HasTracks {
let index = self.selection().track()?;
self.tracks_mut().get_mut(index)
}
/// Get the active scene
#[cfg(feature = "scene")] fn selected_scene (&self) -> Option<&Scene> where Self: HasScenes {
let index = self.selection().scene()?;
self.scenes().get(index)
}
/// Get a mutable reference to the active scene
#[cfg(feature = "scene")] fn selected_scene_mut (&mut self) -> Option<&mut Scene> where Self: HasScenes {
let index = self.selection().scene()?;
self.scenes_mut().get_mut(index)
}
/// Get the active clip
#[cfg(feature = "clip")] fn selected_clip (&self) -> Option<Slot<MidiClip>> where Self: HasScenes + HasTracks {
self.selected_scene()?.clips.read().get(self.selection().track()?).cloned()
}
}

View file

@ -80,148 +80,8 @@ impl HasWidth for Track {
} }
} }
impl<T: AsRefOpt<Track> + AsMutOpt<Track>> HasTrack for T {}
pub trait HasTrack: AsRefOpt<Track> + AsMutOpt<Track> {
fn track (&self) -> Option<&Track> {
self.as_ref_opt()
}
fn track_mut (&mut self) -> Option<&mut Track> {
self.as_mut_opt()
}
}
def_sizes_iter!(TracksSizes => Track); def_sizes_iter!(TracksSizes => Track);
impl_has!(Vec<Track>: |self: Project| self.tracks); impl_has!(Vec<Track>: |self: Project| self.tracks);
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());
impl_has!(Sequencer: |self: Track| self.sequencer); impl_has!(Sequencer: |self: Track| self.sequencer);
impl<T: AsRef<Vec<Track>> + AsMut<Vec<Track>> + HasClock + HasTrackScroll> HasTracks for T {}
pub trait HasTracks: AsRef<Vec<Track>> + AsMut<Vec<Track>> + HasClock + HasTrackScroll {
/// Spacing between tracks.
const TRACK_SPACING: usize = 0;
/// Read-only reference to collection of [Track]s.
fn tracks (&self) -> &Vec<Track> {
self.as_ref()
}
/// Mutable reference to collection of [Track]s.
fn tracks_mut (&mut self) -> &mut Vec<Track> {
self.as_mut()
}
/// Run audio callbacks for every track and every device
fn tracks_jack_process (&mut self, client: &Client, scope: &ProcessScope) -> Control
where Self: HasJack<'static>
{
for track in self.tracks_mut().iter_mut() {
if Control::Quit == Audio::process(&mut track.sequencer, client, scope) {
return Control::Quit
}
for device in track.devices.iter_mut() {
if Control::Quit == DeviceAudio(device).process(client, scope) {
return Control::Quit
}
}
}
Control::Continue
}
/// Add multiple tracks
fn tracks_add_many (
&mut self,
count: usize,
width: Option<usize>,
mins: Arc<[Connect]>,
mouts: Arc<[Connect]>,
) -> Usually<()>
where Self: HasJack<'static> + HasScenes
{
let track_color_1 = ItemColor::random();
let track_color_2 = ItemColor::random();
for i in 0..count {
let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into();
let track = self.tracks_add_one(None, Some(color), mins.clone(), mouts.clone())?.1;
if let Some(width) = width {
track.width = width;
}
}
Ok(())
}
/// Add a track
fn tracks_add_one (
&mut self,
name: Option<Arc<str>>,
color: Option<ItemTheme>,
mins: Arc<[Connect]>,
mouts: Arc<[Connect]>,
) -> Usually<(usize, &mut Track)>
where Self: HasJack<'static> + HasScenes
{
let name: Arc<str> = name.map_or_else(
||format!("trk{}", self.tracks_last()).into(),
|x|x.to_string().into()
);
*self.tracks_last_mut() += 1;
let track = Track::new_added(self.jack(), Some(self.clock()), &name, color, None, mins, mouts)?;
self.tracks_mut().push(track);
let len = self.tracks().len();
let index = len - 1;
for scene in self.scenes_mut().iter_mut() {
while scene.clips.len() < len {
scene.clips.append(None);
}
}
Ok((index, &mut self.tracks_mut()[index]))
}
fn track_longest_name (&self) -> usize {
self.tracks().iter().map(|s|s.name.len()).fold(0, usize::max)
}
}
pub trait HasTrackScroll {
fn track_scroll (&self) -> usize;
fn tracks_last (&self) -> usize;
fn tracks_last_mut (&mut self) -> &mut usize;
}
impl HasTrackScroll for &mut Project {
fn track_scroll (&self) -> usize {
self.track_scroll
}
fn tracks_last (&self) -> usize {
self.track_last
}
fn tracks_last_mut (&mut self) -> &mut usize {
&mut self.track_last
}
}
impl HasTrackScroll for Project {
fn track_scroll (&self) -> usize {
self.track_scroll
}
fn tracks_last (&self) -> usize {
self.track_last
}
fn tracks_last_mut (&mut self) -> &mut usize {
&mut self.track_last
}
}
impl HasTrackScroll for App {
fn track_scroll (&self) -> usize {
self.project.track_scroll()
}
fn tracks_last (&self) -> usize {
self.project.track_last
}
fn tracks_last_mut (&mut self) -> &mut usize {
&mut self.project.track_last
}
}

View file

@ -58,15 +58,6 @@ pub(crate) use symphonia::{
#[cfg(feature = "meter")] pub output_meters: Vec<f32>, #[cfg(feature = "meter")] pub output_meters: Vec<f32>,
} }
pub trait HasSampler: AsRef<Sampler> + AsMut<Sampler> {
fn sampler (&self) -> &Sampler {
self.as_ref()
}
fn sampler_mut (&mut self) -> &mut Sampler {
self.as_mut()
}
}
impl<T: AsRef<Sampler> + AsMut<Sampler>> HasSampler for T {} impl<T: AsRef<Sampler> + AsMut<Sampler>> HasSampler for T {}
impl_audio!(Sampler: sampler_jack_process); impl_audio!(Sampler: sampler_jack_process);

View file

@ -1,5 +1,5 @@
(mode Menu (name Main Menu) (info Welcome to Tek!) (keys Y Confirm) (mode Menu (name Main Menu) (info Welcome to Tek!) (keys Y Confirm)
(view (bar/s 2 Transport (bar/n 2 StatusBar (align/c (bar/s 2 (align/x (bg (g 12) Logo)) Menu)))))) (view (bar/s 2 Transport (bar/n 2 StatusBar (align/c (bar/s 2 (align/x Logo) Menu))))))
(mode Transport (name Transport) (info JACK transport controller.) (mode Transport (name Transport) (info JACK transport controller.)
(keys Clock Global) (keys Clock Global)
(view Transport)) (view Transport))
@ -24,7 +24,7 @@
(keys Global Clock Sampler I IFine Y Page EditorView EditorAdd EditorDel) (keys Global Clock Sampler I IFine Y Page EditorView EditorAdd EditorDel)
(view (bsp/w MetersOut (bsp/e MetersIn (bsp/w GrooveMeta GrooveEditor))))) (view (bsp/w MetersOut (bsp/e MetersIn (bsp/w GrooveMeta GrooveEditor)))))
(view Logo (text tek! 0.4.0 )) (view Logo (bg (g 255) (pad/xy 1 0 (bg (g 0) (fg (g 255) (text tek! 0.4.0 ))))))
(view Browse (bsp/s (pad/xy 3 1 BrowseTitle) (fg (g 96) browser))) (view Browse (bsp/s (pad/xy 3 1 BrowseTitle) (fg (g 96) browser)))
(view PortsOut (bsp/s (align/w (text L-AUDIO-OUT)) (bsp/e (text MIDI-OUT) (align/e (text AUDIO-OUT-R))))) (view PortsOut (bsp/s (align/w (text L-AUDIO-OUT)) (bsp/e (text MIDI-OUT) (align/e (text AUDIO-OUT-R)))))
(view PortsIn (bsp/s (align/w (text L-AUDIO-IN)) (bsp/e (text MIDI-IN) (align/e (text AUDIO-IN-R))))) (view PortsIn (bsp/s (align/w (text L-AUDIO-IN)) (bsp/e (text MIDI-IN) (align/e (text AUDIO-IN-R)))))
@ -36,13 +36,14 @@
(bg (g 20) (full/x (pad/x 1 (bar/s 1 (text Mode) ModeList)))))) (bg (g 20) (full/x (pad/x 1 (bar/s 1 (text Mode) ModeList))))))
(view Transport (bg (g 0) (pad/x 1 (bar/e 4 PlayPause (bar/e 16 Bpm (bar/e 16 Beat Time)))))) (view Transport (bg (g 0) (pad/x 1 (bar/e 4 PlayPause (bar/e 16 Bpm (bar/e 16 Beat Time))))))
(keys Global (@f1 (dialog Help)) (keys Global (@f1 (enter Help))
(@f2 (project new)) (@f2 (project new))
(@f3 (dialog History)) (@f3 (enter History))
(@f6 (dialog :save)) (@f6 (enter Save))
(@f8 (dialog :options)) (@f8 (enter Options))
(@f9 (dialog :load)) (@f9 (enter Load))
(@f10 (dialog :quit))) (@f10 (enter Quit))
(@f12 (enter Devtools)))
(keys Back (@escape cancel)) (keys Back (@escape cancel))
(keys Confirm (@enter confirm)) (keys Confirm (@enter confirm))

View file

@ -45,6 +45,7 @@ mod tek_bind; pub use self::tek_bind::*;
mod tek_cli; pub use self::tek_cli::*; mod tek_cli; pub use self::tek_cli::*;
mod tek_conf; pub use self::tek_conf::*; mod tek_conf; pub use self::tek_conf::*;
mod tek_ctrl; pub use self::tek_ctrl::*; mod tek_ctrl; pub use self::tek_ctrl::*;
mod tek_has; pub use self::tek_has::*;
mod tek_mode; pub use self::tek_mode::*; mod tek_mode; pub use self::tek_mode::*;
mod tek_pool; pub use self::tek_pool::*; mod tek_pool; pub use self::tek_pool::*;
mod tek_view; pub use self::tek_view::*; mod tek_view; pub use self::tek_view::*;
@ -461,14 +462,6 @@ impl ControlAxis {
} }
} }
pub trait HasWidth {
const MIN_WIDTH: usize;
/// Increment track width.
fn width_inc (&mut self);
/// Decrement track width, down to a hardcoded minimum of [Self::MIN_WIDTH].
fn width_dec (&mut self);
}
/// Various possible dialog modes. /// Various possible dialog modes.
/// ///
/// ``` /// ```
@ -538,7 +531,7 @@ impl Dialog {
MenuItem::new(" Create session ", [ MenuItem::new(" Create session ", [
AppCmd::Dialog { dialog: Dialog::None }, AppCmd::Dialog { dialog: Dialog::None },
AppCmd::Mode { command: ModeCmd::Reset { modes: vec!["Arranger".into()] } }, AppCmd::Mode { modes: vec!["Arranger".into()] },
AppCmd::Tracks { command: TracksCmd::Add }, AppCmd::Tracks { command: TracksCmd::Add },
AppCmd::Scenes { command: ScenesCmd::Add }, AppCmd::Scenes { command: ScenesCmd::Add },
AppCmd::MidiClip { command: MidiClipCmd::Upsert }, AppCmd::MidiClip { command: MidiClipCmd::Upsert },
@ -790,3 +783,19 @@ impl App {
if let Dialog::Browse(_, ref b) = self.dialog { Some(b) } else { None } if let Dialog::Browse(_, ref b) = self.dialog { Some(b) } else { None }
} }
} }
impl HasTrackScroll for App {
fn track_scroll (&self) -> usize {
self.project.track_scroll()
}
fn tracks_last (&self) -> usize {
self.project.track_last
}
fn tracks_last_mut (&mut self) -> &mut usize {
&mut self.project.track_last
}
}
impl HasSceneScroll for App {
fn scene_scroll (&self) -> usize { self.project.scene_scroll() }
}

View file

@ -4,8 +4,6 @@ impl_from!(AppCmd: |x: AxisCmd| AppCmd::Axis { command: x });
impl_from!(AppCmd: |x: BrowseCmd| AppCmd::Browse { command: x }); impl_from!(AppCmd: |x: BrowseCmd| AppCmd::Browse { command: x });
impl_from!(AppCmd: |x: MidiClipCmd| AppCmd::MidiClip { command: x }); impl_from!(AppCmd: |x: MidiClipCmd| AppCmd::MidiClip { command: x });
impl_from!(AppCmd: |x: MidiEditCmd| AppCmd::MidiEdit { command: x }); impl_from!(AppCmd: |x: MidiEditCmd| AppCmd::MidiEdit { command: x });
impl_from!(AppCmd: |x: ModeCmd| AppCmd::Mode { command: x });
//impl_from!(AppCmd: |x: PoolCmd| AppCmd::Pool { command: x });
impl_from!(AppCmd: |x: ProjectCmd| AppCmd::Project { command: x }); impl_from!(AppCmd: |x: ProjectCmd| AppCmd::Project { command: x });
impl_from!(AppCmd: |x: SceneCmd| AppCmd::Scene { command: x }); impl_from!(AppCmd: |x: SceneCmd| AppCmd::Scene { command: x });
impl_from!(AppCmd: |x: ScenesCmd| AppCmd::Scenes { command: x }); impl_from!(AppCmd: |x: ScenesCmd| AppCmd::Scenes { command: x });
@ -18,6 +16,25 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x });
self.exit.exit(); self.exit.exit();
Ok(None) Ok(None)
} }
/// Switch to another set of contextual modes.
#[command(Mode = "mode")] pub fn cmd_mode (&mut self, modes: Vec<Arc<str>>) -> Perhaps<AppCmd> {
*self.modes_mut() = modes.into();
Ok(None)
}
/// Enter a contextual mode.
#[command(Enter = "enter")] pub fn cmd_mode_enter (&mut self, mode: Arc<str>) -> Perhaps<AppCmd> {
self.modes_mut().push(mode);
Ok(Some(AppCmd::Leave))
}
/// Leave a contextual mode.
#[command(Leave = "leave")] fn cmd_mode_leave (&mut self) -> Perhaps<AppCmd> {
let mode = self.modes_mut().pop();
Ok(mode.map(|mode|AppCmd::Enter { mode }))
}
/// Navigate along a contextual axis.
#[command(Axis = "axis")] pub fn cmd_axis (&mut self, command: AxisCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
/// Cancel current selection. /// Cancel current selection.
#[command(Cancel = "cancel")] pub fn cmd_cancel (&mut self) -> Perhaps<AppCmd> { #[command(Cancel = "cancel")] pub fn cmd_cancel (&mut self) -> Perhaps<AppCmd> {
todo!("cancel") todo!("cancel")
@ -44,27 +61,22 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x });
std::mem::swap(&mut self.dialog, &mut dialog); std::mem::swap(&mut self.dialog, &mut dialog);
Ok(Some(AppCmd::Dialog { dialog })) Ok(Some(AppCmd::Dialog { dialog }))
} }
#[command(Mode = "mode")] pub fn cmd_mode (&mut self, command: ModeCmd) -> Perhaps<AppCmd> { /// Command the file browser.
Ok(command.dispatch(self)?.map(Into::into))
}
#[command(Project = "session")] pub fn cmd_session (&mut self, command: ProjectCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
#[command(Axis = "axis")] pub fn cmd_axis (&mut self, command: AxisCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
#[command(Browse = "browse")] pub fn cmd_browse (&mut self, command: BrowseCmd) -> Perhaps<AppCmd> { #[command(Browse = "browse")] pub fn cmd_browse (&mut self, command: BrowseCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into)) Ok(command.dispatch(self)?.map(Into::into))
} }
/// Commands that alter the current session.
#[command(Project = "session")] pub fn cmd_session (&mut self, command: ProjectCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
/// Commands pertaining to MIDI clips.
#[command(MidiClip = "clip")] pub fn cmd_midi_clip (&mut self, command: MidiClipCmd) -> Perhaps<AppCmd> { #[command(MidiClip = "clip")] pub fn cmd_midi_clip (&mut self, command: MidiClipCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into)) Ok(command.dispatch(self)?.map(Into::into))
} }
/// Commands pertaining to the MIDI clip editor.
#[command(MidiEdit = "edit")] pub fn cmd_midi_edit (&mut self, command: MidiEditCmd) -> Perhaps<AppCmd> { #[command(MidiEdit = "edit")] pub fn cmd_midi_edit (&mut self, command: MidiEditCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into)) Ok(command.dispatch(self)?.map(Into::into))
} }
//#[command(Pool = "pool")] pub fn cmd_pool (&mut self, command: PoolCmd) -> Perhaps<AppCmd> {
//Ok(command.dispatch(self)?.map(Into::into))
//}
#[command(Scene = "scene")] pub fn cmd_scene (&mut self, command: SceneCmd) -> Perhaps<AppCmd> { #[command(Scene = "scene")] pub fn cmd_scene (&mut self, command: SceneCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into)) Ok(command.dispatch(self)?.map(Into::into))
} }
@ -77,6 +89,9 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x });
#[command(Tracks = "tracks")] pub fn cmd_tracks (&mut self, command: TracksCmd) -> Perhaps<AppCmd> { #[command(Tracks = "tracks")] pub fn cmd_tracks (&mut self, command: TracksCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into)) Ok(command.dispatch(self)?.map(Into::into))
} }
//#[command(Pool = "pool")] pub fn cmd_pool (&mut self, command: PoolCmd) -> Perhaps<AppCmd> {
//Ok(command.dispatch(self)?.map(Into::into))
//}
} }
impl<T: HasProject> ProjectControl for T {} impl<T: HasProject> ProjectControl for T {}
@ -142,29 +157,17 @@ impl<T> ClockControl for T where T: HasClock
} }
} }
impl<T: HasTracks impl<T> TracksControl for T where T: HasTracks
+ HasScenes + HasScenes
+ HasSelection + HasSelection
+ HasJack<'static> + HasJack<'static>
+ Namespace<usize>
+ Namespace<Arc<[Connect]>>
+ Namespace<Option<usize>>
+ Namespace<Option<Arc<str>>>
+ Namespace<Option<ItemTheme>>
+ Namespace<Option<Arc<[Connect]>>>
+ Namespace<ClipPool> + Namespace<ClipPool>
> TracksControl for T {} {}
#[tek_proc::commands(TracksCmd)] pub trait TracksControl: HasTracks #[tek_proc::commands(TracksCmd)] pub trait TracksControl: HasTracks
+ HasScenes + HasScenes
+ HasSelection + HasSelection
+ HasJack<'static> + HasJack<'static>
+ Namespace<usize>
+ Namespace<Arc<[Connect]>>
+ Namespace<Option<usize>>
+ Namespace<Option<Arc<str>>>
+ Namespace<Option<ItemTheme>>
+ Namespace<Option<Arc<[Connect]>>>
+ Namespace<ClipPool> + Namespace<ClipPool>
{ {
/// Stop all playing clips /// Stop all playing clips
@ -190,7 +193,7 @@ impl<T: HasTracks
Ok(None) Ok(None)
} }
#[command(Add = "add")] fn tracks_add (&mut self) -> Perhaps<TracksCmd> #[command(Add = "add")] fn tracks_add (&mut self) -> Perhaps<TracksCmd>
where Self: HasScenes + HasJack<'static> where Self: HasScenes + HasJack<'static> + Sized
{ {
let (_index, _) = self.tracks_add_one(None, None, [].into(), [].into())?; let (_index, _) = self.tracks_add_one(None, None, [].into(), [].into())?;
Ok(None) Ok(None)
@ -212,6 +215,76 @@ impl<T: HasTracks
*self.selection_mut() = select; *self.selection_mut() = select;
Ok(None) Ok(None)
} }
/// Run audio callbacks for every track and every device
fn tracks_jack_process (&mut self, client: &Client, scope: &ProcessScope) -> Control
where Self: HasJack<'static>
{
for track in self.tracks_mut().iter_mut() {
if Control::Quit == Audio::process(&mut track.sequencer, client, scope) {
return Control::Quit
}
for device in track.devices.iter_mut() {
if Control::Quit == DeviceAudio(device).process(client, scope) {
return Control::Quit
}
}
}
Control::Continue
}
/// Add multiple tracks
fn tracks_add_many (
&mut self,
count: usize,
width: Option<usize>,
mins: Arc<[Connect]>,
mouts: Arc<[Connect]>,
) -> Usually<()>
where Self: HasJack<'static> + HasScenes
{
let track_color_1 = ItemColor::random();
let track_color_2 = ItemColor::random();
for i in 0..count {
let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into();
let track = self.tracks_add_one(None, Some(color), mins.clone(), mouts.clone())?.1;
if let Some(width) = width {
track.width = width;
}
}
Ok(())
}
/// Add a track
fn tracks_add_one (
&mut self,
name: Option<Arc<str>>,
color: Option<ItemTheme>,
mins: Arc<[Connect]>,
mouts: Arc<[Connect]>,
) -> Usually<(usize, &mut Track)>
where Self: HasJack<'static> + HasScenes
{
let name: Arc<str> = name.map_or_else(
||format!("trk{}", self.tracks_last()).into(),
|x|x.to_string().into()
);
*self.tracks_last_mut() += 1;
let track = Track::new_added(self.jack(), Some(self.clock()), &name, color, None, mins, mouts)?;
self.tracks_mut().push(track);
let len = self.tracks().len();
let index = len - 1;
for scene in self.scenes_mut().iter_mut() {
while scene.clips.len() < len {
scene.clips.append(None);
}
}
Ok((index, &mut self.tracks_mut()[index]))
}
fn track_longest_name (&self) -> usize {
self.tracks().iter().map(|s|s.name.len()).fold(0, usize::max)
}
} }
impl<T: HasTrack impl<T: HasTrack
@ -283,9 +356,6 @@ impl<T: HasScenes
+ HasTrackScroll + HasTrackScroll
+ HasSelection + HasSelection
+ HasJack<'static> + HasJack<'static>
+ Namespace<usize>
+ Namespace<Option<ItemTheme>>
+ Namespace<Option<Arc<str>>>
> ScenesControl for T {} > ScenesControl for T {}
#[tek_proc::commands(ScenesCmd)] pub trait ScenesControl: HasScenes #[tek_proc::commands(ScenesCmd)] pub trait ScenesControl: HasScenes
@ -293,11 +363,8 @@ impl<T: HasScenes
+ HasTrackScroll + HasTrackScroll
+ HasSelection + HasSelection
+ HasJack<'static> + HasJack<'static>
+ Namespace<usize>
+ Namespace<Option<ItemTheme>>
+ Namespace<Option<Arc<str>>>
{ {
#[command(Add = "add")] fn scenes_add (&mut self) -> Perhaps<ScenesCmd> { #[command(Add = "add")] fn scenes_add (&mut self) -> Perhaps<ScenesCmd> where Self: Sized {
let (_index, _) = self.scenes_add_one(None, None)?; let (_index, _) = self.scenes_add_one(None, None)?;
Ok(None) Ok(None)
} }
@ -318,6 +385,41 @@ impl<T: HasScenes
*self.selection_mut() = select; *self.selection_mut() = select;
Ok(None) Ok(None)
} }
/// Generate the default name for a new scene
fn scenes_default_name (&self) -> Arc<str> {
format!("s{:3>}", self.scenes().len() + 1).into()
}
fn scenes_longest_name (&self) -> usize {
self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max)
}
/// Add multiple scenes
fn scenes_add_many (&mut self, n: usize)
-> Usually<()> where Self: HasTracks
{
let scene_color_1 = ItemColor::random();
let scene_color_2 = ItemColor::random();
for i in 0..n {
let _ = self.scenes_add_one(None, Some(
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
))?;
}
Ok(())
}
/// Add a scene
fn scenes_add_one (&mut self, name: Option<Arc<str>>, color: Option<ItemTheme>)
-> Usually<(usize, &mut Scene)>
where
Self: HasTracks
{
let scene = Scene {
name: name.map_or_else(||self.scenes_default_name(), |x|x.to_string().into()),
clips: Pool::from(vec![None;self.tracks().len()]),
color: color.unwrap_or_else(ItemTheme::random),
};
self.scenes_mut().push(scene);
let index = self.scenes().len() - 1;
Ok((index, &mut self.scenes_mut()[index]))
}
} }
impl<T: HasScene impl<T: HasScene
@ -721,36 +823,6 @@ impl<T: HasClip + HasEditor
////} ////}
//} //}
impl<T> ModeControl for T where T: HasModeStack
+ Namespace<Vec<Arc<str>>> + Namespace<Arc<str>> + Namespace<usize>
{}
#[tek_proc::commands(ModeCmd)] pub trait ModeControl: HasModeStack
+ Namespace<Vec<Arc<str>>> + Namespace<Arc<str>> + Namespace<usize>
{
#[command(Reset = "reset")] fn mode_reset (&mut self, modes: Vec<Arc<str>>) -> Perhaps<ModeCmd> {
*self.modes_mut() = modes.into();
Ok(None)
}
#[command(Push = "push")] fn mode_push (&mut self, mode: Arc<str>) -> Perhaps<ModeCmd> {
self.modes_mut().push(mode);
Ok(Some(ModeCmd::Pop))
}
#[command(Pop = "pop")] fn mode_pop (&mut self) -> Perhaps<ModeCmd> {
let mode = self.modes_mut().pop();
Ok(mode.map(|mode|ModeCmd::Push { mode }))
}
}
pub trait HasClip {
fn clip (&self) -> Option<ArcRwLockReadGuard<RawRwLock, Option<Arc<RwLock<MidiClip>>>>> {
self.clip_slot().map(|slot|slot.read())
}
fn clip_mut (&self) -> Option<ArcRwLockWriteGuard<RawRwLock, Option<Arc<RwLock<MidiClip>>>>> {
self.clip_slot().map(|slot|slot.write())
}
fn clip_slot (&self) -> Option<Slot<MidiClip>>;
}
impl<T> MidiClipControl for T where T: HasClip impl<T> MidiClipControl for T where T: HasClip
+ Namespace<Option<bool>> + Namespace<Option<ItemTheme>> {} + Namespace<Option<bool>> + Namespace<Option<ItemTheme>> {}
#[tek_proc::commands(MidiClipCmd)] pub trait MidiClipControl: HasClip #[tek_proc::commands(MidiClipCmd)] pub trait MidiClipControl: HasClip

198
src/tek_has.rs Normal file
View file

@ -0,0 +1,198 @@
use crate::*;
pub trait HasModeStack {
fn modes (&self) -> RwLockReadGuard<'_, Vec<Arc<str>>>;
fn modes_mut (&mut self) -> RwLockWriteGuard<'_, Vec<Arc<str>>>;
}
impl<T> HasEditor for T where T:
AsRef<Option<MidiEditor>> + AsMut<Option<MidiEditor>> {}
pub trait HasEditor:
AsRef<Option<MidiEditor>> + AsMut<Option<MidiEditor>>
{
fn editor (&self) -> &Option<MidiEditor> {
self.as_ref()
}
fn editor_mut (&mut self) -> &mut Option<MidiEditor> {
self.as_mut()
}
}
pub trait HasMidiBuffers {
fn note_buf_mut (&mut self) -> &mut Vec<u8>;
fn midi_buf_mut (&mut self) -> &mut Vec<Vec<Vec<u8>>>;
}
pub trait HasPlayClip: HasClock {
fn reset (&self) -> bool;
fn reset_mut (&mut self) -> &mut bool;
fn play_clip (&self) -> &Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>;
fn play_clip_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>;
fn next_clip (&self) -> &Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>;
fn next_clip_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>;
}
impl <T> HasSequencer for T where T:
AsRef<Sequencer> + AsMut<Sequencer> {}
pub trait HasSequencer:
AsRef<Sequencer> + AsMut<Sequencer>
{
fn sequencer (&self) -> &Sequencer {
self.as_ref()
}
fn sequencer_mut (&mut self) -> &mut Sequencer {
self.as_mut()
}
}
impl<T> HasClock for T where T:
AsRef<Clock> + AsMut<Clock> {}
pub trait HasClock:
AsRef<Clock> + AsMut<Clock>
{
fn clock (&self) -> &Clock {
self.as_ref()
}
fn clock_mut (&mut self) -> &mut Clock {
self.as_mut()
}
}
pub trait HasDevices:
AsRef<Vec<Device>> + AsMut<Vec<Device>>
{
fn devices (&self) -> &Vec<Device> {
self.as_ref()
}
fn devices_mut (&mut self) -> &mut Vec<Device> {
self.as_mut()
}
}
pub trait HasProject:
AsRef<Option<Project>> + AsMut<Option<Project>>
{}
impl<T> HasProject for T where T:
AsRef<Option<Project>> + AsMut<Option<Project>>
{}
pub trait HasSceneScroll: HasScenes {
fn scene_scroll (&self) -> usize;
}
impl<T> HasScene for T where T:
AsRefOpt<Scene> + AsMutOpt<Scene> + Send + Sync
{}
pub trait HasScene:
AsRefOpt<Scene> + AsMutOpt<Scene>
{
fn scene (&self) -> Option<&Scene> {
self.as_ref_opt()
}
fn scene_mut (&mut self) -> Option<&mut Scene> {
self.as_mut_opt()
}
}
impl<T> HasScenes for T where T:
AsRef<Vec<Scene>> + AsMut<Vec<Scene>>
{}
pub trait HasScenes: AsRef<Vec<Scene>> + AsMut<Vec<Scene>> {
fn scenes (&self) -> &Vec<Scene> {
self.as_ref()
}
fn scenes_mut (&mut self) -> &mut Vec<Scene> {
self.as_mut()
}
}
impl<T> HasSelection for T where T:
AsRef<Selection> + AsMut<Selection>
{}
pub trait HasSelection: AsRef<Selection> + AsMut<Selection> {
fn selection (&self) -> &Selection {
self.as_ref()
}
fn selection_mut (&mut self) -> &mut Selection {
self.as_mut()
}
}
impl<T> HasTrack for T where T:
AsRefOpt<Track> + AsMutOpt<Track>
{}
pub trait HasTrack: AsRefOpt<Track> + AsMutOpt<Track> {
fn track (&self) -> Option<&Track> {
self.as_ref_opt()
}
fn track_mut (&mut self) -> Option<&mut Track> {
self.as_mut_opt()
}
}
impl<T> HasTracks for T where T:
AsRef<Vec<Track>> + AsMut<Vec<Track>> + HasClock + HasTrackScroll
{}
pub trait HasTracks: AsRef<Vec<Track>> + AsMut<Vec<Track>> + HasClock + HasTrackScroll {
/// Read-only reference to collection of [Track]s.
fn tracks (&self) -> &Vec<Track> {
self.as_ref()
}
/// Mutable reference to collection of [Track]s.
fn tracks_mut (&mut self) -> &mut Vec<Track> {
self.as_mut()
}
}
pub trait HasTrackScroll {
fn track_scroll (&self) -> usize;
fn tracks_last (&self) -> usize;
fn tracks_last_mut (&mut self) -> &mut usize;
}
pub trait HasSampler: AsRef<Sampler> + AsMut<Sampler> {
fn sampler (&self) -> &Sampler {
self.as_ref()
}
fn sampler_mut (&mut self) -> &mut Sampler {
self.as_mut()
}
}
pub trait HasClip {
fn clip_slot (&self) -> Option<Slot<MidiClip>>;
fn clip (&self) -> Option<ArcRwLockReadGuard<RawRwLock, Option<Arc<RwLock<MidiClip>>>>> {
self.clip_slot().map(|slot|slot.read())
}
fn clip_mut (&self) -> Option<ArcRwLockWriteGuard<RawRwLock, Option<Arc<RwLock<MidiClip>>>>> {
self.clip_slot().map(|slot|slot.write())
}
}
pub trait HasWidth {
const MIN_WIDTH: usize;
/// Increment track width.
fn width_inc (&mut self);
/// Decrement track width, down to a hardcoded minimum of [Self::MIN_WIDTH].
fn width_dec (&mut self);
}
pub trait HasClipsSize {
/// Size of clips area.
fn clips_size (&self) -> &Sizer;
/// Draw content annotated with value of [Self::clips_size].
fn with_clips_size (&self, show: bool, content: impl Draw<Tui>) -> impl Draw<Tui> {
let size = self.clips_size();
above(
when(show, fg(Green, east!(size.w() as usize, "x", size.h() as usize)).align_se()),
size.of(content.align_c().full_wh())
)
}
}

View file

@ -116,11 +116,6 @@ impl AsMut<Mode> for Mode {
} }
} }
pub trait HasModeStack {
fn modes (&self) -> RwLockReadGuard<'_, Vec<Arc<str>>>;
fn modes_mut (&mut self) -> RwLockWriteGuard<'_, Vec<Arc<str>>>;
}
impl HasModeStack for App { impl HasModeStack for App {
fn modes (&self) -> RwLockReadGuard<'_, Vec<Arc<str>>> { fn modes (&self) -> RwLockReadGuard<'_, Vec<Arc<str>>> {
self.modes.read() self.modes.read()

View file

@ -41,7 +41,7 @@ impl<T> Pool<T> {
} }
impl Pool<MidiClip> { impl Pool<MidiClip> {
pub fn eval <'a> (_: &App, _: impl Language) -> Perhaps<ClipPool> { pub fn eval <'a, T> (_: &T, _: impl Language) -> Perhaps<ClipPool> {
todo!() todo!()
} }
pub fn remove (&self, filter: &MidiClip) { pub fn remove (&self, filter: &MidiClip) {

View file

@ -600,18 +600,6 @@ impl OctaveVertical {
impl HasClipsSize for App { fn clips_size (&self) -> &Sizer { &self.project.size_inner } } impl HasClipsSize for App { fn clips_size (&self) -> &Sizer { &self.project.size_inner } }
impl HasClipsSize for Project { fn clips_size (&self) -> &Sizer { &self.size_inner } } impl HasClipsSize for Project { fn clips_size (&self) -> &Sizer { &self.size_inner } }
pub trait HasClipsSize {
/// Size of clips area.
fn clips_size (&self) -> &Sizer;
/// Draw content annotated with value of [Self::clips_size].
fn with_clips_size (&self, show: bool, content: impl Draw<Tui>) -> impl Draw<Tui> {
let size = self.clips_size();
above(
when(show, fg(Green, east!(size.w() as usize, "x", size.h() as usize)).align_se()),
size.of(content.align_c().full_wh())
)
}
}
impl MidiEditor { impl MidiEditor {
pub fn clip_status (&self) -> impl Draw<Tui> + '_ { pub fn clip_status (&self) -> impl Draw<Tui> + '_ {
@ -668,6 +656,8 @@ impl<T> TracksView for T where T:
pub trait TracksView: pub trait TracksView:
HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTrack HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTrack
{ {
/// Spacing between tracks.
const TRACK_SPACING: usize = 0;
/// Iterate over tracks with their corresponding sizes. /// Iterate over tracks with their corresponding sizes.
fn tracks_with_sizes (&self) -> impl TracksSizes<'_> { fn tracks_with_sizes (&self) -> impl TracksSizes<'_> {
@ -880,7 +870,7 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize +
fn w_side (&self) -> u16; fn w_side (&self) -> u16;
fn w_mid (&self) -> u16; fn w_mid (&self) -> u16;
fn view_scenes_names (&self) -> impl Draw<Tui> { fn view_scenes_names (&self) -> impl Draw<Tui> where Self: Sized {
let select = self.selection(); let select = self.selection();
let editor = self.editor(); let editor = self.editor();
let editing = self.is_editing(); let editing = self.is_editing();