From c1e71289082ec7416ac19c822393ea70f63d1b75 Mon Sep 17 00:00:00 2001 From: Akseli Lahtinen Date: Fri, 20 Jun 2025 00:15:31 +0300 Subject: New selection effects This adds a new selection effect that is similar to what we have in QtQuick file item views. There are also changes to some usability: Instead of only the icon and text being the clickable area in icon and details mode, the whole selection is now the clickable area. Otherwise the usability should stay the same, it's mostly a visual change. See also: https://invent.kde.org/teams/vdg/issues/-/issues/94 --- src/kitemviews/kitemlistcontroller.cpp | 39 ++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 14 deletions(-) (limited to 'src/kitemviews/kitemlistcontroller.cpp') diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp index cd60c3a41..fdde48abc 100644 --- a/src/kitemviews/kitemlistcontroller.cpp +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -675,6 +675,10 @@ bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent *event, con return false; } + for (KItemListWidget *widget : m_view->visibleItemListWidgets()) { + widget->setPressed(false); + } + if (m_view->m_tapAndHoldIndicator->isActive()) { m_view->m_tapAndHoldIndicator->setActive(false); } @@ -990,7 +994,7 @@ bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent *event, const // we also unhover any old expansion toggle hovers, in case the mouse movement from expansion toggle to icon+text is too fast (i.e. newHoveredWidget is never null between the transition) unhoverOldExpansionWidget(); - const bool isOverIconAndText = newHoveredWidget->iconRect().contains(mappedPos) || newHoveredWidget->textRect().contains(mappedPos); + const bool isOverIconAndText = newHoveredWidget->selectionRectCore().contains(mappedPos); const bool hasMultipleSelection = m_selectionManager->selectedItems().count() > 1; if (hasMultipleSelection && !isOverIconAndText) { @@ -1029,6 +1033,7 @@ bool KItemListController::hoverLeaveEvent(QGraphicsSceneHoverEvent *event, const const auto widgets = m_view->visibleItemListWidgets(); for (KItemListWidget *widget : widgets) { + widget->setPressed(false); if (widget->isHovered()) { widget->setHovered(false); Q_EMIT itemUnhovered(widget->index()); @@ -1365,9 +1370,8 @@ void KItemListController::slotRubberBandChanged() const QRectF widgetRect = m_view->itemRect(index); if (widgetRect.intersects(rubberBandRect)) { // Select the full row intersecting with the rubberband rectangle - const QRectF selectionRect = widget->selectionRect().translated(widgetRect.topLeft()); - const QRectF iconRect = widget->iconRect().translated(widgetRect.topLeft()); - if (selectionRect.intersects(rubberBandRect) || iconRect.intersects(rubberBandRect)) { + const QRectF selectionRect = widget->selectionRectFull().translated(widgetRect.topLeft()); + if (selectionRect.intersects(rubberBandRect)) { selectedItems.insert(index); } } @@ -1467,7 +1471,7 @@ KItemListWidget *KItemListController::widgetForPos(const QPointF &pos) const const auto widgets = m_view->visibleItemListWidgets(); for (KItemListWidget *widget : widgets) { const QPointF mappedPos = widget->mapFromItem(m_view, pos); - if (widget->contains(mappedPos) || widget->selectionRect().contains(mappedPos)) { + if (widget->contains(mappedPos)) { return widget; } } @@ -1486,7 +1490,7 @@ KItemListWidget *KItemListController::widgetForDropPos(const QPointF &pos) const const auto widgets = m_view->visibleItemListWidgets(); for (KItemListWidget *widget : widgets) { const QPointF mappedPos = widget->mapFromItem(m_view, pos); - if (widget->contains(mappedPos)) { + if (widget->selectionRectCore().contains(mappedPos)) { return widget; } } @@ -1641,6 +1645,7 @@ bool KItemListController::onPress(const QPointF &pos, const Qt::KeyboardModifier // simplify the overall logic and possibilities both for users and devs. const bool leftClick = buttons & Qt::LeftButton; const bool rightClick = buttons & Qt::RightButton; + const bool singleClickActivation = m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick); // The previous selection is cleared if either // 1. The selection mode is SingleSelection, or @@ -1661,11 +1666,11 @@ bool KItemListController::onPress(const QPointF &pos, const Qt::KeyboardModifier if (selectedItemsCount > 1 && m_pressedIndex.has_value()) { const auto row = m_view->m_visibleItems.value(m_pressedIndex.value()); const auto mappedPos = row->mapFromItem(m_view, pos); - if (pressedItemAlreadySelected || row->iconRect().contains(mappedPos) || row->textRect().contains(mappedPos)) { + if (row->selectionRectCore().contains(mappedPos)) { // we are indeed inside the text/icon rect, keep m_pressedIndex what it is // and short-circuit for single-click activation (it will then propagate to onRelease and activate the item) // or we just keep going for double-click activation - if (m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced) { + if (singleClickActivation || m_singleClickActivationEnforced) { if (!pressedItemAlreadySelected) { // An unselected item was clicked directly while deselecting multiple other items so we mark it "current". m_selectionManager->setCurrentItem(m_pressedIndex.value()); @@ -1676,6 +1681,9 @@ bool KItemListController::onPress(const QPointF &pos, const Qt::KeyboardModifier m_selectionManager->setSelected(m_pressedIndex.value(), 1, KItemListSelectionManager::Toggle); } } + if (leftClick) { + row->setPressed(true); + } return true; // event handled, don't create rubber band } } else { @@ -1723,11 +1731,16 @@ bool KItemListController::onPress(const QPointF &pos, const Qt::KeyboardModifier if (m_pressedIndex.has_value()) { // The hover highlight area of an item is being pressed. 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)); + + const bool hitTargetIsRowEmptyRegion = !row->selectionRectCore().contains(row->mapFromItem(m_view, pos)); // again, when this method returns false, a rubberBand selection is created as the event is not consumed; // createRubberBand here tells us whether to return true or false. bool createRubberBand = (hitTargetIsRowEmptyRegion && m_selectionManager->selectedItems().isEmpty()); + if (leftClick) { + row->setPressed(true); + } + if (rightClick && hitTargetIsRowEmptyRegion) { // 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. @@ -1741,8 +1754,7 @@ bool KItemListController::onPress(const QPointF &pos, const Qt::KeyboardModifier break; case SingleSelection: - if (!leftClick || shiftOrControlPressed - || (!m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) && !m_singleClickActivationEnforced)) { + if (!leftClick || shiftOrControlPressed || (!singleClickActivation && !m_singleClickActivationEnforced)) { m_selectionManager->setSelected(m_pressedIndex.value()); } break; @@ -1755,7 +1767,7 @@ bool KItemListController::onPress(const QPointF &pos, const Qt::KeyboardModifier // We rule out the latter, if the item is not clicked directly and was unselected previously. const auto row = m_view->m_visibleItems.value(m_pressedIndex.value()); const auto mappedPos = row->mapFromItem(m_view, pos); - if (!row->iconRect().contains(mappedPos) && !row->textRect().contains(mappedPos) && !pressedItemAlreadySelected) { + if (!row->selectionRectCore().contains(mappedPos)) { createRubberBand = true; } else { m_selectionManager->setSelected(m_pressedIndex.value(), 1, KItemListSelectionManager::Toggle); @@ -1765,8 +1777,7 @@ bool KItemListController::onPress(const QPointF &pos, const Qt::KeyboardModifier } } else if (!shiftPressed || !m_selectionManager->isAnchoredSelectionActive()) { // Select the pressed item and start a new anchored selection - if (!leftClick || shiftOrControlPressed - || (!m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) && !m_singleClickActivationEnforced)) { + if (!leftClick || shiftOrControlPressed || (!singleClickActivation && !m_singleClickActivationEnforced)) { m_selectionManager->setSelected(m_pressedIndex.value(), 1, KItemListSelectionManager::Select); } m_selectionManager->beginAnchoredSelection(m_pressedIndex.value()); -- cgit v1.3