Forráskód Böngészése

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 :^)
Sergey Bugaev 5 éve
szülő
commit
d3ce7ae0e3

+ 12 - 12
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

+ 150 - 0
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);
+}

+ 9 - 0
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;

+ 3 - 37
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())

+ 0 - 2
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 {

+ 22 - 107
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);

+ 0 - 6
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 };

+ 0 - 19
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);

+ 0 - 1
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;