]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kitemlistcontroller.cpp
Improve naming consistency and leave mode on Escape
[dolphin.git] / src / kitemviews / kitemlistcontroller.cpp
index ba4047dbe8c85661f50ec3a7e311b451f06872c2..ce96cab6c97be6b58a5b4cd2625983e9c4e05040 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <KTwoFingerSwipe>
 #include <KTwoFingerTap>
+#include <KUrlMimeData>
 
 #include <QAccessible>
 #include <QApplication>
 #include <QGraphicsSceneEvent>
 #include <QGraphicsView>
 #include <QMimeData>
+#include <QStyleHints>
 #include <QTimer>
 #include <QTouchEvent>
 
 KItemListController::KItemListController(KItemModelBase* model, KItemListView* view, QObject* parent) :
     QObject(parent),
     m_singleClickActivationEnforced(false),
+    m_selectionMode(false),
     m_selectionTogglePressed(false),
     m_clearSelectionIfItemsAreNotDragged(false),
     m_isSwipeGesture(false),
@@ -50,6 +53,7 @@ KItemListController::KItemListController(KItemModelBase* model, KItemListView* v
     m_pressedIndex(std::nullopt),
     m_pressedMousePos(),
     m_autoActivationTimer(nullptr),
+    m_longPressDetectionTimer(nullptr),
     m_swipeGesture(Qt::CustomGesture),
     m_twoFingerTapGesture(Qt::CustomGesture),
     m_oldSelection(),
@@ -68,6 +72,15 @@ KItemListController::KItemListController(KItemModelBase* model, KItemListView* v
     m_autoActivationTimer->setInterval(-1);
     connect(m_autoActivationTimer, &QTimer::timeout, this, &KItemListController::slotAutoActivationTimeout);
 
+    m_longPressDetectionTimer = new QTimer(this);
+    m_longPressDetectionTimer->setSingleShot(true);
+    m_longPressDetectionTimer->setInterval(QGuiApplication::styleHints()->mousePressAndHoldInterval());
+    connect(m_longPressDetectionTimer, &QTimer::timeout, this, [this]() {
+        if (!m_selectionMode) {
+            Q_EMIT selectionModeChangeRequested(true);
+        }
+    });
+
     setModel(model);
     setView(view);
 
@@ -219,6 +232,16 @@ bool KItemListController::singleClickActivationEnforced() const
     return m_singleClickActivationEnforced;
 }
 
+void KItemListController::setSelectionModeEnabled(bool enabled)
+{
+    m_selectionMode = enabled;
+}
+
+bool KItemListController::selectionMode() const
+{
+    return m_selectionMode;
+}
+
 bool KItemListController::keyPressEvent(QKeyEvent* event)
 {
     int index = m_selectionManager->currentItem();
@@ -407,6 +430,9 @@ bool KItemListController::keyPressEvent(QKeyEvent* event)
     }
 
     case Qt::Key_Escape:
+        if (m_selectionMode && m_selectionManager->selectedItems().count() < 1) {
+            Q_EMIT selectionModeChangeRequested(false);
+        }
         if (m_selectionBehavior != SingleSelection) {
             m_selectionManager->clearSelection();
         }
@@ -575,10 +601,14 @@ bool KItemListController::mouseMoveEvent(QGraphicsSceneMouseEvent* event, const
         return false;
     }
 
+    const QPointF pos = transform.map(event->pos());
+    if ((pos - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
+        m_longPressDetectionTimer->stop();
+    }
+
     if (m_pressedIndex.has_value() && !m_view->rubberBand()->isActive()) {
         // Check whether a dragging should be started
         if (event->buttons() & Qt::LeftButton) {
-            const QPointF pos = transform.map(event->pos());
             if ((pos - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
                 if (!m_selectionManager->isSelected(m_pressedIndex.value())) {
                     // Always assure that the dragged item gets selected. Usually this is already
@@ -638,6 +668,8 @@ bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, con
         m_view->m_tapAndHoldIndicator->setActive(false);
     }
 
+    m_longPressDetectionTimer->stop();
+
     KItemListRubberBand* rubberBand = m_view->rubberBand();
     if (event->source() == Qt::MouseEventSynthesizedByQt && !rubberBand->isActive() && m_isTouchEvent) {
         return false;
@@ -724,7 +756,7 @@ bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent* event, cons
     KItemListWidget* oldHoveredWidget = hoveredWidget();
 
     const QPointF pos = transform.map(event->pos());
-    KItemListWidget* newHoveredWidget = widgetForPos(pos);
+    KItemListWidget* newHoveredWidget = widgetForDropPos(pos);
 
     if (oldHoveredWidget != newHoveredWidget) {
         m_autoActivationTimer->stop();
@@ -806,7 +838,12 @@ bool KItemListController::dropEvent(QGraphicsSceneDragDropEvent* event, const QT
         Q_EMIT aboveItemDropEvent(dropAboveIndex, event);
     } else if (!event->mimeData()->hasFormat(m_model->blacklistItemDropEventMimeType())) {
         // Something has been dropped on an item or on an empty part of the view.
-        Q_EMIT itemDropEvent(m_view->itemAt(pos).value_or(-1), event);
+        const KItemListWidget *receivingWidget = widgetForDropPos(pos);
+        if (receivingWidget) {
+            Q_EMIT itemDropEvent(receivingWidget->index(), event);
+        } else {
+            Q_EMIT itemDropEvent(-1, event);
+        }
     }
 
     QAccessibleEvent accessibilityEvent(view(), QAccessible::DragDropEnd);
@@ -1241,7 +1278,7 @@ void KItemListController::slotRubberBandChanged()
         // been activated in case if no Shift- or Control-key are pressed
         const bool shiftOrControlPressed = QApplication::keyboardModifiers() & Qt::ShiftModifier ||
                                            QApplication::keyboardModifiers() & Qt::ControlModifier;
-        if (!shiftOrControlPressed) {
+        if (!shiftOrControlPressed && !m_selectionMode) {
             m_oldSelection.clear();
         }
     }
@@ -1290,7 +1327,7 @@ void KItemListController::slotRubberBandChanged()
         }
     } while (!selectionFinished);
 
-    if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
+    if ((QApplication::keyboardModifiers() & Qt::ControlModifier) || m_selectionMode) {
         // If Control is pressed, the selection state of all items in the rubberband is toggled.
         // Therefore, the new selection contains:
         // 1. All previously selected items which are not inside the rubberband, and
@@ -1313,10 +1350,11 @@ void KItemListController::startDragging()
         return;
     }
 
-    QMimeDatadata = m_model->createMimeData(selectedItems);
+    QMimeData *data = m_model->createMimeData(selectedItems);
     if (!data) {
         return;
     }
+    KUrlMimeData::exportUrlsToPortal(data);
 
     // The created drag object will be owned and deleted
     // by QApplication::activeWindow().
@@ -1364,6 +1402,21 @@ KItemListWidget* KItemListController::widgetForPos(const QPointF& pos) const
     return nullptr;
 }
 
+KItemListWidget* KItemListController::widgetForDropPos(const QPointF& pos) const
+{
+    Q_ASSERT(m_view);
+
+    const auto widgets = m_view->visibleItemListWidgets();
+    for (KItemListWidget* widget : widgets) {
+        const QPointF mappedPos = widget->mapFromItem(m_view, pos);
+        if (widget->contains(mappedPos)) {
+            return widget;
+        }
+    }
+
+    return nullptr;
+}
+
 void KItemListController::updateKeyboardAnchor()
 {
     const bool validAnchor = m_keyboardAnchorIndex >= 0 &&
@@ -1496,9 +1549,15 @@ bool KItemListController::onPress(const QPoint& screenPos, const QPointF& pos, c
     }
 
     const bool shiftPressed = modifiers & Qt::ShiftModifier;
-    const bool controlPressed = modifiers & Qt::ControlModifier;
+    const bool controlPressed = (modifiers & Qt::ControlModifier) || m_selectionMode; // Keeping selectionMode similar to pressing control will hopefully
+                                                                                      // simplify the overall logic and possibilities both for users and devs.
+    const bool leftClick = buttons & Qt::LeftButton;
     const bool rightClick = buttons & Qt::RightButton;
 
+    if (leftClick) {
+        m_longPressDetectionTimer->start();
+    }
+
     // The previous selection is cleared if either
     // 1. The selection mode is SingleSelection, or
     // 2. the selection mode is MultiSelection, and *none* of the following conditions are met:
@@ -1542,7 +1601,7 @@ bool KItemListController::onPress(const QPoint& screenPos, const QPointF& pos, c
                 return false;
             }
         }
-    } else if (pressedItemAlreadySelected && !shiftOrControlPressed && (buttons & Qt::LeftButton)) {
+    } else if (pressedItemAlreadySelected && !shiftOrControlPressed && leftClick) {
         // The user might want to start dragging multiple items, but if he clicks the item
         // in order to trigger it instead, the other selected items must be deselected.
         // However, we do not know yet what the user is going to do.
@@ -1578,6 +1637,7 @@ 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));
@@ -1586,8 +1646,15 @@ bool KItemListController::onPress(const QPoint& screenPos, const QPointF& pos, c
         bool createRubberBand = (hitTargetIsRowEmptyRegion && m_selectionManager->selectedItems().isEmpty());
 
         if (rightClick && hitTargetIsRowEmptyRegion) {
-            // we got a right click outside the text rect, default to action on the current url and not the pressed item
-            Q_EMIT itemContextMenuRequested(m_pressedIndex.value(), screenPos);
+            // 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);
+            }
             return true;
         }
 
@@ -1600,8 +1667,8 @@ bool KItemListController::onPress(const QPoint& screenPos, const QPointF& pos, c
             break;
 
         case MultiSelection:
-            if (controlPressed && !shiftPressed) {
-                // A mouse button press is happening on an item while control is pressed. This either means a user wants to:
+            if (controlPressed && !shiftPressed && leftClick) {
+                // A left mouse button press is happening on an item while control is pressed. This either means a user wants to:
                 // - toggle the selection of item(s) or
                 // - they want to begin a drag on the item(s) to copy them.
                 // We rule out the latter, if the item is not clicked directly and was unselected previously.