use new AsRefOpt/AsMutOpt glue traits

This commit is contained in:
okay stopped screaming 2026-02-23 22:49:20 +02:00
parent 6295f2e601
commit ab90129f4c
7 changed files with 661 additions and 740 deletions

View file

@ -50,7 +50,6 @@ proptest-derive = { version = "^0.5.1" }
[features] [features]
default = ["cli", "arranger", "sampler"] default = ["cli", "arranger", "sampler"]
arranger = ["port", "editor", "sequencer", "track", "scene", "clip", "select"] arranger = ["port", "editor", "sequencer", "track", "scene", "clip", "select"]
browse = [] browse = []
clap = [] clap = []

View file

@ -2,7 +2,7 @@ export RUSTFLAGS := "--cfg procmacro2_semver_exempt -Zmacro-backtrace -Clink-arg
export RUST_BACKTRACE := "1" export RUST_BACKTRACE := "1"
default +ARGS="new": default +ARGS="new":
target/debug/tek {{ARGS}} cargo run -- {{ARGS}}
doc +ARGS="": doc +ARGS="":
cargo doc --open -j4 --document-private-items {{ARGS}} cargo doc --open -j4 --document-private-items {{ARGS}}

View file

@ -59,7 +59,6 @@ pub(crate) use tengri::{
pub(crate) use ConnectName::*; pub(crate) use ConnectName::*;
pub(crate) use ConnectScope::*; pub(crate) use ConnectScope::*;
pub(crate) use ConnectStatus::*; pub(crate) use ConnectStatus::*;
pub(crate) use JackState::*;
/// Command-line entrypoint. /// Command-line entrypoint.
#[cfg(feature = "cli")] pub fn main () -> Usually<()> { #[cfg(feature = "cli")] pub fn main () -> Usually<()> {
@ -70,7 +69,7 @@ pub(crate) use JackState::*;
/// Create a new application from a backend, project, config, and mode /// Create a new application from a backend, project, config, and mode
/// ///
/// ``` /// ```
/// let jack = tek::Jack::new(&"test_tek").expect("failed to connect to jack"); /// let jack = tek::tengri::Jack::new(&"test_tek").expect("failed to connect to jack");
/// let proj = tek::Arrangement::default(); /// let proj = tek::Arrangement::default();
/// let mut conf = tek::Config::default(); /// let mut conf = tek::Config::default();
/// conf.add("(mode hello)"); /// conf.add("(mode hello)");

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@ use builder_pattern::Builder;
/// Total state /// Total state
/// ///
/// ``` /// ```
/// use tek::{TracksView, ScenesView, AddScene}; /// use tek::{HasTracks, HasScenes, TracksView, ScenesView};
/// let mut app = tek::App::default(); /// let mut app = tek::App::default();
/// let _ = app.scene_add(None, None).unwrap(); /// let _ = app.scene_add(None, None).unwrap();
/// let _ = app.update_clock(); /// let _ = app.update_clock();

View file

@ -125,24 +125,55 @@ pub trait HasMidiClip {
fn clip (&self) -> Option<Arc<RwLock<MidiClip>>>; fn clip (&self) -> Option<Arc<RwLock<MidiClip>>>;
} }
pub trait HasClock: AsRef<Clock> + AsMut<Clock> { pub trait HasClock: AsRef<Clock> + AsMut<Clock> {
fn clock_mut (&mut self) -> &mut Clock { self.as_mut() }
fn clock (&self) -> &Clock { self.as_ref() } 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>> { pub trait HasDevices: AsRef<Vec<Device>> + AsMut<Vec<Device>> {
fn devices_mut (&mut self) -> &mut Vec<Device> { self.as_mut() }
fn devices (&self) -> &Vec<Device> { self.as_ref() } fn devices (&self) -> &Vec<Device> { self.as_ref() }
} fn devices_mut (&mut self) -> &mut Vec<Device> { self.as_mut() }
pub trait HasSelection: AsRef<Selection> + AsMut<Selection> {
fn selection_mut (&mut self) -> &mut Selection { self.as_mut() }
fn selection (&self) -> &Selection { self.as_ref() }
} }
pub trait HasSequencer: AsRef<Sequencer> + AsMut<Sequencer> { pub trait HasSequencer: AsRef<Sequencer> + AsMut<Sequencer> {
fn sequencer_mut (&mut self) -> &mut Sequencer { self.as_mut() } fn sequencer_mut (&mut self) -> &mut Sequencer { self.as_mut() }
fn sequencer (&self) -> &Sequencer { self.as_ref() } fn sequencer (&self) -> &Sequencer { self.as_ref() }
} }
pub trait HasScene: AsRef<Option<Scene>> + AsMut<Option<Scene>> { pub trait HasSceneScroll: HasScenes { fn scene_scroll (&self) -> usize; }
fn scene_mut (&mut self) -> &mut Option<Scene> { self.as_mut() } pub trait HasTrackScroll: HasTracks { fn track_scroll (&self) -> usize; }
fn scene (&self) -> Option<&Scene> { self.as_ref() } pub trait HasScene: AsRefOpt<Scene> + AsMutOpt<Scene> {
fn scene_mut (&mut self) -> Option<&mut Scene> { self.as_mut_opt() }
fn scene (&self) -> Option<&Scene> { self.as_ref_opt() }
}
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<Arc<RwLock<MidiClip>>> where Self: HasScenes + HasTracks {
self.selected_scene()?.clips.get(self.selection().track()?)?.clone()
}
} }
pub trait HasScenes: 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 (&self) -> &Vec<Scene> { self.as_ref() }
@ -150,12 +181,30 @@ pub trait HasScenes: AsRef<Vec<Scene>> + AsMut<Vec<Scene>> {
/// Generate the default name for a new scene /// Generate the default name for a new scene
fn scene_default_name (&self) -> Arc<str> { format!("s{:3>}", self.scenes().len() + 1).into() } fn scene_default_name (&self) -> Arc<str> { format!("s{:3>}", self.scenes().len() + 1).into() }
fn scene_longest_name (&self) -> usize { self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max) } fn scene_longest_name (&self) -> usize { self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max) }
} /// Add multiple scenes
pub trait HasSceneScroll: HasScenes { fn scenes_add (&mut self, n: usize) -> Usually<()> where Self: HasTracks {
fn scene_scroll (&self) -> usize; let scene_color_1 = ItemColor::random();
} let scene_color_2 = ItemColor::random();
pub trait HasTrackScroll: HasTracks { for i in 0..n {
fn track_scroll (&self) -> usize; let _ = self.scene_add(None, Some(
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
))?;
}
Ok(())
}
/// Add a scene
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemTheme>)
-> Usually<(usize, &mut Scene)> where Self: HasTracks
{
let scene = Scene {
name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()),
clips: 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 HasWidth { pub trait HasWidth {
const MIN_WIDTH: usize; const MIN_WIDTH: usize;
@ -170,39 +219,26 @@ pub trait HasMidiBuffers {
} }
/// ``` /// ```
/// use tek::{MidiEditor, HasEditor, tengri::Has}; /// use tek::{*, tengri::*};
/// ///
/// let mut host = TestEditorHost(Some(MidiEditor::default())); /// struct Test(Option<MidiEditor>);
/// struct TestEditorHost(Option<MidiEditor>); /// impl_as_ref_opt!(MidiEditor: |self: Test|self.0.as_ref());
/// impl AsRef<Option<MidiEditor>> for TestEditorHost { /// impl_as_mut_opt!(MidiEditor: |self: Test|self.0.as_mut());
/// fn get (&self) -> &Option<MidiEditor> { &self.0 }
/// fn get_mut (&mut self) -> &mut Option<MidiEditor> { &mut self.0 }
/// }
/// ///
/// let mut host = Test(Some(MidiEditor::default()));
/// let _ = host.editor(); /// let _ = host.editor();
/// let _ = host.editor_mut(); /// let _ = host.editor_mut();
/// let _ = host.is_editing(); /// let _ = host.is_editing();
/// let _ = host.editor_w(); /// let _ = host.editor_w();
/// let _ = host.editor_h(); /// let _ = host.editor_h();
/// ``` /// ```
pub trait HasEditor: AsRef<Option<MidiEditor>> { pub trait HasEditor: AsRefOpt<MidiEditor> + AsMutOpt<MidiEditor> {
fn editor (&self) -> Option<&MidiEditor> { fn editor (&self) -> Option<&MidiEditor> { self.as_ref_opt() }
self.get().as_ref() fn editor_mut (&mut self) -> Option<&mut MidiEditor> { self.as_mut_opt() }
} fn is_editing (&self) -> bool { self.editor().is_some() }
fn editor_mut (&mut self) -> Option<&mut MidiEditor> { fn editor_w (&self) -> usize { self.editor().map(|e|e.size.w()).unwrap_or(0) as usize }
self.get_mut().as_mut() fn editor_h (&self) -> usize { self.editor().map(|e|e.size.h()).unwrap_or(0) as usize }
}
fn is_editing (&self) -> bool {
self.editor().is_some()
}
fn editor_w (&self) -> usize {
self.editor().map(|e|e.size.w()).unwrap_or(0) as usize
}
fn editor_h (&self) -> usize {
self.editor().map(|e|e.size.h()).unwrap_or(0) as usize
}
} }
pub trait HasClips { pub trait HasClips {
fn clips <'a> (&'a self) -> std::sync::RwLockReadGuard<'a, ClipPool>; fn clips <'a> (&'a self) -> std::sync::RwLockReadGuard<'a, ClipPool>;
fn clips_mut <'a> (&'a self) -> std::sync::RwLockWriteGuard<'a, ClipPool>; fn clips_mut <'a> (&'a self) -> std::sync::RwLockWriteGuard<'a, ClipPool>;
@ -212,7 +248,6 @@ pub trait HasClips {
(self.clips().len() - 1, clip) (self.clips().len() - 1, clip)
} }
} }
/// Trait for thing that may receive MIDI. /// Trait for thing that may receive MIDI.
pub trait HasMidiIns { pub trait HasMidiIns {
fn midi_ins (&self) -> &Vec<MidiInput>; fn midi_ins (&self) -> &Vec<MidiInput>;
@ -237,7 +272,6 @@ pub trait HasMidiIns {
}) })
} }
} }
/// Trait for thing that may output MIDI. /// Trait for thing that may output MIDI.
pub trait HasMidiOuts { pub trait HasMidiOuts {
fn midi_outs (&self) -> &Vec<MidiOutput>; fn midi_outs (&self) -> &Vec<MidiOutput>;
@ -259,9 +293,7 @@ pub trait HasMidiOuts {
} }
} }
} }
pub trait HasTracks: AsRef<Vec<Track>> + AsMut<Vec<Track>> {
impl<T: AsRef<Vec<Track>> + AsMut<Vec<Track>> + Send + Sync> HasTracks for T {}
pub trait HasTracks: AsRef<Vec<Track>> + AsMut<Vec<Track>> + Send + Sync {
fn tracks (&self) -> &Vec<Track> { self.as_ref() } fn tracks (&self) -> &Vec<Track> { self.as_ref() }
fn tracks_mut (&mut self) -> &mut Vec<Track> { self.as_mut() } fn tracks_mut (&mut self) -> &mut Vec<Track> { self.as_mut() }
/// Run audio callbacks for every track and every device /// Run audio callbacks for every track and every device
@ -292,10 +324,9 @@ pub trait HasTracks: AsRef<Vec<Track>> + AsMut<Vec<Track>> + Send + Sync {
/// Spacing between tracks. /// Spacing between tracks.
const TRACK_SPACING: usize = 0; const TRACK_SPACING: usize = 0;
} }
pub trait HasTrack: AsRefOpt<Track> + AsMutOpt<Track> {
pub trait HasTrack { fn track (&self) -> Option<&Track> { self.as_ref_opt() }
fn track (&self) -> Option<&Track>; fn track_mut (&mut self) -> Option<&mut Track> { self.as_mut_opt() }
fn track_mut (&mut self) -> Option<&mut Track>;
#[cfg(feature = "port")] fn view_midi_ins_status <'a> (&'a self, theme: ItemTheme) -> impl Content<TuiOut> + 'a { #[cfg(feature = "port")] fn view_midi_ins_status <'a> (&'a self, theme: ItemTheme) -> impl Content<TuiOut> + 'a {
self.track().map(move|track|view_ports_status(theme, "MIDI ins: ", &track.sequencer.midi_ins)) self.track().map(move|track|view_ports_status(theme, "MIDI ins: ", &track.sequencer.midi_ins))
} }
@ -523,33 +554,6 @@ pub trait MidiViewer: Measured<TuiOut> + MidiRange + MidiPoint + Debug + Send +
} }
} }
pub trait AddScene: HasScenes + HasTracks {
/// Add multiple scenes
fn scenes_add (&mut self, n: usize) -> Usually<()> {
let scene_color_1 = ItemColor::random();
let scene_color_2 = ItemColor::random();
for i in 0..n {
let _ = self.scene_add(None, Some(
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
))?;
}
Ok(())
}
/// Add a scene
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemTheme>)
-> Usually<(usize, &mut Scene)>
{
let scene = Scene {
name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()),
clips: 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 ClipsView: TracksView + ScenesView { pub trait ClipsView: TracksView + ScenesView {
fn view_scenes_clips <'a> (&'a self) fn view_scenes_clips <'a> (&'a self)

2
tengri

@ -1 +1 @@
Subproject commit c1b727bafc27dc715d7239a0bc63a1cdfe962bc2 Subproject commit d1c08df5351ce8c3913723602a05268d593c9a45