pianoroll: dragging start handle before zero shifts MIDI later in time

This commit is contained in:
Paul Davis
2025-04-08 15:48:09 -06:00
parent 259e878887
commit 0c924c3933
5 changed files with 153 additions and 13 deletions

View File

@@ -7564,13 +7564,34 @@ ClipStartDrag::motion (GdkEvent* event, bool first_move)
{
ArdourCanvas::Rect r (original_rect);
timepos_t pos (adjusted_current_time (event));
editing_context.snap_to_with_modifier (pos, event, Temporal::RoundNearest, ARDOUR::SnapToGrid_Scaled, true);
double pix = editing_context.timeline_to_canvas (editing_context.time_to_pixel (pos));
double x, y;
gdk_event_get_coords (event, &x, &y);
if (x >= editing_context.timeline_origin()) {
/* Compute snapped position and adjust rect item if appropriate */
timepos_t pos = adjusted_current_time (event);
editing_context.snap_to_with_modifier (pos, event, Temporal::RoundNearest, ARDOUR::SnapToGrid_Scaled, true);
double pix = editing_context.timeline_to_canvas (editing_context.time_to_pixel (pos));
if (pix >= editing_context.timeline_origin()) {
r.x1 = dragging_rect->parent()->canvas_to_item (Duple (pix, 0.0)).x;
}
if (pix > editing_context.timeline_origin()) {
r.x1 = dragging_rect->parent()->canvas_to_item (Duple (pix, 0.0)).x;
} else {
/* We need to do our own math here because the normal drag
* coordinates are clamped to zero (no negative values).
*/
x -= editing_context.timeline_origin();
timepos_t tp (mce.pixel_to_sample (x));
Beats b (tp.beats() * -1);
mce.shift_midi (timepos_t (b), false);
/* ensure the line is in the right place */
r.x1 = r.x0 + 1.;
}
@@ -7585,22 +7606,50 @@ ClipStartDrag::finished (GdkEvent* event, bool movement_occured)
return;
}
timepos_t pos = adjusted_current_time (event);
double x, y;
gdk_event_get_coords (event, &x, &y);
assert (mce.midi_view());
if (x >= editing_context.timeline_origin()) {
timepos_t pos = adjusted_current_time (event);
editing_context.snap_to_with_modifier (pos, event, Temporal::RoundNearest, ARDOUR::SnapToGrid_Scaled, true);
double pix = editing_context.timeline_to_canvas (editing_context.time_to_pixel (pos));
if (pix >= editing_context.timeline_origin()) {
assert (mce.midi_view());
if (mce.midi_view()->show_source()) {
pos = mce.midi_view()->source_beats_to_timeline (pos.beats());
}
editing_context.snap_to_with_modifier (pos, event, Temporal::RoundNearest, ARDOUR::SnapToGrid_Scaled, true);
mce.set_trigger_start (pos);
}
} else {
/* We need to do our own math here because the normal drag
* coordinates are clamped to zero (no negative values).
*/
x -= editing_context.timeline_origin();
timepos_t tp (mce.pixel_to_sample (x));
Beats b (tp.beats() * -1);
mce.shift_midi (timepos_t (b), true);
if (mce.midi_view()->show_source()) {
pos = mce.midi_view()->source_beats_to_timeline (pos.beats());
}
editing_context.snap_to_with_modifier (pos, event, Temporal::RoundNearest, ARDOUR::SnapToGrid_Scaled, true);
mce.set_trigger_start (pos);
}
void
ClipStartDrag::aborted (bool)
ClipStartDrag::aborted (bool movement_occured)
{
dragging_rect->set (original_rect);
if (movement_occured) {
/* redraw to get notes back to the right places */
mce.shift_midi (timepos_t (Temporal::Beats()), false);
}
}
ClipEndDrag::ClipEndDrag (EditingContext& ec, ArdourCanvas::Rectangle& r, Pianoroll& m)

View File

@@ -1260,6 +1260,9 @@ MidiView::model_changed()
_marked_for_selection.clear ();
_marked_for_velocity.clear ();
_pending_note_selection.clear ();
size_start_rect ();
size_end_rect ();
}
void
@@ -5270,6 +5273,81 @@ MidiView::add_split_notes ()
}
}
void
MidiView::shift_midi (timepos_t const & t, bool model)
{
/* INTENDED FOR USE IN PIANOROLL CONTEXT ONLY */
assert (_show_source);
Beats beats (t.beats());
if (model) {
/* Change the model */
std::string cmd = string_compose (_("Shift MIDI by %1"), beats.str());
XMLNode& before (_midi_region->get_state());
_editing_context.begin_reversible_command (cmd);
_model->insert_silence_at_start (beats, _editing_context.history());
XMLNode& after (_midi_region->get_state());
_editing_context.add_command (new MementoCommand<MidiRegion> (*(_midi_region.get()), &before, &after));
_editing_context.commit_reversible_command ();
} else {
/* Only change the view */
for (auto & [ note, gui ] : _events) {
Temporal::Beats note_time_qn;
double dx = 0.0;
if (_show_source) {
note_time_qn = note->time ();
} else {
note_time_qn = _midi_region->source_beats_to_absolute_beats (note->time());
}
if (_midi_context.note_mode() == Sustained) {
dx = _editing_context.time_to_pixel_unrounded (timepos_t (note_time_qn + beats));
/*: ::item_to_canvas() converts to a global canvas
* coordinate, but ::time_to_pixel() gives us a
* timeline-relative coordinate.
*
* So we need to adjust ...
*/
dx -= _editing_context.canvas_to_timeline (gui->item()->item_to_canvas (ArdourCanvas::Duple (gui->x0(), 0)).x);
} else {
/* Hit::x0() is offset by _position.x, unlike Note::x0() */
Hit* hit = dynamic_cast<Hit*>(gui);
if (hit) {
dx = _editing_context.time_to_pixel_unrounded (timepos_t (note_time_qn + beats));
dx -= _editing_context.canvas_to_timeline (gui->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x);
}
}
gui->move_event (dx, 0.0);
/* update length, which may have changed in pixels at the new location */
if (_midi_context.note_mode() == Sustained) {
Note* sus = dynamic_cast<Note*> (gui);
double len_dx = _editing_context.time_to_pixel_unrounded (timepos_t (note_time_qn) + t + timecnt_t (note->length()));
/* at this point, len_dx is a timeline-relative pixel
* duration. To convert it back to an item-centric
* coordinate, we need to first convert it to a global
* canvas position.
*/
len_dx = _editing_context.timeline_to_canvas (len_dx);
sus->set_x1 (gui->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
}
}
}
}
double
MidiView::height() const
{

View File

@@ -358,6 +358,8 @@ class MidiView : public virtual sigc::trackable, public LineMerger
void note_deleted (NoteBase*);
void clear_note_selection ();
void shift_midi (Temporal::timepos_t const &, bool model);
void show_verbose_cursor_for_new_note_value(std::shared_ptr<NoteType> current_note, uint8_t new_note) const;
std::shared_ptr<ARDOUR::MidiTrack> midi_track() const { return _midi_track; }

View File

@@ -2822,3 +2822,12 @@ Pianoroll::allow_trim_cursors () const
return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseTimeFX;
}
void
Pianoroll::shift_midi (timepos_t const & t, bool model)
{
if (!view) {
return;
}
view->shift_midi (t, model);
}

View File

@@ -142,6 +142,8 @@ class Pianoroll : public CueEditor
void set_session (ARDOUR::Session*);
bool allow_trim_cursors () const;
void shift_midi (Temporal::timepos_t const &, bool model);
protected:
void load_bindings ();
void register_actions ();