]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Make main view react to context menu events
authorFelix Ernst <felixernst@zohomail.eu>
Mon, 13 Nov 2023 16:50:24 +0000 (17:50 +0100)
committerFelix Ernst <felixernst@kde.org>
Sat, 18 Nov 2023 10:49:11 +0000 (10:49 +0000)
Before this commit, Dolphin's main view would not react to any
context menu events. It only showed context menus based on
hard-coded mouse or keyboard events i.e. mouse right-click and
presses of the "Menu" key.

This commit removes those hard-coded reactions and instead makes it
so the view shows a context menu whenever a QContextMenuEvent is
received. Therefore, a context menu will now be opened when any
platform- or system-specific context menu triggers are invoked e.g.
the Shift+F10 keyboard shortcut.

Aside from this, the only side-effect is a partial removal of an
unrelated bug: Previously, the hover highlight on items was never
cleared when the header column in details view mode was hovered.
With this commit, the hover is now correctly cleared most of the
time.

src/kitemviews/kitemlistcontainer.cpp
src/kitemviews/kitemlistcontainer.h
src/kitemviews/kitemlistcontroller.cpp
src/kitemviews/kitemlistcontroller.h
src/kitemviews/kitemlistview.cpp

index f89a6c8ce3eea3a18b199bb09783b2582f62c8b2..1cac8f7a602bbe71e29c045f919e5dcbe1ff5db2 100644 (file)
@@ -138,6 +138,21 @@ void KItemListContainer::keyPressEvent(QKeyEvent *event)
     }
 }
 
+void KItemListContainer::contextMenuEvent(QContextMenuEvent *event)
+{
+    // Note copied from the keyPressEvent() method above because the same reasons probably also apply here.
+    // TODO: We should find a better way to handle the context menu events in the view.
+    // The reasons why we need this hack are:
+    // 1. Without reimplementing contextMenuEvent() here, the event would not reach the QGraphicsView.
+    // 2. By default, the KItemListView does not have the keyboard focus in the QGraphicsScene, so
+    //    simply sending the event to the QGraphicsView which is the KItemListContainer's viewport
+    //    does not work.
+    KItemListView *view = m_controller->view();
+    if (view) {
+        QApplication::sendEvent(view, event);
+    }
+}
+
 void KItemListContainer::showEvent(QShowEvent *event)
 {
     QAbstractScrollArea::showEvent(event);
index 40b0753764dcbeaec5038adbd53fe90b396537d3..93016ab82bac5f46f96f6f0ccf947f50fcb916ae 100644 (file)
@@ -46,6 +46,7 @@ public:
 
 protected:
     void keyPressEvent(QKeyEvent *event) override;
+    void contextMenuEvent(QContextMenuEvent *event) override;
     void showEvent(QShowEvent *event) override;
     void resizeEvent(QResizeEvent *event) override;
     void scrollContentsBy(int dx, int dy) override;
index 5abf1830be349f2bd397e302920ffbfb3beef259..60f23c1dbe6b6ee1e2a1d21cec7173700db47bc3 100644 (file)
@@ -425,28 +425,6 @@ bool KItemListController::keyPressEvent(QKeyEvent *event)
         break;
     }
 
-    case Qt::Key_Menu: {
-        // Emit the signal itemContextMenuRequested() in case if at least one
-        // item is selected. Otherwise the signal viewContextMenuRequested() will be emitted.
-        const KItemSet selectedItems = m_selectionManager->selectedItems();
-        int index = -1;
-        if (selectedItems.count() >= 2) {
-            const int currentItemIndex = m_selectionManager->currentItem();
-            index = selectedItems.contains(currentItemIndex) ? currentItemIndex : selectedItems.first();
-        } else if (selectedItems.count() == 1) {
-            index = selectedItems.first();
-        }
-
-        if (index >= 0) {
-            const QRectF contextRect = m_view->itemContextRect(index);
-            const QPointF pos(m_view->scene()->views().first()->mapToGlobal(contextRect.bottomRight().toPoint()));
-            Q_EMIT itemContextMenuRequested(index, pos);
-        } else {
-            Q_EMIT viewContextMenuRequested(QCursor::pos());
-        }
-        break;
-    }
-
     case Qt::Key_Escape:
         if (m_selectionMode) {
             Q_EMIT selectionModeChangeRequested(false);
@@ -701,19 +679,7 @@ bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event,
     }
 
     if (event->button() & Qt::RightButton) {
-        m_selectionManager->clearSelection();
-        if (index.has_value()) {
-            m_selectionManager->setSelected(index.value());
-            Q_EMIT itemContextMenuRequested(index.value(), event->screenPos());
-        } else {
-            const QRectF headerBounds = m_view->headerBoundaries();
-            if (headerBounds.contains(event->pos())) {
-                Q_EMIT headerContextMenuRequested(event->screenPos());
-            } else {
-                Q_EMIT viewContextMenuRequested(event->screenPos());
-            }
-        }
-        return true;
+        return false;
     }
 
     bool emitItemActivated = !(m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced)
@@ -724,6 +690,59 @@ bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event,
     return false;
 }
 
+bool KItemListController::contextMenuEvent(QContextMenuEvent *event)
+{
+    if (event->reason() == QContextMenuEvent::Keyboard) {
+        // Emit the signal itemContextMenuRequested() if at least one item is selected.
+        // Otherwise the signal viewContextMenuRequested() will be emitted.
+        const KItemSet selectedItems = m_selectionManager->selectedItems();
+        int index = -1;
+        if (selectedItems.count() >= 2) {
+            const int currentItemIndex = m_selectionManager->currentItem();
+            index = selectedItems.contains(currentItemIndex) ? currentItemIndex : selectedItems.first();
+        } else if (selectedItems.count() == 1) {
+            index = selectedItems.first();
+        }
+
+        if (index >= 0) {
+            const QRectF contextRect = m_view->itemContextRect(index);
+            const QPointF pos(m_view->scene()->views().first()->mapToGlobal(contextRect.bottomRight().toPoint()));
+            Q_EMIT itemContextMenuRequested(index, pos);
+        } else {
+            Q_EMIT viewContextMenuRequested(event->globalPos());
+        }
+        return true;
+    }
+
+    const auto pos = event->pos();
+    const auto globalPos = event->globalPos();
+
+    if (m_view->headerBoundaries().contains(pos)) {
+        Q_EMIT headerContextMenuRequested(globalPos);
+        return true;
+    }
+
+    const auto pressedItem = m_view->itemAt(pos);
+    // We only open a context menu for the pressed item if it is selected.
+    // That's because the same click might have de-selected the item or because the press was only in the row of the item but not on it.
+    if (pressedItem && m_selectionManager->selectedItems().contains(pressedItem.value())) {
+        // The selection rectangle for an item was clicked
+        Q_EMIT itemContextMenuRequested(m_pressedIndex.value(), globalPos);
+        return true;
+    }
+
+    // Remove any hover highlights so the context menu doesn't look like it applies to a row.
+    const auto widgets = m_view->visibleItemListWidgets();
+    for (KItemListWidget *widget : widgets) {
+        if (widget->isHovered()) {
+            widget->setHovered(false);
+            Q_EMIT itemUnhovered(widget->index());
+        }
+    }
+    Q_EMIT viewContextMenuRequested(globalPos);
+    return true;
+}
+
 bool KItemListController::dragEnterEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform)
 {
     Q_UNUSED(event)
@@ -1208,6 +1227,8 @@ bool KItemListController::processEvent(QEvent *event, const QTransform &transfor
         return mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event), QTransform());
     case QEvent::GraphicsSceneMouseDoubleClick:
         return mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event), QTransform());
+    case QEvent::ContextMenu:
+        return contextMenuEvent(static_cast<QContextMenuEvent *>(event));
     case QEvent::GraphicsSceneWheel:
         return wheelEvent(static_cast<QGraphicsSceneWheelEvent *>(event), QTransform());
     case QEvent::GraphicsSceneDragEnter:
@@ -1393,6 +1414,10 @@ KItemListWidget *KItemListController::widgetForPos(const QPointF &pos) const
 {
     Q_ASSERT(m_view);
 
+    if (m_view->headerBoundaries().contains(pos)) {
+        return nullptr;
+    }
+
     const auto widgets = m_view->visibleItemListWidgets();
     for (KItemListWidget *widget : widgets) {
         const QPointF mappedPos = widget->mapFromItem(m_view, pos);
@@ -1408,6 +1433,10 @@ KItemListWidget *KItemListController::widgetForDropPos(const QPointF &pos) const
 {
     Q_ASSERT(m_view);
 
+    if (m_view->headerBoundaries().contains(pos)) {
+        return nullptr;
+    }
+
     const auto widgets = m_view->visibleItemListWidgets();
     for (KItemListWidget *widget : widgets) {
         const QPointF mappedPos = widget->mapFromItem(m_view, pos);
@@ -1617,12 +1646,6 @@ bool KItemListController::onPress(const QPoint &screenPos, const QPointF &pos, c
     }
 
     if (rightClick) {
-        // Do header hit check and short circuit before commencing any state changing effects
-        if (m_view->headerBoundaries().contains(pos)) {
-            Q_EMIT headerContextMenuRequested(screenPos);
-            return true;
-        }
-
         // Stop rubber band from persisting after right-clicks
         KItemListRubberBand *rubberBand = m_view->rubberBand();
         if (rubberBand->isActive()) {
@@ -1634,7 +1657,6 @@ bool KItemListController::onPress(const QPoint &screenPos, const QPointF &pos, c
 
     if (m_pressedIndex.has_value()) {
         // The hover highlight area of an item is being pressed.
-        m_selectionManager->setCurrentItem(m_pressedIndex.value());
         const auto row = m_view->m_visibleItems.value(m_pressedIndex.value()); // anything outside of row.contains() will be the empty region of the row rect
         const bool hitTargetIsRowEmptyRegion = !row->contains(row->mapFromItem(m_view, pos));
         // again, when this method returns false, a rubberBand selection is created as the event is not consumed;
@@ -1642,18 +1664,13 @@ bool KItemListController::onPress(const QPoint &screenPos, const QPointF &pos, c
         bool createRubberBand = (hitTargetIsRowEmptyRegion && m_selectionManager->selectedItems().isEmpty());
 
         if (rightClick && hitTargetIsRowEmptyRegion) {
-            // We have a right click outside the icon and text rect but within the hover highlight area
-            // but it is unclear if this means that a selection rectangle for an item was clicked or the background of the view.
-            if (m_selectionManager->selectedItems().contains(m_pressedIndex.value())) {
-                // The selection rectangle for an item was clicked
-                Q_EMIT itemContextMenuRequested(m_pressedIndex.value(), screenPos);
-            } else {
-                row->setHovered(false); // Removes the hover highlight so the context menu doesn't look like it applies to the row.
-                Q_EMIT viewContextMenuRequested(screenPos);
-            }
+            // We have a right click outside the icon and text rect but within the hover highlight area.
+            // We don't want items to get selected through this, so we return now.
             return true;
         }
 
+        m_selectionManager->setCurrentItem(m_pressedIndex.value());
+
         switch (m_selectionBehavior) {
         case NoSelection:
             break;
@@ -1690,19 +1707,9 @@ bool KItemListController::onPress(const QPoint &screenPos, const QPointF &pos, c
             break;
         }
 
-        if (rightClick) {
-            Q_EMIT itemContextMenuRequested(m_pressedIndex.value(), screenPos);
-        }
         return !createRubberBand;
     }
 
-    if (rightClick) {
-        // header right click handling would have been done before this so just normal context
-        // menu here is fine
-        Q_EMIT viewContextMenuRequested(screenPos);
-        return true;
-    }
-
     return false;
 }
 
index 0576fc7fd60742872eb0d83b55d3244240e378f9..9c3a003d352e49264ff02e50e4ca3866fef23d67 100644 (file)
@@ -24,6 +24,7 @@ class KItemListKeyboardSearchManager;
 class KItemListSelectionManager;
 class KItemListView;
 class KItemListWidget;
+class QContextMenuEvent;
 class QGestureEvent;
 class QGraphicsSceneHoverEvent;
 class QGraphicsSceneDragDropEvent;
@@ -313,6 +314,7 @@ private:
     bool mouseMoveEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
     bool mouseReleaseEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
     bool mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
+    bool contextMenuEvent(QContextMenuEvent *event);
     bool dragEnterEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
     bool dragLeaveEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
     bool dragMoveEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
index cf14836592ffe680a7cccb640a361f769eefa5c0..89376ab20430064cbbbc5e3a20b327ce31300094 100644 (file)
@@ -402,6 +402,10 @@ qreal KItemListView::verticalPageStep() const
 
 std::optional<int> KItemListView::itemAt(const QPointF &pos) const
 {
+    if (headerBoundaries().contains(pos)) {
+        return std::nullopt;
+    }
+
     QHashIterator<int, KItemListWidget *> it(m_visibleItems);
     while (it.hasNext()) {
         it.next();