mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-10-08 18:56:42 +02:00
tek_has
This commit is contained in:
parent
0a89fab28a
commit
9e3cc1139d
18 changed files with 450 additions and 414 deletions
|
|
@ -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.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
||||||
|
|
@ -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]>>;
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
||||||
32
src/proj.rs
32
src/proj.rs
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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() } }
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
19
src/tek.edn
19
src/tek.edn
|
|
@ -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))
|
||||||
|
|
|
||||||
27
src/tek.rs
27
src/tek.rs
|
|
@ -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() }
|
||||||
|
}
|
||||||
|
|
|
||||||
204
src/tek_ctrl.rs
204
src/tek_ctrl.rs
|
|
@ -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
198
src/tek_has.rs
Normal 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())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue