X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/86df5ae994e96483559020befd96b838aee4d3ee..2e588733c759cccd2d86e34a3a394b9a8ab0c800:/src/kitemviews/kitemlistcontroller.cpp diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp index ba4047dbe..ce96cab6c 100644 --- a/src/kitemviews/kitemlistcontroller.cpp +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -26,12 +27,14 @@ #include #include #include +#include #include #include 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; } - QMimeData* data = 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.