diff --git a/Applications/Piano/RollWidget.cpp b/Applications/Piano/RollWidget.cpp index d8d0aa670e3d92..9d99f1b67c85a5 100644 --- a/Applications/Piano/RollWidget.cpp +++ b/Applications/Piano/RollWidget.cpp @@ -159,29 +159,58 @@ void RollWidget::mousedown_event(GUI::MouseEvent& event) if (!widget_inner_rect().contains(event.x(), event.y())) return; - int y = (event.y() + vertical_scrollbar().value()) - frame_thickness(); + m_note_drag_start = event.position(); + + int y = (m_note_drag_start.value().y() + vertical_scrollbar().value()) - frame_thickness(); y /= note_height; + m_drag_note = (note_count - 1) - y; + + mousemove_event(event); +} - // There's a case where we can't just use x / m_note_width. For example, if - // your m_note_width is 3.1 you will have a rect starting at 3. When that - // leftmost pixel of the rect is clicked you will do 3 / 3.1 which is 0 - // and not 1. We can avoid that case by shifting x by 1 if m_note_width is - // fractional, being careful not to shift out of bounds. - int x = (event.x() + horizontal_scrollbar().value()) - frame_thickness(); - bool note_width_is_fractional = m_note_width - static_cast(m_note_width) != 0; - bool x_is_not_last = x != widget_inner_rect().width() - 1; - if (note_width_is_fractional && x_is_not_last) - ++x; - x /= m_note_width; - - int note = (note_count - 1) - y; - u32 on_sample = roll_length * (static_cast(x) / m_num_notes); - u32 off_sample = (roll_length * (static_cast(x + 1) / m_num_notes)) - 1; - m_track_manager.current_track().set_roll_note(note, on_sample, off_sample); +void RollWidget::mousemove_event(GUI::MouseEvent& event) +{ + if (!m_note_drag_start.has_value()) + return; + + if (m_note_drag_location.has_value()) { + // Clear previous note + m_track_manager.current_track().set_roll_note(m_drag_note, m_note_drag_location.value().on_sample, m_note_drag_location.value().off_sample); + } + + auto get_note_x = [&](int x0) { + // There's a case where we can't just use x / m_note_width. For example, if + // your m_note_width is 3.1 you will have a rect starting at 3. When that + // leftmost pixel of the rect is clicked you will do 3 / 3.1 which is 0 + // and not 1. We can avoid that case by shifting x by 1 if m_note_width is + // fractional, being careful not to shift out of bounds. + int x = (x0 + horizontal_scrollbar().value()) - frame_thickness(); + bool note_width_is_fractional = m_note_width - static_cast(m_note_width) != 0; + bool x_is_not_last = x != widget_inner_rect().width() - 1; + if (note_width_is_fractional && x_is_not_last) + ++x; + x /= m_note_width; + return x; + }; + + int x0 = get_note_x(m_note_drag_start.value().x()); + int x1 = get_note_x(event.x()); + + u32 on_sample = roll_length * (static_cast(min(x0, x1)) / m_num_notes); + u32 off_sample = (roll_length * (static_cast(max(x0, x1) + 1) / m_num_notes)) - 1; + m_track_manager.current_track().set_roll_note(m_drag_note, on_sample, off_sample); + m_note_drag_location = RollNote({ on_sample, off_sample }); update(); } +void RollWidget::mouseup_event(GUI::MouseEvent& event) +{ + (void)event; + m_note_drag_start = {}; + m_note_drag_location = {}; +} + // FIXME: Implement zoom and horizontal scroll events in LibGUI, not here. void RollWidget::mousewheel_event(GUI::MouseEvent& event) { diff --git a/Applications/Piano/RollWidget.h b/Applications/Piano/RollWidget.h index 28910156cc39c9..bbfcc8bb6270ef 100644 --- a/Applications/Piano/RollWidget.h +++ b/Applications/Piano/RollWidget.h @@ -46,6 +46,8 @@ class RollWidget final : public GUI::ScrollableWidget { virtual void paint_event(GUI::PaintEvent&) override; virtual void mousedown_event(GUI::MouseEvent& event) override; + virtual void mousemove_event(GUI::MouseEvent& event) override; + virtual void mouseup_event(GUI::MouseEvent& event) override; virtual void mousewheel_event(GUI::MouseEvent&) override; TrackManager& m_track_manager; @@ -55,4 +57,8 @@ class RollWidget final : public GUI::ScrollableWidget { int m_num_notes { 0 }; double m_note_width { 0.0 }; int m_zoom_level { 1 }; + + Optional m_note_drag_start; + Optional m_note_drag_location; + int m_drag_note; }; diff --git a/Applications/Piano/Track.cpp b/Applications/Piano/Track.cpp index 22cbbe03f89447..754a43407aef26 100644 --- a/Applications/Piano/Track.cpp +++ b/Applications/Piano/Track.cpp @@ -287,7 +287,7 @@ void Track::set_roll_note(int note, u32 on_sample, u32 off_sample) sync_roll(note); return; } - if (it->on_sample == new_roll_note.on_sample && it->off_sample == new_roll_note.off_sample) { + if (it->on_sample <= new_roll_note.on_sample && it->off_sample >= new_roll_note.on_sample) { if (m_time >= it->on_sample && m_time <= it->off_sample) set_note(note, Off); m_roll_notes[note].remove(it); @@ -301,12 +301,6 @@ void Track::set_roll_note(int note, u32 on_sample, u32 off_sample) it = m_roll_notes[note].begin(); continue; } - if (it->on_sample < new_roll_note.on_sample && it->off_sample >= new_roll_note.on_sample) { - if (m_time >= new_roll_note.off_sample && m_time <= it->off_sample) - set_note(note, Off); - it->off_sample = new_roll_note.on_sample - 1; - ASSERT(it->length() >= 2); - } ++it; }