add HitBrushDrag, to allow brushing notes on the grid

This commit is contained in:
Paul Davis 2025-10-31 13:24:21 -06:00
parent dcb482e74d
commit 88bf459a61
4 changed files with 152 additions and 3 deletions

View file

@ -443,7 +443,7 @@ Drag::end_grab (GdkEvent* event)
_item->ungrab (); _item->ungrab ();
finished (event, _starting_point_passed); finished (event, _starting_point_passed);
editing_context.verbose_cursor().hide (); editing_context.verbose_cursor().hide ();
return _starting_point_passed; return _starting_point_passed;
@ -6903,7 +6903,6 @@ NoteCreateDrag::aborted (bool)
HitCreateDrag::HitCreateDrag (EditingContext& ec, ArdourCanvas::Item* i, MidiView* mv) HitCreateDrag::HitCreateDrag (EditingContext& ec, ArdourCanvas::Item* i, MidiView* mv)
: Drag (ec, i, Temporal::BeatTime, ec.get_trackview_group()) : Drag (ec, i, Temporal::BeatTime, ec.get_trackview_group())
, _midi_view (mv) , _midi_view (mv)
, _last_pos (Temporal::Beats ())
, _y (0) , _y (0)
{ {
} }
@ -6962,6 +6961,120 @@ HitCreateDrag::y_to_region (double y) const
return y; return y;
} }
/*-----------------------*/
HitBrushDrag::HitBrushDrag (EditingContext& ec, ArdourCanvas::Item* i, MidiView* mv)
: Drag (ec, i, Temporal::BeatTime, ec.get_trackview_group())
, _midi_view (mv)
, _last_pos (Temporal::Beats ())
, _y (0)
, added_notes (false)
{
}
HitBrushDrag::~HitBrushDrag ()
{
}
void
HitBrushDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
{
Drag::start_grab (event, cursor);
_y = _midi_view->note_to_y (_midi_view->y_to_note (y_to_region (event->button.y)));
timepos_t pos (_drags->current_pointer_time ());
assert (Config->get_default_quantization().type == AnyTime::BBT_Offset);
stride = get_stride (pos.beats(), Config->get_default_quantization().bbt_offset);
Temporal::Beats dl = _midi_view->get_draw_length_beats (pos);
if (dl > stride) {
stride = dl;
}
editing_context.snap_to (pos, RoundNearest, SnapToGrid_Scaled);
next_grid = pos.beats ();
}
Temporal::Beats
HitBrushDrag::get_stride (Temporal::Beats const & pos, Temporal::BBT_Offset const & q)
{
if (q < Temporal::BBT_Offset (0, 0, 0)) {
/* negative quantization == do not quantize */
return Temporal::Beats ();
} else if (q.bars == 0) {
return Temporal::Beats (q.beats, q.ticks);
}
Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use());
return tmap->meter_at (pos).to_quarters (q); /* Quantization as beats */
}
void
HitBrushDrag::motion (GdkEvent* event, bool first_move)
{
Beats length;
if (last_pointer_x() >= current_pointer_x()) {
/* nothing to if we move earlier in time */
return;
}
if (first_move) {
_midi_view->start_note_diff_command (_("brush notes"), true);
}
const timepos_t pos = _drags->current_pointer_time ();
while (pos.beats() >= next_grid) {
if (!_midi_view->midi_region()) {
editing_context.make_a_region ();
assert (_midi_view->midi_region());
}
length = _midi_view->get_draw_length_beats (timepos_t (next_grid));
Beats start;
if (!_midi_view->on_timeline()) {
Beats spos = _midi_view->midi_region()->source_position().beats() + next_grid;
start = _midi_view->midi_region ()->absolute_time_to_source_beats (timepos_t (spos));
} else {
start = _midi_view->midi_region ()->absolute_time_to_source_beats (timepos_t (next_grid));
}
_midi_view->create_note_at (timepos_t (start), _y, length, event->button.state, false, false);
added_notes = true;
next_grid += stride;
}
}
void
HitBrushDrag::finished (GdkEvent* event, bool had_movement)
{
if (added_notes) {
_midi_view->end_note_diff_command ();
} else {
_midi_view->abort_note_diff ();
}
}
double
HitBrushDrag::y_to_region (double y) const
{
double x = 0;
_midi_view->drag_group ()->canvas_to_item (x, y);
return y;
}
/*-------------------*/
CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor& e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn) CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor& e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
: Drag (e, i, Temporal::AudioTime, e.get_trackview_group()) : Drag (e, i, Temporal::AudioTime, e.get_trackview_group())
, arv (rv) , arv (rv)

View file

@ -708,9 +708,40 @@ public:
private: private:
double y_to_region (double) const; double y_to_region (double) const;
MidiView* _midi_view;
int _y;
};
class HitBrushDrag : public Drag
{
public:
HitBrushDrag (EditingContext&, ArdourCanvas::Item *, MidiView *);
~HitBrushDrag ();
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool) {}
bool active (Editing::MouseMode mode) {
return mode == Editing::MouseDraw || mode == Editing::MouseContent;
}
bool y_movement_matters () const {
return false;
}
private:
double y_to_region (double) const;
Temporal::Beats get_stride (Temporal::Beats const & pos, Temporal::BBT_Offset const & quantization);
MidiView* _midi_view; MidiView* _midi_view;
Temporal::timepos_t _last_pos; Temporal::timepos_t _last_pos;
int _y; int _y;
Temporal::Beats stride;
Temporal::Beats next_grid;
bool added_notes;
}; };

View file

@ -568,7 +568,11 @@ MidiView::button_press (GdkEventButton* ev)
draw_drag = new HitCreateDrag (_editing_context, drag_group(), this); draw_drag = new HitCreateDrag (_editing_context, drag_group(), this);
_editing_context.drags()->set (draw_drag, (GdkEvent *) ev); _editing_context.drags()->set (draw_drag, (GdkEvent *) ev);
} else { } else {
draw_drag = new NoteCreateDrag (_editing_context, drag_group(), this); if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
draw_drag = new HitBrushDrag (_editing_context, drag_group(), this);
} else {
draw_drag = new NoteCreateDrag (_editing_context, drag_group(), this);
}
_editing_context.drags()->set (draw_drag, (GdkEvent *) ev); _editing_context.drags()->set (draw_drag, (GdkEvent *) ev);
} }

View file

@ -442,6 +442,7 @@ class MidiView : public virtual sigc::trackable, public LineMerger
friend class NoteDrag; friend class NoteDrag;
friend class NoteCreateDrag; friend class NoteCreateDrag;
friend class HitCreateDrag; friend class HitCreateDrag;
friend class HitBrushDrag;
friend class MidiGhostRegion; friend class MidiGhostRegion;
friend class EditNoteDialog; friend class EditNoteDialog;