From 0b5710bb4cbc702bca1676ff6d7f324ca3f2c7c3 Mon Sep 17 00:00:00 2001 From: Petr Gladkikh Date: Fri, 8 Dec 2023 22:35:38 +0100 Subject: [PATCH] Edit commands animation Still to be done: undo/redo animation. --- src/app.rs | 19 ++- src/changeset.rs | 8 ++ src/main.rs | 2 +- src/project.rs | 5 +- src/stave.rs | 287 ++++++++++++++++++++++++++++--------------- src/track_edit.rs | 28 ++++- src/track_history.rs | 3 + 7 files changed, 233 insertions(+), 119 deletions(-) diff --git a/src/app.rs b/src/app.rs index 06a9da0..984f231 100644 --- a/src/app.rs +++ b/src/app.rs @@ -117,13 +117,10 @@ impl eframe::App for EmApp { if ui.input(|i| i.key_pressed(egui::Key::Space)) { self.toggle_pause(); } - ui.heading(format!( - "🌲 {} [{} / {}]", - self.stave.history.directory.display(), - self.stave.history.version(), - self.stave.track_version.to_string() - )); - + { + let h = self.stave.history.borrow(); + ui.heading(format!("🌲 {} [{}]", h.directory.display(), h.version())); + } StripBuilder::new(ui) .size(Size::remainder()) .size(Size::exact(20.0)) @@ -182,17 +179,19 @@ impl eframe::App for EmApp { self.export(); } if ui.button("⤵ Undo").clicked() { - self.stave.history.undo(&mut vec![]); // TODO (implement) changes highlighting + self.stave.history.borrow_mut().undo(&mut vec![]); + // TODO (implement) changes highlighting } if ui.button("⤴ Redo").clicked() { - self.stave.history.redo(&mut vec![]); // TODO (implement) changes highlighting + self.stave.history.borrow_mut().redo(&mut vec![]); + // TODO (implement) changes highlighting } }); ui.horizontal(|ui| { // Status line ui.label(format!( "track_len={} n_sel={} t_sel={} at={}s ", - self.stave.history.with_track(|t| t.events.len()), + self.stave.history.borrow().with_track(|t| t.events.len()), self.stave.note_selection.count(), self.stave.time_selection.as_ref().map_or( "()".to_string(), diff --git a/src/changeset.rs b/src/changeset.rs index 3d77e1d..b075bb3 100644 --- a/src/changeset.rs +++ b/src/changeset.rs @@ -29,6 +29,14 @@ impl EventAction { } } + pub fn before(&self) -> Option<&TrackEvent> { + match self { + EventAction::Delete(ev) => Some(ev), + EventAction::Update(ev, _) => Some(ev), + EventAction::Insert(_) => None, + } + } + pub fn after(&self) -> Option<&TrackEvent> { match self { EventAction::Delete(_) => None, diff --git a/src/main.rs b/src/main.rs index b291dd6..d051cf5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,7 +65,7 @@ pub fn main() { } { - let track_midi_source = TrackSource::new(project.history.track.clone()); + let track_midi_source = TrackSource::new(project.history.borrow().track.clone()); engine_command_sender .send(Box::new(|engine| engine.add(Box::new(track_midi_source)))) .unwrap(); diff --git a/src/project.rs b/src/project.rs index e3ca2bc..52b872e 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,10 +1,11 @@ +use std::cell::RefCell; use std::fs; use std::path::PathBuf; use crate::track_history::TrackHistory; pub struct Project { - pub history: TrackHistory, + pub history: RefCell, pub home_path: PathBuf, } @@ -37,7 +38,7 @@ impl Project { history.open(); Project { home_path: directory, - history, + history: RefCell::new(history), } } } diff --git a/src/stave.rs b/src/stave.rs index 423066d..4b64665 100644 --- a/src/stave.rs +++ b/src/stave.rs @@ -1,24 +1,27 @@ +use std::cell::RefCell; use std::collections::btree_set::Iter; use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::ops::Range; use std::path::PathBuf; use eframe::egui::{ - self, Color32, Frame, Margin, Modifiers, Painter, PointerButton, Pos2, Rangef, Rect, Rounding, - Sense, Stroke, Ui, + self, Color32, Context, Frame, Margin, Modifiers, Painter, PointerButton, Pos2, Rangef, Rect, + Rounding, Sense, Stroke, Ui, }; use egui::Rgba; use ordered_float::OrderedFloat; use serde::{Deserialize, Serialize}; use crate::changeset::Changeset; -use crate::common::{Time, VersionId}; +use crate::common::Time; use crate::track::{ - export_smf, EventId, Level, Note, Pitch, Track, TrackEvent, TrackEventType, MIDI_CC_SUSTAIN_ID, + export_smf, EventId, Level, Note, Pitch, Track, TrackEvent, TrackEventType, MAX_LEVEL, + MIDI_CC_SUSTAIN_ID, }; use crate::track_edit::{ accent_selected_notes, add_new_note, delete_selected, set_damper, shift_selected, shift_tail, - stretch_selected_notes, tape_delete, tape_insert, transpose_selected_notes, EditCommandId, + stretch_selected_notes, tape_delete, tape_insert, transpose_selected_notes, AppliedCommand, + EditCommandId, }; use crate::track_history::TrackHistory; use crate::{util, Pix}; @@ -131,10 +134,47 @@ impl Bookmarks { } } +#[derive(Debug)] +pub struct EditTransition { + pub animation_id: egui::Id, + pub command_id: EditCommandId, + pub changeset: Changeset, + pub coeff: f32, +} + +impl EditTransition { + pub fn start( + ctx: &Context, + animation_id: egui::Id, + command_id: EditCommandId, + changeset: Changeset, + ) -> Self { + let coeff = ctx.animate_bool(animation_id, false); + EditTransition { + animation_id, + command_id, + coeff, + changeset, + } + } + + pub fn update(mut self, ctx: &Context) -> Self { + self.coeff = ctx.animate_bool(self.animation_id, true); + self + } + + pub fn value(&self) -> Option { + if self.coeff >= 1.0 { + None + } else { + Some(self.coeff) + } + } +} + #[derive(Debug)] pub struct Stave { - pub history: TrackHistory, - pub track_version: VersionId, + pub history: RefCell, pub time_left: Time, pub time_right: Time, @@ -145,7 +185,7 @@ pub struct Stave { pub time_selection: Option>, pub note_draw: Option, pub note_selection: NotesSelection, - pub command_transition: Option<(u64, EditCommandId, Changeset)>, + pub transition: Option, } const COLOR_SELECTED: Rgba = Rgba::from_rgb(0.7, 0.1, 0.3); @@ -165,10 +205,9 @@ pub struct StaveResponse { } impl Stave { - pub fn new(history: TrackHistory, bookmarks: Bookmarks) -> Stave { + pub fn new(history: RefCell, bookmarks: Bookmarks) -> Stave { Stave { history, - track_version: 0, time_left: 0, time_right: chrono::Duration::minutes(5).num_microseconds().unwrap(), view_rect: Rect::NOTHING, @@ -177,12 +216,13 @@ impl Stave { time_selection: None, note_draw: None, note_selection: NotesSelection::default(), - command_transition: None, + transition: None, } } pub fn save_to(&mut self, file_path: &PathBuf) { self.history + .borrow() .with_track(|track| export_smf(&track.events, file_path)); } @@ -263,7 +303,8 @@ impl Stave { ); let mut note_hovered = None; { - let track = self.history.track.read().expect("Read track."); + let history = self.history.borrow(); + let track = history.track.read().expect("Read track."); self.draw_track( &key_ys, &half_tone_step, @@ -271,7 +312,6 @@ impl Stave { &mut time_hovered, &mut note_hovered, &painter, - &egui_response, &track, ); } @@ -291,7 +331,7 @@ impl Stave { } if let Some(new_note) = &self.note_draw { - self.draw_note( + self.default_draw_note( &painter, 64, (new_note.time.start, new_note.time.end), @@ -305,7 +345,7 @@ impl Stave { response: egui_response, pitch_hovered, time_hovered, - note_hovered, + note_hovered: note_hovered, modifiers: ui.input(|i| i.modifiers), } }) @@ -320,7 +360,6 @@ impl Stave { time_hovered: &Option