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

@ -115,12 +115,8 @@ impl MidiEditor {
fn note_range_prev (&self) -> usize {
self.get_note_axis().saturating_sub(1)
}
fn time_zoom (&self) -> usize {
self.get_time_zoom()
}
fn time_zoom_next (&self) -> usize {
self.get_time_zoom() + 1
}
fn time_zoom (&self) -> usize { self.get_time_zoom() }
fn time_zoom_next (&self) -> usize { self.get_time_zoom() + 1 }
fn time_zoom_next_fine (&self) -> usize { self.get_time_zoom() + 1 }
fn time_zoom_prev (&self) -> usize { self.get_time_zoom().saturating_sub(1).max(1) }
fn time_zoom_prev_fine (&self) -> usize { self.get_time_zoom().saturating_sub(1).max(1) }
@ -140,28 +136,16 @@ impl MidiEditor {
}
}
/// ```
/// use tek::{*, tengri::*};
///
/// struct Test(Option<MidiEditor>);
/// impl_as_ref!(Option<MidiEditor>: |self: Test|&self.0);
/// impl_as_mut!(Option<MidiEditor>: |self: Test|&mut self.0);
///
/// let mut host = Test(Some(MidiEditor::default()));
/// let _ = host.editor();
/// let _ = host.editor_mut();
/// let _ = host.is_editing();
/// let _ = host.editor_w();
/// let _ = host.editor_h();
/// ```
impl <T: NotePoint+TimePoint> MidiPoint for T {}
impl <T: TimeRange+NoteRange> MidiRange for T {}
pub trait MidiViewer: MidiRange + MidiPoint + Debug + Send + Sync {
fn buffer_size (&self, clip: &MidiClip) -> (usize, usize);
fn redraw (&self);
/// Make sure cursor is within note range
fn autoscroll (&self) {
let note_pos = self.get_note_pos().min(127);
@ -173,6 +157,7 @@ pub trait MidiViewer: MidiRange + MidiPoint + Debug + Send + Sync {
self.note_lo().store((note_lo + note_pos).saturating_sub(note_hi), Relaxed);
}
}
/// Make sure time range is within display
fn autozoom (&self) {
if self.time_lock().load(Relaxed) {
@ -216,6 +201,7 @@ pub trait MidiViewer: MidiRange + MidiPoint + Debug + Send + Sync {
//}
//self.time_zoom().set(time_zoom);
}
}
impl_has!(Sizer: |self: MidiEditor| self.size);
@ -244,59 +230,173 @@ impl MidiViewer for MidiEditor {
}
}
impl App {
/// Toggle MIDI editor.
///
/// ```
/// tek::App::default().toggle_editor(None);
/// ```
pub fn toggle_editor (&mut self, value: Option<bool>) {
//FIXME: self.editing.store(value.unwrap_or_else(||!self.is_editing()), Relaxed);
if value.unwrap_or_else(||!self.editor().is_some()) {
self.toggle_editor_on()
} else {
self.toggle_editor_off()
}
}
/// 12 piano keys, some highlighted.
///
/// ```
/// let keys = tek::OctaveVertical::default();
/// ```
#[derive(Copy, Clone)]
pub struct OctaveVertical {
pub on: [bool; 12],
pub colors: [Color; 3]
}
fn toggle_editor_on (&mut self) {
// Create new clip in pool when entering empty cell
if let Selection::TrackClip { track: track_index, scene } = *self.selection()
&& let Some(scene) = self.project.scenes.get_mut(scene)
&& scene.clips.get(track_index).is_none()
&& let Some(track) = self.project.tracks.get_mut(track_index)
{
let color = track.color.base.mix(scene.color.base, 0.5);
let color = ItemColor::random_near(color, 0.2).into();
let clip: MidiClip = MidiClip { color, ..Default::default() };
let clip = self.project.clips.append(Some(clip));
// autocolor: new clip colors from scene and track color
if let Some(editor) = &mut self.project.editor {
*editor.mode.clip.write() = clip.0.read().clone();
editor.mode.color = clip.0.read().as_ref().map(|p|p.read().color)
.unwrap_or(ItemTheme::G[64]);
}
*scene.clips.slot(track_index).write() = clip.0.read().clone();
//Some(clip)
} else {
//None
}
}
fn toggle_editor_off (&mut self) {
if let Selection::TrackClip { track, scene } = *self.selection()
&& let Some(scene) = self.project.scenes.get_mut(scene)
{
// Remove clip from arrangement when exiting empty clip editor
let mut swapped = None;
if let Some(count) = scene.clips.slot(track).read().as_ref().map(|c|c.read().count_midi_messages())
&& count == 0
{
std::mem::swap(&mut swapped, &mut scene.clips.slot(track).write());
}
if let Some(clip) = swapped {
self.project.clips.remove(&*clip.read());
}
}
impl OctaveVertical {
pub fn color (&self, pitch: usize) -> Color {
let pitch = pitch % 12;
self.colors[if self.on[pitch] { 2 } else { match pitch { 0 | 2 | 4 | 5 | 6 | 8 | 10 => 0, _ => 1 } }]
}
}
impl_default!(OctaveVertical: Self {
on: [false; 12], colors: [Rgb(255,255,255), Rgb(0,0,0), Rgb(255,0,0)]
});
/// A clip, rendered as a horizontal piano roll.
///
/// Uses full blocks on note on and half blocks on legato: █▄ █▄ █▄
///
/// ```
/// let piano = tek::PianoHorizontal::default();
/// ```
#[derive(Clone, Default)] pub struct PianoHorizontal {
pub clip: ClipSlot,
/// Buffer where the whole clip is rerendered on change
pub buffer: Arc<RwLock<BigBuffer>>,
/// Size of actual notes area
pub size: Sizer,
/// The display window
pub range: MidiSelection,
/// The note cursor
pub point: MidiCursor,
/// The highlight color palette
pub color: ItemTheme,
/// Width of the keyboard
pub keys_width: u16,
}
impl_has!(Sizer: |self: PianoHorizontal| self.size);
impl PianoHorizontal {
pub fn new () -> Self {
let size = Sizer::default();
let mut range = MidiSelection::from((12, true));
range.time_axis = size.0.clone();
range.note_axis = size.1.clone();
let piano = Self { keys_width: 5, size, range, ..Default::default() };
piano.redraw();
piano
}
}
impl TimeRange for PianoHorizontal {
fn time_len (&self) -> &AtomicUsize { self.range.time_len() }
fn time_zoom (&self) -> &AtomicUsize { self.range.time_zoom() }
fn time_lock (&self) -> &AtomicBool { self.range.time_lock() }
fn time_start (&self) -> &AtomicUsize { self.range.time_start() }
fn time_axis (&self) -> &AtomicUsize { self.range.time_axis() }
}
impl NoteRange for PianoHorizontal {
fn note_lo (&self) -> &AtomicUsize { self.range.note_lo() }
fn note_axis (&self) -> &AtomicUsize { self.range.note_axis() }
}
impl NotePoint for PianoHorizontal {
fn note_len (&self) -> &AtomicUsize { self.point.note_len() }
fn note_pos (&self) -> &AtomicUsize { self.point.note_pos() }
}
impl TimePoint for PianoHorizontal {
fn time_pos (&self) -> &AtomicUsize { self.point.time_pos() }
}
impl MidiViewer for PianoHorizontal {
/// Determine the required space to render the clip.
fn buffer_size (&self, clip: &MidiClip) -> (usize, usize) {
let zoom = self.range.time_zoom().load(Relaxed);
let size = if zoom > 0 { clip.length / zoom } else { 0 };
(size, 128)
}
/// Draw the piano roll into the buffer.
fn redraw (&self) {
*self.buffer.write() = self.clip.map_inner(|clip|{
let buf_size = self.buffer_size(&clip);
let mut buffer = BigBuffer::from(buf_size);
let time_zoom = self.get_time_zoom().max(1);
self.time_len().store(clip.length, Relaxed);
let note_len = self.get_note_len();
let note_pos = self.get_note_pos();
let time_pos = self.get_time_pos();
draw_piano_fg(&mut buffer, &clip, time_zoom);
buffer
}).unwrap_or_default()
}
}
impl std::fmt::Debug for PianoHorizontal {
fn fmt (&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
let buffer = self.buffer.read();
f.debug_struct("PianoHorizontal")
.field("time_zoom", &self.range.time_zoom)
.field("buffer", &format!("{}x{}", buffer.width, buffer.height))
.finish()
}
}
//impl App {
///// Toggle MIDI editor.
/////
///// ```
///// tek::App::default().toggle_editor(None);
///// ```
//pub fn toggle_editor (&mut self, value: Option<bool>) {
////FIXME: self.editing.store(value.unwrap_or_else(||!self.is_editing()), Relaxed);
//if value.unwrap_or_else(||!self.editor().is_some()) {
//self.toggle_editor_on()
//} else {
//self.toggle_editor_off()
//}
//}
//fn toggle_editor_on (&mut self) {
//// Create new clip in pool when entering empty cell
//if let Selection::TrackClip { track: track_index, scene } = *self.selection()
//&& let Some(scene) = self.project.scenes.get_mut(scene)
//&& scene.clips.get(track_index).is_none()
//&& let Some(track) = self.project.tracks.get_mut(track_index)
//{
//let color = track.color.base.mix(scene.color.base, 0.5);
//let color = ItemColor::random_near(color, 0.2).into();
//let clip: MidiClip = MidiClip { color, ..Default::default() };
//let clip = self.project.clips.append(Some(clip));
//// autocolor: new clip colors from scene and track color
//if let Some(editor) = &mut self.project.editor {
//*editor.mode.clip.write() = clip.0.read().clone();
//editor.mode.color = clip.0.read().as_ref().map(|p|p.read().color)
//.unwrap_or(ItemTheme::G[64]);
//}
//*scene.clips.slot(track_index).write() = clip.0.read().clone();
////Some(clip)
//} else {
////None
//}
//}
//fn toggle_editor_off (&mut self) {
//if let Selection::TrackClip { track, scene } = *self.selection()
//&& let Some(scene) = self.project.scenes.get_mut(scene)
//{
//// Remove clip from arrangement when exiting empty clip editor
//let mut swapped = None;
//if let Some(count) = scene.clips.slot(track).read().as_ref().map(|c|c.read().count_midi_messages())
//&& count == 0
//{
//std::mem::swap(&mut swapped, &mut scene.clips.slot(track).write());
//}
//if let Some(clip) = swapped {
//self.project.clips.remove(&*clip.read());
//}
//}
//}
//}