mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-09 05:06:43 +01:00
modal -> dialog; extract dialog, selection, editor
This commit is contained in:
parent
6286d69824
commit
a3beab0f36
7 changed files with 391 additions and 377 deletions
170
crates/app/src/model/editor.rs
Normal file
170
crates/app/src/model/editor.rs
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
use crate::*;
|
||||
|
||||
/// Contains state for viewing and editing a clip
|
||||
pub struct MidiEditor {
|
||||
/// Size of editor on screen
|
||||
pub size: Measure<TuiOut>,
|
||||
/// View mode and state of editor
|
||||
pub mode: PianoHorizontal,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for MidiEditor {
|
||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
f.debug_struct("MidiEditor")
|
||||
.field("mode", &self.mode)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MidiEditor {
|
||||
fn default () -> Self {
|
||||
Self {
|
||||
size: Measure::new(),
|
||||
mode: PianoHorizontal::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
has_size!(<TuiOut>|self: MidiEditor|&self.size);
|
||||
|
||||
content!(TuiOut: |self: MidiEditor| {
|
||||
self.autoscroll();
|
||||
//self.autozoom();
|
||||
self.size.of(&self.mode)
|
||||
});
|
||||
|
||||
from!(|clip: &Arc<RwLock<MidiClip>>|MidiEditor = {
|
||||
let model = Self::from(Some(clip.clone()));
|
||||
model.redraw();
|
||||
model
|
||||
});
|
||||
|
||||
from!(|clip: Option<Arc<RwLock<MidiClip>>>|MidiEditor = {
|
||||
let mut model = Self::default();
|
||||
*model.clip_mut() = clip;
|
||||
model.redraw();
|
||||
model
|
||||
});
|
||||
|
||||
impl MidiEditor {
|
||||
/// Put note at current position
|
||||
pub fn put_note (&mut self, advance: bool) {
|
||||
let mut redraw = false;
|
||||
if let Some(clip) = self.clip() {
|
||||
let mut clip = clip.write().unwrap();
|
||||
let note_start = self.time_pos();
|
||||
let note_pos = self.note_pos();
|
||||
let note_len = self.note_len();
|
||||
let note_end = note_start + (note_len.saturating_sub(1));
|
||||
let key: u7 = u7::from(note_pos as u8);
|
||||
let vel: u7 = 100.into();
|
||||
let length = clip.length;
|
||||
let note_end = note_end % length;
|
||||
let note_on = MidiMessage::NoteOn { key, vel };
|
||||
if !clip.notes[note_start].iter().any(|msg|*msg == note_on) {
|
||||
clip.notes[note_start].push(note_on);
|
||||
}
|
||||
let note_off = MidiMessage::NoteOff { key, vel };
|
||||
if !clip.notes[note_end].iter().any(|msg|*msg == note_off) {
|
||||
clip.notes[note_end].push(note_off);
|
||||
}
|
||||
if advance {
|
||||
self.set_time_pos(note_end);
|
||||
}
|
||||
redraw = true;
|
||||
}
|
||||
if redraw {
|
||||
self.mode.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clip_status (&self) -> impl Content<TuiOut> + '_ {
|
||||
let (color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
|
||||
(clip.color, clip.name.clone(), clip.length, clip.looped)
|
||||
} else { (ItemTheme::G[64], String::new().into(), 0, false) };
|
||||
Bsp::e(
|
||||
FieldH(color, "Edit", format!("{name} ({length})")),
|
||||
FieldH(color, "Loop", looped.to_string())
|
||||
)
|
||||
}
|
||||
|
||||
pub fn edit_status (&self) -> impl Content<TuiOut> + '_ {
|
||||
let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
|
||||
(clip.color, clip.length)
|
||||
} else { (ItemTheme::G[64], 0) };
|
||||
let time_pos = self.time_pos();
|
||||
let time_zoom = self.time_zoom().get();
|
||||
let time_lock = if self.time_lock().get() { "[lock]" } else { " " };
|
||||
let note_pos = format!("{:>3}", self.note_pos());
|
||||
let note_name = format!("{:4}", Note::pitch_to_name(self.note_pos()));
|
||||
let note_len = format!("{:>4}", self.note_len());
|
||||
Bsp::e(
|
||||
FieldH(color, "Time", format!("{length}/{time_zoom}+{time_pos} {time_lock}")),
|
||||
FieldH(color, "Note", format!("{note_name} {note_pos} {note_len}")),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeRange for MidiEditor {
|
||||
fn time_len (&self) -> &AtomicUsize { self.mode.time_len() }
|
||||
fn time_zoom (&self) -> &AtomicUsize { self.mode.time_zoom() }
|
||||
fn time_lock (&self) -> &AtomicBool { self.mode.time_lock() }
|
||||
fn time_start (&self) -> &AtomicUsize { self.mode.time_start() }
|
||||
fn time_axis (&self) -> &AtomicUsize { self.mode.time_axis() }
|
||||
}
|
||||
|
||||
impl NoteRange for MidiEditor {
|
||||
fn note_lo (&self) -> &AtomicUsize { self.mode.note_lo() }
|
||||
fn note_axis (&self) -> &AtomicUsize { self.mode.note_axis() }
|
||||
}
|
||||
|
||||
impl NotePoint for MidiEditor {
|
||||
fn note_len (&self) -> usize { self.mode.note_len() }
|
||||
fn set_note_len (&self, x: usize) -> usize { self.mode.set_note_len(x) }
|
||||
fn note_pos (&self) -> usize { self.mode.note_pos() }
|
||||
fn set_note_pos (&self, x: usize) -> usize { self.mode.set_note_pos(x) }
|
||||
}
|
||||
|
||||
impl TimePoint for MidiEditor {
|
||||
fn time_pos (&self) -> usize { self.mode.time_pos() }
|
||||
fn set_time_pos (&self, x: usize) -> usize { self.mode.set_time_pos(x) }
|
||||
}
|
||||
|
||||
impl MidiViewer for MidiEditor {
|
||||
fn buffer_size (&self, clip: &MidiClip) -> (usize, usize) { self.mode.buffer_size(clip) }
|
||||
fn redraw (&self) { self.mode.redraw() }
|
||||
fn clip (&self) -> &Option<Arc<RwLock<MidiClip>>> { self.mode.clip() }
|
||||
fn clip_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> { self.mode.clip_mut() }
|
||||
fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) }
|
||||
}
|
||||
|
||||
pub trait HasEditor {
|
||||
fn editor (&self) -> &Option<MidiEditor>;
|
||||
fn editor_mut (&mut self) -> &Option<MidiEditor>;
|
||||
fn is_editing (&self) -> bool { true }
|
||||
fn editor_w (&self) -> usize { 0 }
|
||||
fn editor_h (&self) -> usize { 0 }
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! has_editor {
|
||||
(|$self:ident: $Struct:ident|{
|
||||
editor = $e0:expr;
|
||||
editor_w = $e1:expr;
|
||||
editor_h = $e2:expr;
|
||||
is_editing = $e3:expr;
|
||||
}) => {
|
||||
impl HasEditor for $Struct {
|
||||
fn editor (&$self) -> &Option<MidiEditor> { &$e0 }
|
||||
fn editor_mut (&mut $self) -> &Option<MidiEditor> { &mut $e0 }
|
||||
fn editor_w (&$self) -> usize { $e1 }
|
||||
fn editor_h (&$self) -> usize { $e2 }
|
||||
fn is_editing (&$self) -> bool { $e3 }
|
||||
}
|
||||
};
|
||||
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
||||
impl $(<$($L),*$($T $(: $U)?),*>)? HasEditor for $Struct $(<$($L),*$($T),*>)? {
|
||||
fn editor (&$self) -> &MidiEditor { &$cb }
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue