but how to pass arbitrary chars to the config

This commit is contained in:
🪞👃🪞 2025-01-12 02:23:39 +01:00
parent f485a068a8
commit 19ed6a24b8
11 changed files with 627 additions and 644 deletions

View file

@ -1,7 +1,4 @@
use crate::*;
use KeyCode::{Char, Up, Down, Left, Right, Enter};
use MidiEditCommand::*;
pub trait HasEditor {
fn editor (&self) -> &MidiEditor;
}
@ -18,17 +15,13 @@ pub struct MidiEditor {
pub size: Measure<TuiOut>,
pub keymap: EdnKeymap,
}
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 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 {
@ -41,41 +34,10 @@ impl Default for MidiEditor {
}
}
}
has_size!(<TuiOut>|self: MidiEditor|&self.size);
render!(TuiOut: (self: MidiEditor) => {
self.autoscroll();
//self.autozoom();
self.size.of(&self.mode)
});
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) { self.mode.set_note_len(x) }
fn note_point (&self) -> usize { self.mode.note_point() }
fn set_note_point (&self, x: usize) { self.mode.set_note_point(x) }
}
impl TimePoint for MidiEditor {
fn time_point (&self) -> usize { self.mode.time_point() }
fn set_time_point (&self, x: usize) { self.mode.set_time_point(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) }
}
impl MidiEditor {
pub fn clip_length (&self) -> usize {
self.clip().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1)
}
/// Put note at current position
pub fn put_note (&mut self, advance: bool) {
let mut redraw = false;
@ -106,120 +68,43 @@ impl MidiEditor {
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 {
(ItemPalette::from(TuiTheme::g(64)), String::new().into(), 0, false)
};
row!(
FieldV(color, "Edit", format!("{name} ({length})")),
FieldV(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 {
(ItemPalette::from(TuiTheme::g(64)), 0)
};
let time_point = self.time_point();
let time_zoom = self.time_zoom().get();
let time_lock = if self.time_lock().get() { "[lock]" } else { " " };
let note_point = format!("{:>3}", self.note_point());
let note_name = format!("{:4}", Note::pitch_to_name(self.note_point()));
let note_len = format!("{:>4}", self.note_len());
Bsp::e(
FieldV(color, "Time", format!("{length}/{time_zoom}+{time_point} {time_lock}")),
FieldV(color, "Note", format!("{note_name} {note_point} {note_len}")),
)
}
}
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()
}
}
#[derive(Clone, Debug)]
pub enum MidiEditCommand {
// TODO: 1-9 seek markers that by default start every 8th of the clip
AppendNote,
PutNote,
SetNoteCursor(usize),
SetNoteLength(usize),
SetNoteScroll(usize),
SetTimeCursor(usize),
SetTimeScroll(usize),
SetTimeZoom(usize),
SetTimeLock(bool),
Show(Option<Arc<RwLock<MidiClip>>>),
}
handle!(TuiIn: |self: MidiEditor, input|MidiEditCommand::execute_with_state(self, input.event()));
keymap!(KEYS_MIDI_EDITOR = |s: MidiEditor, _input: Event| MidiEditCommand {
key(Up) => SetNoteCursor(s.note_point() + 1),
key(Char('w')) => SetNoteCursor(s.note_point() + 1),
key(Down) => SetNoteCursor(s.note_point().saturating_sub(1)),
key(Char('s')) => SetNoteCursor(s.note_point().saturating_sub(1)),
key(Left) => SetTimeCursor(s.time_point().saturating_sub(s.note_len())),
key(Char('a')) => SetTimeCursor(s.time_point().saturating_sub(s.note_len())),
key(Right) => SetTimeCursor((s.time_point() + s.note_len()) % s.clip_length()),
ctrl(alt(key(Up))) => SetNoteScroll(s.note_point() + 3),
ctrl(alt(key(Down))) => SetNoteScroll(s.note_point().saturating_sub(3)),
ctrl(alt(key(Left))) => SetTimeScroll(s.time_point().saturating_sub(s.time_zoom().get())),
ctrl(alt(key(Right))) => SetTimeScroll((s.time_point() + s.time_zoom().get()) % s.clip_length()),
ctrl(key(Up)) => SetNoteScroll(s.note_lo().get() + 1),
ctrl(key(Down)) => SetNoteScroll(s.note_lo().get().saturating_sub(1)),
ctrl(key(Left)) => SetTimeScroll(s.time_start().get().saturating_sub(s.note_len())),
ctrl(key(Right)) => SetTimeScroll(s.time_start().get() + s.note_len()),
alt(key(Up)) => SetNoteCursor(s.note_point() + 3),
alt(key(Down)) => SetNoteCursor(s.note_point().saturating_sub(3)),
alt(key(Left)) => SetTimeCursor(s.time_point().saturating_sub(s.time_zoom().get())),
alt(key(Right)) => SetTimeCursor((s.time_point() + s.time_zoom().get()) % s.clip_length()),
key(Char('d')) => SetTimeCursor((s.time_point() + s.note_len()) % s.clip_length()),
key(Char('z')) => SetTimeLock(!s.time_lock().get()),
key(Char('-')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::next(s.time_zoom().get()) }),
key(Char('_')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::next(s.time_zoom().get()) }),
key(Char('=')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::prev(s.time_zoom().get()) }),
key(Char('+')) => SetTimeZoom(if s.time_lock().get() { s.time_zoom().get() } else { NoteDuration::prev(s.time_zoom().get()) }),
key(Enter) => PutNote,
ctrl(key(Enter)) => AppendNote,
key(Char(',')) => SetNoteLength(NoteDuration::prev(s.note_len())),
key(Char('.')) => SetNoteLength(NoteDuration::next(s.note_len())),
key(Char('<')) => SetNoteLength(NoteDuration::prev(s.note_len())),
key(Char('>')) => SetNoteLength(NoteDuration::next(s.note_len())),
//// TODO: kpat!(Char('/')) => // toggle 3plet
//// TODO: kpat!(Char('?')) => // toggle dotted
from!(|clip: &Arc<RwLock<MidiClip>>|MidiEditor = {
let model = Self::from(Some(clip.clone()));
model.redraw();
model
});
impl MidiEditor {
fn clip_length (&self) -> usize {
self.clip().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1)
}
from!(|clip: Option<Arc<RwLock<MidiClip>>>|MidiEditor = {
let mut model = Self::default();
*model.clip_mut() = clip;
model.redraw();
model
});
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 Command<MidiEditor> for MidiEditCommand {
fn execute (self, state: &mut MidiEditor) -> Perhaps<Self> {
use MidiEditCommand::*;
match self {
Show(clip) => { state.set_clip(clip.as_ref()); },
PutNote => { state.put_note(false); },
AppendNote => { state.put_note(true); },
SetTimeZoom(x) => { state.time_zoom().set(x); state.redraw(); },
SetTimeLock(x) => { state.time_lock().set(x); },
SetTimeScroll(x) => { state.time_start().set(x); },
SetNoteScroll(x) => { state.note_lo().set(x.min(127)); },
SetNoteLength(x) => { state.set_note_len(x); },
SetTimeCursor(x) => { state.set_time_point(x); },
SetNoteCursor(note) => { state.set_note_point(note.min(127)); },
_ => todo!("{:?}", self)
}
Ok(None)
}
impl NoteRange for MidiEditor {
fn note_lo (&self) -> &AtomicUsize { self.mode.note_lo() }
fn note_axis (&self) -> &AtomicUsize { self.mode.note_axis() }
}
impl EdnCommand<MidiEditor> for MidiEditCommand {
fn from_edn <'a> (state: &MidiEditor, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
todo!()
}
impl NotePoint for MidiEditor {
fn note_len (&self) -> usize { self.mode.note_len() }
fn set_note_len (&self, x: usize) { self.mode.set_note_len(x) }
fn note_point (&self) -> usize { self.mode.note_point() }
fn set_note_point (&self, x: usize) { self.mode.set_note_point(x) }
}
impl TimePoint for MidiEditor {
fn time_point (&self) -> usize { self.mode.time_point() }
fn set_time_point (&self, x: usize) { self.mode.set_time_point(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) }
}