diff options
author | Sergey Bugaev <bugaevc@gmail.com> | 2020-01-22 21:12:05 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-01-22 21:22:23 +0100 |
commit | d3ce7ae0e305a4eb6056076eb34577a13e5f8ed5 (patch) | |
tree | 70ba406b2ff9d3f9a5e7b6a091944686e2fb4509 | |
parent | fd11c96e8ec7c81c95cb2f8fd8258562fe4bef67 (diff) | |
download | serenity-d3ce7ae0e305a4eb6056076eb34577a13e5f8ed5.zip |
LibGUI: Move most of mouse event handling to GAbstractView
This deduplicates existing code, and also gives all the model-backed widgets
selection manipulation and drag support for free :^)
-rw-r--r-- | Libraries/LibGUI/GAbstractColumnView.cpp | 24 | ||||
-rw-r--r-- | Libraries/LibGUI/GAbstractView.cpp | 150 | ||||
-rw-r--r-- | Libraries/LibGUI/GAbstractView.h | 9 | ||||
-rw-r--r-- | Libraries/LibGUI/GColumnsView.cpp | 40 | ||||
-rw-r--r-- | Libraries/LibGUI/GColumnsView.h | 2 | ||||
-rw-r--r-- | Libraries/LibGUI/GItemView.cpp | 129 | ||||
-rw-r--r-- | Libraries/LibGUI/GItemView.h | 6 | ||||
-rw-r--r-- | Libraries/LibGUI/GListView.cpp | 19 | ||||
-rw-r--r-- | Libraries/LibGUI/GListView.h | 1 |
9 files changed, 196 insertions, 184 deletions
diff --git a/Libraries/LibGUI/GAbstractColumnView.cpp b/Libraries/LibGUI/GAbstractColumnView.cpp index a2ca580311..12244a2884 100644 --- a/Libraries/LibGUI/GAbstractColumnView.cpp +++ b/Libraries/LibGUI/GAbstractColumnView.cpp @@ -251,7 +251,7 @@ int GAbstractColumnView::column_width(int column_index) const void GAbstractColumnView::mousemove_event(GMouseEvent& event) { if (!model()) - return; + return GAbstractView::mousemove_event(event); if (m_in_column_resize) { auto delta = event.position() - m_column_resize_origin; @@ -301,6 +301,8 @@ void GAbstractColumnView::mousemove_event(GMouseEvent& event) set_hovered_header_index(-1); } window()->set_override_cursor(GStandardCursor::None); + + GAbstractView::mousemove_event(event); } void GAbstractColumnView::mouseup_event(GMouseEvent& event) @@ -311,6 +313,7 @@ void GAbstractColumnView::mouseup_event(GMouseEvent& event) if (!column_resize_grabbable_rect(m_resizing_column).contains(adjusted_position)) window()->set_override_cursor(GStandardCursor::None); m_in_column_resize = false; + return; } if (m_pressed_column_header_index != -1) { auto header_rect = this->header_rect(m_pressed_column_header_index); @@ -325,17 +328,20 @@ void GAbstractColumnView::mouseup_event(GMouseEvent& event) m_pressed_column_header_index = -1; m_pressed_column_header_is_pressed = false; update_headers(); + return; } } + + GAbstractView::mouseup_event(event); } void GAbstractColumnView::mousedown_event(GMouseEvent& event) { if (!model()) - return; + return GAbstractView::mousedown_event(event); if (event.button() != GMouseButton::Left) - return; + return GAbstractView::mousedown_event(event); if (event.y() < header_height()) { int column_count = model()->column_count(); @@ -361,19 +367,13 @@ void GAbstractColumnView::mousedown_event(GMouseEvent& event) bool is_toggle; auto index = index_at_event_position(event.position(), is_toggle); - if (!index.is_valid()) { - selection().clear(); - return; - } - if (is_toggle && model()->row_count(index)) { + + if (index.is_valid() && is_toggle && model()->row_count(index)) { toggle_index(index); return; } - if (event.modifiers() & Mod_Ctrl) - selection().toggle(index); - else - selection().set(index); + GAbstractView::mousedown_event(event); } GModelIndex GAbstractColumnView::index_at_event_position(const Point& position, bool& is_toggle) const diff --git a/Libraries/LibGUI/GAbstractView.cpp b/Libraries/LibGUI/GAbstractView.cpp index f7179afc3e..b8dd15c0fd 100644 --- a/Libraries/LibGUI/GAbstractView.cpp +++ b/Libraries/LibGUI/GAbstractView.cpp @@ -24,8 +24,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <AK/StringBuilder.h> #include <Kernel/KeyCode.h> #include <LibGUI/GAbstractView.h> +#include <LibGUI/GDragOperation.h> #include <LibGUI/GModel.h> #include <LibGUI/GModelEditingDelegate.h> #include <LibGUI/GPainter.h> @@ -171,3 +173,151 @@ NonnullRefPtr<Font> GAbstractView::font_for_index(const GModelIndex& index) cons return *column_metadata.font; return font(); } + +void GAbstractView::mousedown_event(GMouseEvent& event) +{ + GScrollableWidget::mousedown_event(event); + + if (!model()) + return; + + if (event.button() == GMouseButton::Left) + m_left_mousedown_position = event.position(); + + auto index = index_at_event_position(event.position()); + m_might_drag = false; + + if (!index.is_valid()) { + m_selection.clear(); + } else if (event.modifiers() & Mod_Ctrl) { + m_selection.toggle(index); + } else if (event.button() == GMouseButton::Left) { + // We might be starting a drag, so don't throw away other selected items yet. + m_might_drag = true; + m_selection.add(index); + } else { + m_selection.set(index); + } + + update(); +} + +void GAbstractView::mousemove_event(GMouseEvent& event) +{ + if (!model() || !m_might_drag) + return GScrollableWidget::mousemove_event(event); + + if (!(event.buttons() & GMouseButton::Left) || m_selection.is_empty()) { + m_might_drag = false; + return GScrollableWidget::mousemove_event(event); + } + + auto diff = event.position() - m_left_mousedown_position; + auto distance_travelled_squared = diff.x() * diff.x() + diff.y() * diff.y(); + constexpr int drag_distance_threshold = 5; + + if (distance_travelled_squared <= drag_distance_threshold) + return GScrollableWidget::mousemove_event(event); + + dbg() << "Initiate drag!"; + auto drag_operation = GDragOperation::construct(); + + RefPtr<GraphicsBitmap> bitmap; + + StringBuilder text_builder; + StringBuilder data_builder; + bool first = true; + m_selection.for_each_index([&](auto& index) { + auto text_data = m_model->data(index); + if (!first) + text_builder.append(", "); + text_builder.append(text_data.to_string()); + + auto drag_data = m_model->data(index, GModel::Role::DragData); + data_builder.append(drag_data.to_string()); + data_builder.append('\n'); + + first = false; + + if (!bitmap) { + GVariant icon_data = model()->data(index, GModel::Role::Icon); + if (icon_data.is_icon()) + bitmap = icon_data.as_icon().bitmap_for_size(32); + } + }); + + drag_operation->set_text(text_builder.to_string()); + drag_operation->set_bitmap(bitmap); + drag_operation->set_data("url-list", data_builder.to_string()); + + auto outcome = drag_operation->exec(); + + switch (outcome) { + case GDragOperation::Outcome::Accepted: + dbg() << "Drag was accepted!"; + break; + case GDragOperation::Outcome::Cancelled: + dbg() << "Drag was cancelled!"; + break; + default: + ASSERT_NOT_REACHED(); + break; + } +} + +void GAbstractView::mouseup_event(GMouseEvent& event) +{ + GScrollableWidget::mouseup_event(event); + + if (!model()) + return; + + if (m_might_drag) { + // We were unsure about unselecting items other than the current one + // in mousedown_event(), because we could be seeing a start of a drag. + // Since we're here, it was not that; so fix up the selection now. + auto index = index_at_event_position(event.position()); + if (index.is_valid()) + m_selection.set(index); + else + m_selection.clear(); + m_might_drag = false; + update(); + } +} + +void GAbstractView::doubleclick_event(GMouseEvent& event) +{ + if (!model()) + return; + + if (event.button() != GMouseButton::Left) + return; + + m_might_drag = false; + + auto index = index_at_event_position(event.position()); + + if (!index.is_valid()) + m_selection.clear(); + else if (!m_selection.contains(index)) + m_selection.set(index); + + activate_selected(); +} + +void GAbstractView::context_menu_event(GContextMenuEvent& event) +{ + if (!model()) + return; + + auto index = index_at_event_position(event.position()); + + if (index.is_valid()) + m_selection.add(index); + else + selection().clear(); + + if (on_context_menu_request) + on_context_menu_request(index, event); +} diff --git a/Libraries/LibGUI/GAbstractView.h b/Libraries/LibGUI/GAbstractView.h index 59def38632..bf8fecc172 100644 --- a/Libraries/LibGUI/GAbstractView.h +++ b/Libraries/LibGUI/GAbstractView.h @@ -76,6 +76,12 @@ protected: explicit GAbstractView(GWidget* parent); virtual ~GAbstractView() override; + virtual void mousedown_event(GMouseEvent&) override; + virtual void mousemove_event(GMouseEvent&) override; + virtual void mouseup_event(GMouseEvent&) override; + virtual void doubleclick_event(GMouseEvent&) override; + virtual void context_menu_event(GContextMenuEvent&) override; + virtual void did_scroll() override; void activate(const GModelIndex&); void activate_selected(); @@ -86,6 +92,9 @@ protected: RefPtr<GWidget> m_edit_widget; Rect m_edit_widget_content_rect; + Point m_left_mousedown_position; + bool m_might_drag { false }; + private: RefPtr<GModel> m_model; OwnPtr<GModelEditingDelegate> m_editing_delegate; diff --git a/Libraries/LibGUI/GColumnsView.cpp b/Libraries/LibGUI/GColumnsView.cpp index 8c72925b3c..882c42a3c3 100644 --- a/Libraries/LibGUI/GColumnsView.cpp +++ b/Libraries/LibGUI/GColumnsView.cpp @@ -216,6 +216,8 @@ GModelIndex GColumnsView::index_at_event_position(const Point& a_position) const void GColumnsView::mousedown_event(GMouseEvent& event) { + GAbstractView::mousedown_event(event); + if (!model()) return; @@ -223,15 +225,7 @@ void GColumnsView::mousedown_event(GMouseEvent& event) return; auto index = index_at_event_position(event.position()); - if (!index.is_valid()) { - selection().clear(); - return; - } - - if (event.modifiers() & Mod_Ctrl) { - selection().toggle(index); - } else { - selection().set(index); + if (index.is_valid() && !(event.modifiers() & Mod_Ctrl)) { if (model()->row_count(index)) push_column(index); } @@ -250,34 +244,6 @@ void GColumnsView::did_update_model() update(); } -void GColumnsView::doubleclick_event(GMouseEvent& event) -{ - if (!model()) - return; - - if (event.button() != GMouseButton::Left) - return; - - mousedown_event(event); - activate_selected(); -} - -void GColumnsView::context_menu_event(GContextMenuEvent& event) -{ - if (!model()) - return; - - auto index = index_at_event_position(event.position()); - if (index.is_valid()) { - if (!selection().contains(index)) - selection().set(index); - } else { - selection().clear(); - } - if (on_context_menu_request) - on_context_menu_request(index, event); -} - void GColumnsView::keydown_event(GKeyEvent& event) { if (!model()) diff --git a/Libraries/LibGUI/GColumnsView.h b/Libraries/LibGUI/GColumnsView.h index f34cca2101..3e98f33cfe 100644 --- a/Libraries/LibGUI/GColumnsView.h +++ b/Libraries/LibGUI/GColumnsView.h @@ -51,8 +51,6 @@ private: virtual void did_update_model() override; virtual void paint_event(GPaintEvent&) override; virtual void mousedown_event(GMouseEvent& event) override; - virtual void doubleclick_event(GMouseEvent& event) override; - virtual void context_menu_event(GContextMenuEvent& event) override; virtual void keydown_event(GKeyEvent& event) override; struct Column { diff --git a/Libraries/LibGUI/GItemView.cpp b/Libraries/LibGUI/GItemView.cpp index 1fb36a7eb2..c44c82feec 100644 --- a/Libraries/LibGUI/GItemView.cpp +++ b/Libraries/LibGUI/GItemView.cpp @@ -138,32 +138,32 @@ GModelIndex GItemView::index_at_event_position(const Point& position) const void GItemView::mousedown_event(GMouseEvent& event) { + if (!model()) + return GAbstractView::mousedown_event(event); + + if (event.button() != GMouseButton::Left) + return GAbstractView::mousedown_event(event); + auto index = index_at_event_position(event.position()); + if (index.is_valid()) { + // We might start dragging this item, but not rubber-banding. + return GAbstractView::mousedown_event(event); + } - if (event.button() == GMouseButton::Left) { - m_left_mousedown_position = event.position(); - if (!index.is_valid()) { - if (event.modifiers() & Mod_Ctrl) { - selection().for_each_index([&](auto& index) { - m_rubber_band_remembered_selection.append(index); - }); - } else { - selection().clear(); - } - m_rubber_banding = true; - m_rubber_band_origin = event.position(); - m_rubber_band_current = event.position(); - } else { - if (event.modifiers() & Mod_Ctrl) - selection().toggle(index); - else if (selection().size() > 1) - m_might_drag = true; - else - selection().set(index); - } + ASSERT(m_rubber_band_remembered_selection.is_empty()); + + if (event.modifiers() & Mod_Ctrl) { + selection().for_each_index([&](auto& index) { + m_rubber_band_remembered_selection.append(index); + }); + } else { + selection().clear(); } - GAbstractView::mousedown_event(event); + m_might_drag = false; + m_rubber_banding = true; + m_rubber_band_origin = event.position(); + m_rubber_band_current = event.position(); } void GItemView::mouseup_event(GMouseEvent& event) @@ -172,14 +172,6 @@ void GItemView::mouseup_event(GMouseEvent& event) m_rubber_banding = false; m_rubber_band_remembered_selection.clear(); update(); - return; - } - auto index = index_at_event_position(event.position()); - if (index.is_valid()) { - if ((selection().size() > 1) & m_might_drag) { - selection().set(index); - m_might_drag = false; - } } GAbstractView::mouseup_event(event); } @@ -207,86 +199,9 @@ void GItemView::mousemove_event(GMouseEvent& event) } } - if (event.buttons() & GMouseButton::Left && !selection().is_empty()) { - auto diff = event.position() - m_left_mousedown_position; - auto distance_travelled_squared = diff.x() * diff.x() + diff.y() * diff.y(); - constexpr int drag_distance_threshold = 5; - if (distance_travelled_squared > (drag_distance_threshold)) { - dbg() << "Initiate drag!"; - auto drag_operation = GDragOperation::construct(); - - RefPtr<GraphicsBitmap> bitmap; - - StringBuilder text_builder; - StringBuilder data_builder; - int index_iterations = 0; - selection().for_each_index([&](auto& index) { - index_iterations++; - auto text_data = model()->data(index); - if (index_iterations == 0) - text_builder.append(" "); - text_builder.append(text_data.to_string()); - if (!(index_iterations == selection().size())) - text_builder.append(", "); - - auto drag_data = model()->data(index, GModel::Role::DragData); - data_builder.append(drag_data.to_string()); - data_builder.append('\n'); - - if (!bitmap) { - GVariant icon_data = model()->data(index, GModel::Role::Icon); - if (icon_data.is_icon()) - bitmap = icon_data.as_icon().bitmap_for_size(32); - } - }); - - drag_operation->set_text(text_builder.to_string()); - drag_operation->set_bitmap(bitmap); - drag_operation->set_data("url-list", data_builder.to_string()); - auto outcome = drag_operation->exec(); - switch (outcome) { - case GDragOperation::Outcome::Accepted: - dbg() << "Drag was accepted!"; - break; - case GDragOperation::Outcome::Cancelled: - dbg() << "Drag was cancelled!"; - break; - default: - ASSERT_NOT_REACHED(); - break; - } - } - } - GAbstractView::mousemove_event(event); } -void GItemView::context_menu_event(GContextMenuEvent& event) -{ - if (!model()) - return; - auto index = index_at_event_position(event.position()); - if (index.is_valid()) { - if (!selection().contains(index)) - selection().set(index); - } else { - selection().clear(); - } - if (on_context_menu_request) - on_context_menu_request(index, event); - GAbstractView::context_menu_event(event); -} - -void GItemView::doubleclick_event(GMouseEvent& event) -{ - if (!model()) - return; - if (event.button() == GMouseButton::Left) { - mousedown_event(event); - activate_selected(); - } -} - void GItemView::get_item_rects(int item_index, const Font& font, const GVariant& item_text, Rect& item_rect, Rect& icon_rect, Rect& text_rect) const { item_rect = this->item_rect(item_index); diff --git a/Libraries/LibGUI/GItemView.h b/Libraries/LibGUI/GItemView.h index dfb52d30d4..4eb21552c6 100644 --- a/Libraries/LibGUI/GItemView.h +++ b/Libraries/LibGUI/GItemView.h @@ -61,8 +61,6 @@ private: virtual void mousemove_event(GMouseEvent&) override; virtual void mouseup_event(GMouseEvent&) override; virtual void keydown_event(GKeyEvent&) override; - virtual void doubleclick_event(GMouseEvent&) override; - virtual void context_menu_event(GContextMenuEvent&) override; int item_count() const; Rect item_rect(int item_index) const; @@ -75,10 +73,6 @@ private: int m_visual_column_count { 0 }; int m_visual_row_count { 0 }; - bool m_might_drag { false }; - - Point m_left_mousedown_position; - Size m_effective_item_size { 80, 80 }; bool m_rubber_banding { false }; diff --git a/Libraries/LibGUI/GListView.cpp b/Libraries/LibGUI/GListView.cpp index c6dae846c2..750a4b1a1b 100644 --- a/Libraries/LibGUI/GListView.cpp +++ b/Libraries/LibGUI/GListView.cpp @@ -102,25 +102,6 @@ Point GListView::adjusted_position(const Point& position) const return position.translated(horizontal_scrollbar().value() - frame_thickness(), vertical_scrollbar().value() - frame_thickness()); } -void GListView::mousedown_event(GMouseEvent& event) -{ - if (!model()) - return; - - if (event.button() != GMouseButton::Left) - return; - - auto index = index_at_event_position(event.position()); - if (index.is_valid()) { - if (event.modifiers() & Mod_Ctrl) - selection().toggle(index); - else - selection().set(index); - } else { - selection().clear(); - } -} - void GListView::paint_event(GPaintEvent& event) { GFrame::paint_event(event); diff --git a/Libraries/LibGUI/GListView.h b/Libraries/LibGUI/GListView.h index 4fac12902e..fb237bf8f1 100644 --- a/Libraries/LibGUI/GListView.h +++ b/Libraries/LibGUI/GListView.h @@ -60,7 +60,6 @@ public: private: virtual void did_update_model() override; virtual void paint_event(GPaintEvent&) override; - virtual void mousedown_event(GMouseEvent&) override; virtual void doubleclick_event(GMouseEvent&) override; virtual void keydown_event(GKeyEvent&) override; virtual void resize_event(GResizeEvent&) override; |