piano keys reappear

This commit is contained in:
i do not exist 2026-09-23 16:08:54 +03:00
parent 9db60c3429
commit 9a50ce6613
15 changed files with 856 additions and 782 deletions

View file

@ -48,6 +48,7 @@ use crate::*;
/// Scroll offset of scenes
#[cfg(feature = "scene")] pub scene_scroll: usize,
}
impl Project {
/// Create a new arrangement.
pub fn new (
@ -104,7 +105,7 @@ impl Project {
//1 + self.devices_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0)
}
}
impl HasJack<'static> for Project { fn jack (&self) -> &Jack<'static> { &self.jack } }
impl_has!(Jack<'static>: |self: Project| self.jack);
impl_has!(Sizer: |self: Project| self.size);
impl_has!(Clock: |self: Project| self.clock);
@ -114,6 +115,13 @@ impl_as_ref_opt!(Scene: |self: Project| self.selected_scene());
impl_as_mut_opt!(Scene: |self: Project| self.selected_scene_mut());
impl_as_ref!(Option<MidiEditor>: |self: Project| &self.editor);
impl_as_mut!(Option<MidiEditor>: |self: Project| &mut self.editor);
impl HasJack<'static> for Project {
fn jack (&self) -> &Jack<'static> {
&self.jack
}
}
impl HasClip for Project {
fn clip_slot (&self) -> Option<Slot<MidiClip>> {
match self.selection {
@ -126,6 +134,25 @@ impl HasClip for Project {
}
}
}
impl HasClipsSize for Project {
fn clips_size (&self) -> &Sizer {
&self.size_inner
}
}
impl ScenesView for Project {
fn h_scenes (&self) -> u16 {
(self.size.h() as u16).saturating_sub(20)
}
fn w_side (&self) -> u16 {
(self.size.w() as u16 * 2 / 10).max(20)
}
fn w_mid (&self) -> u16 {
(self.size.w() as u16).saturating_sub(2 * self.w_side()).max(40)
}
}
pub fn swap_value <T: Clone + PartialEq, U> (
target: &mut T, value: &T, returned: impl Fn(T)->U
) -> Perhaps<U> {
@ -137,6 +164,7 @@ pub fn swap_value <T: Clone + PartialEq, U> (
Ok(Some(returned(value)))
}
}
pub fn toggle_bool <U> (
target: &mut bool, value: &Option<bool>, returned: impl Fn(Option<bool>)->U
) -> Perhaps<U> {
@ -148,6 +176,7 @@ pub fn toggle_bool <U> (
Ok(Some(returned(Some(value))))
}
}
/// Modes for clip pool
#[derive(Debug, Clone)] pub enum PoolMode {
/// Renaming a pattern
@ -159,6 +188,7 @@ pub fn toggle_bool <U> (
/// Save clip to disk
Export(usize, Browse),
}
/// Focused field of `ClipLength`
#[derive(Copy, Clone, Debug)] pub enum ClipLengthFocus {
/// Editing the number of bars
@ -168,6 +198,7 @@ pub fn toggle_bool <U> (
/// Editing the number of ticks
Tick,
}
impl ClipLengthFocus {
pub fn next (&mut self) {
use ClipLengthFocus::*;
@ -178,6 +209,7 @@ impl ClipLengthFocus {
*self = match self { Bar => Tick, Beat => Bar, Tick => Beat, }
}
}
/// Displays and edits clip length.
#[derive(Clone, Debug, Default)] pub struct ClipLength {
/// Pulses per beat (quaver)
@ -189,6 +221,7 @@ impl ClipLengthFocus {
/// Selected subdivision
pub focus: Option<ClipLengthFocus>,
}
impl ClipLength {
pub fn _new (pulses: usize, focus: Option<ClipLengthFocus>) -> Self {
Self { ppq: PPQ, bpb: 4, pulses, focus }
@ -232,6 +265,205 @@ impl HasSceneScroll for Project {
fn scene_scroll (&self) -> usize { self.scene_scroll }
}
def_sizes_iter!(PortsSizes => Arc<str>, [Connect]);
impl_has!(Vec<MidiInput>: |self: Project| self.midi_ins);
impl_has!(Vec<MidiOutput>: |self: Project| self.midi_outs);
impl_has!(Vec<MidiInput>: |self: App|self.project.midi_ins);
impl_has!(Vec<MidiOutput>: |self: App|self.project.midi_outs);
pub struct Junction<T: JackPort>(T);
impl<T: JackPort> Draw<Tui> for Junction<T> {
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
T::KIND.draw(to)
}
}
impl_as_ref_opt!(Track: |self: Project| self.selected_track());
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_mut_opt!(Track: |self: App| self.project.as_mut_opt());
/// Represents the current user selection in the arranger
#[derive(PartialEq, Clone, Copy, Debug, Default)]
pub enum Selection {
#[default]
/// Nothing is selected
Nothing,
/// The whole mix is selected
Mix,
/// A MIDI input is selected.
Input(usize),
/// A MIDI output is selected.
Output(usize),
/// A clip is selected.
Clip(usize),
/// A sample is selected.
Sample(usize),
/// A scene is selected.
#[cfg(feature = "scene")] Scene(usize),
/// A track is selected.
#[cfg(feature = "track")] Track(usize),
/// A clip slot (track × scene) is selected.
#[cfg(feature = "track")] TrackClip { track: usize, scene: usize },
/// A track's MIDI input connection is selected.
#[cfg(feature = "track")] TrackInput { track: usize, port: usize },
/// A track's MIDI output connection is selected.
#[cfg(feature = "track")] TrackOutput { track: usize, port: usize },
/// A track device slot is selected.
#[cfg(feature = "track")] TrackDevice { track: usize, device: usize },
}
impl Selection {
pub fn describe (
&self,
#[cfg(feature = "track")] tracks: &[Track],
#[cfg(feature = "scene")] scenes: &[Scene],
) -> Arc<str> {
use Selection::*;
format!("{}", match self {
Mix => "Everything".to_string(),
#[cfg(feature = "scene")] Scene(s) =>
scenes.get(*s).map(|scene|format!("S{s}: {}", &scene.name)).unwrap_or_else(||"S??".into()),
#[cfg(feature = "track")] Track(t) =>
tracks.get(*t).map(|track|format!("T{t}: {}", &track.name)).unwrap_or_else(||"T??".into()),
TrackClip { track, scene } => match (tracks.get(*track), scenes.get(*scene)) {
(Some(_), Some(s)) => match &s.clips.get(*track) {
None => format!("T{track} S{scene}: Empty"),
Some(clip) => format!("T{track} S{scene} C{}",
clip.as_ref().map(|clip|clip.read().name.clone()).unwrap_or_default()),
},
_ => format!("T{track} S{scene}: Empty"),
},
_ => todo!()
}).into()
}
}
#[cfg(feature = "scene")]
impl Selection {
pub fn scene (&self) -> Option<usize> {
use Selection::*;
match self { Scene(scene) | TrackClip { scene, .. } => Some(*scene), _ => None }
}
pub fn select_scene (&self, scene_count: usize) -> Self {
use Selection::*;
match self {
Mix | Track(_) => Scene(0),
Scene(s) => Scene((s + 1) % scene_count),
TrackClip { scene, .. } => Track(*scene),
_ => todo!(),
}
}
pub fn select_scene_next (&self, len: usize) -> Self {
use Selection::*;
match self {
Mix => Scene(0),
Track(t) => TrackClip { track: *t, scene: 0 },
Scene(s) => if s + 1 < len { Scene(s + 1) } else { Mix },
TrackClip { track, scene } => if scene + 1 < len { TrackClip { track: *track, scene: scene + 1 } } else { Track(*track) },
_ => todo!()
}
}
pub fn select_scene_prev (&self) -> Self {
use Selection::*;
match self {
Mix | Scene(0) => Mix,
Scene(s) => Scene(s - 1),
Track(t) => Track(*t),
TrackClip { track, scene: 0 } => Track(*track),
TrackClip { track, scene } => TrackClip { track: *track, scene: scene - 1 },
_ => todo!()
}
}
}
#[cfg(feature = "track")]
impl Selection {
pub fn track (&self) -> Option<usize> {
use Selection::*;
if let Track(track)|TrackClip{track,..}|TrackInput{track,..}|TrackOutput{track,..}|TrackDevice{track,..} = self {
Some(*track)
} else {
None
}
}
pub fn select_track (&self, track_count: usize) -> Self {
use Selection::*;
match self {
Mix => Track(0),
Scene(_) => Mix,
Track(t) => Track((t + 1) % track_count),
TrackClip { track, .. } => Track(*track),
_ => todo!(),
}
}
pub fn select_track_next (&self, len: usize) -> Self {
use Selection::*;
match self {
Mix => Track(0),
Scene(s) => TrackClip { track: 0, scene: *s },
Track(t) => if t + 1 < len { Track(t + 1) } else { Mix },
TrackClip {track, scene} => if track + 1 < len { TrackClip { track: track + 1, scene: *scene } } else { Scene(*scene) },
_ => todo!()
}
}
pub fn select_track_prev (&self) -> Self {
use Selection::*;
match self {
Mix => Mix,
Scene(s) => Scene(*s),
Track(0) => Mix,
Track(t) => Track(t - 1),
TrackClip { track: 0, scene } => Scene(*scene),
TrackClip { track: t, scene } => TrackClip { track: t - 1, scene: *scene },
_ => todo!()
}
}
}
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()
}
}
//impl_from!(Pool<MidiClip>: |clip:&Arc<RwLock<MidiClip>>|{
//let model = Self::default();
//model.clips.try_write().unwrap().push(clip.clone());