#include <KTwoFingerSwipe>
#include <KTwoFingerTap>
+#include <KUrlMimeData>
#include <QAccessible>
#include <QApplication>
#include <QTimer>
#include <QTouchEvent>
-KItemListController::KItemListController(KItemModelBase* model, KItemListView* view, QObject* parent) :
- QObject(parent),
- m_singleClickActivationEnforced(false),
- m_selectionTogglePressed(false),
- m_clearSelectionIfItemsAreNotDragged(false),
- m_isSwipeGesture(false),
- m_dragActionOrRightClick(false),
- m_scrollerIsScrolling(false),
- m_pinchGestureInProgress(false),
- m_mousePress(false),
- m_isTouchEvent(false),
- m_selectionBehavior(NoSelection),
- m_autoActivationBehavior(ActivationAndExpansion),
- m_mouseDoubleClickAction(ActivateItemOnly),
- m_model(nullptr),
- m_view(nullptr),
- m_selectionManager(new KItemListSelectionManager(this)),
- m_keyboardManager(new KItemListKeyboardSearchManager(this)),
- m_pressedIndex(std::nullopt),
- m_pressedMousePos(),
- m_autoActivationTimer(nullptr),
- m_swipeGesture(Qt::CustomGesture),
- m_twoFingerTapGesture(Qt::CustomGesture),
- m_oldSelection(),
- m_keyboardAnchorIndex(-1),
- m_keyboardAnchorPos(0)
+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)
+ , m_dragActionOrRightClick(false)
+ , m_scrollerIsScrolling(false)
+ , m_pinchGestureInProgress(false)
+ , m_mousePress(false)
+ , m_isTouchEvent(false)
+ , m_selectionBehavior(NoSelection)
+ , m_autoActivationBehavior(ActivationAndExpansion)
+ , m_mouseDoubleClickAction(ActivateItemOnly)
+ , m_model(nullptr)
+ , m_view(nullptr)
+ , m_selectionManager(new KItemListSelectionManager(this))
+ , m_keyboardManager(new KItemListKeyboardSearchManager(this))
+ , m_pressedIndex(std::nullopt)
+ , m_pressedMouseGlobalPos()
+ , m_autoActivationTimer(nullptr)
+ , m_swipeGesture(Qt::CustomGesture)
+ , m_twoFingerTapGesture(Qt::CustomGesture)
+ , m_oldSelection()
+ , m_keyboardAnchorIndex(-1)
+ , m_keyboardAnchorPos(0)
{
- connect(m_keyboardManager, &KItemListKeyboardSearchManager::changeCurrentItem,
- this, &KItemListController::slotChangeCurrentItem);
- connect(m_selectionManager, &KItemListSelectionManager::currentChanged,
- m_keyboardManager, &KItemListKeyboardSearchManager::slotCurrentChanged);
- connect(m_selectionManager, &KItemListSelectionManager::selectionChanged,
- m_keyboardManager, &KItemListKeyboardSearchManager::slotSelectionChanged);
+ connect(m_keyboardManager, &KItemListKeyboardSearchManager::changeCurrentItem, this, &KItemListController::slotChangeCurrentItem);
+ connect(m_selectionManager, &KItemListSelectionManager::currentChanged, m_keyboardManager, &KItemListKeyboardSearchManager::slotCurrentChanged);
+ connect(m_selectionManager, &KItemListSelectionManager::selectionChanged, m_keyboardManager, &KItemListKeyboardSearchManager::slotSelectionChanged);
m_autoActivationTimer = new QTimer(this);
m_autoActivationTimer->setSingleShot(true);
- m_autoActivationTimer->setInterval(-1);
+ m_autoActivationTimer->setInterval(750);
connect(m_autoActivationTimer, &QTimer::timeout, this, &KItemListController::slotAutoActivationTimeout);
setModel(model);
Q_ASSERT(!m_model);
}
-void KItemListController::setModel(KItemModelBase* model)
+void KItemListController::setModel(KItemModelBase *model)
{
if (m_model == model) {
return;
}
- KItemModelBase* oldModel = m_model;
+ KItemModelBase *oldModel = m_model;
if (oldModel) {
oldModel->deleteLater();
}
Q_EMIT modelChanged(m_model, oldModel);
}
-KItemModelBase* KItemListController::model() const
+KItemModelBase *KItemListController::model() const
{
return m_model;
}
-KItemListSelectionManager* KItemListController::selectionManager() const
+KItemListSelectionManager *KItemListController::selectionManager() const
{
return m_selectionManager;
}
-void KItemListController::setView(KItemListView* view)
+void KItemListController::setView(KItemListView *view)
{
if (m_view == view) {
return;
}
- KItemListView* oldView = m_view;
+ KItemListView *oldView = m_view;
if (oldView) {
disconnect(oldView, &KItemListView::scrollOffsetChanged, this, &KItemListController::slotViewScrollOffsetChanged);
oldView->deleteLater();
Q_EMIT viewChanged(m_view, oldView);
}
-KItemListView* KItemListController::view() const
+KItemListView *KItemListController::view() const
{
return m_view;
}
int KItemListController::indexCloseToMousePressedPosition() const
{
- QHashIterator<KItemListWidget*, KItemListGroupHeader*> it(m_view->m_visibleGroups);
+ const QPointF pressedMousePos = m_view->transform().map(m_view->scene()->views().first()->mapFromGlobal(m_pressedMouseGlobalPos.toPoint()));
+
+ QHashIterator<KItemListWidget *, KItemListGroupHeader *> it(m_view->m_visibleGroups);
while (it.hasNext()) {
it.next();
KItemListGroupHeader *groupHeader = it.value();
- const QPointF mappedToGroup = groupHeader->mapFromItem(nullptr, m_pressedMousePos);
+ const QPointF mappedToGroup = groupHeader->mapFromItem(nullptr, pressedMousePos);
if (groupHeader->contains(mappedToGroup)) {
return it.key()->index();
}
return -1;
}
-void KItemListController::setAutoActivationDelay(int delay)
+void KItemListController::setAutoActivationEnabled(bool enabled)
{
- m_autoActivationTimer->setInterval(delay);
+ m_autoActivationEnabled = enabled;
}
-int KItemListController::autoActivationDelay() const
+bool KItemListController::isAutoActivationEnabled() const
{
- return m_autoActivationTimer->interval();
+ return m_autoActivationEnabled;
}
void KItemListController::setSingleClickActivationEnforced(bool singleClick)
return m_singleClickActivationEnforced;
}
-bool KItemListController::keyPressEvent(QKeyEvent* event)
+void KItemListController::setSelectionModeEnabled(bool enabled)
+{
+ m_selectionMode = enabled;
+}
+
+bool KItemListController::selectionMode() const
+{
+ return m_selectionMode;
+}
+
+bool KItemListController::isSearchAsYouTypeActive() const
+{
+ return m_keyboardManager->isSearchAsYouTypeActive();
+}
+
+bool KItemListController::keyPressEvent(QKeyEvent *event)
{
int index = m_selectionManager->currentItem();
int key = event->key();
+ const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
+
+ const bool horizontalScrolling = m_view->scrollOrientation() == Qt::Horizontal;
+
+ if (m_view->layoutDirection() == Qt::RightToLeft) {
+ // swap left and right arrow keys
+ switch (key) {
+ case Qt::Key_Left:
+ key = Qt::Key_Right;
+ break;
+ case Qt::Key_Right:
+ key = Qt::Key_Left;
+ break;
+ default:
+ break;
+ }
+ }
// Handle the expanding/collapsing of items
- if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
- if (key == Qt::Key_Right) {
- if (m_model->setExpanded(index, true)) {
- return true;
+ // expand / collapse all selected directories
+ if (m_view->supportsItemExpanding() && m_model->isExpandable(index) && (key == Qt::Key_Right || key == Qt::Key_Left)) {
+ const bool expandOrCollapse = key == Qt::Key_Right ? true : false;
+ bool shouldReturn = m_model->setExpanded(index, expandOrCollapse);
+
+ // edit in reverse to preserve index of the first handled items
+ const auto selectedItems = m_selectionManager->selectedItems();
+ for (auto it = selectedItems.rbegin(); it != selectedItems.rend(); ++it) {
+ shouldReturn |= m_model->setExpanded(*it, expandOrCollapse);
+ if (!shiftPressed) {
+ m_selectionManager->setSelected(*it);
}
- } else if (key == Qt::Key_Left) {
- if (m_model->setExpanded(index, false)) {
- return true;
+ }
+ if (shouldReturn) {
+ // update keyboard anchors
+ if (shiftPressed) {
+ m_keyboardAnchorIndex = selectedItems.count() > 0 ? qMin(index, selectedItems.last()) : index;
+ m_keyboardAnchorPos = keyboardAnchorPos(m_keyboardAnchorIndex);
}
+
+ event->ignore();
+ return true;
}
}
- const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
const bool controlPressed = event->modifiers() & Qt::ControlModifier;
- const bool shiftOrControlPressed = shiftPressed || controlPressed;
- const bool navigationPressed = key == Qt::Key_Home || key == Qt::Key_End ||
- key == Qt::Key_PageUp || key == Qt::Key_PageDown ||
- key == Qt::Key_Up || key == Qt::Key_Down ||
- key == Qt::Key_Left || key == Qt::Key_Right;
+ if (m_selectionMode && !controlPressed && !shiftPressed && (key == Qt::Key_Enter || key == Qt::Key_Return)) {
+ key = Qt::Key_Space; // In selection mode one moves around with arrow keys and toggles selection with Enter.
+ }
+ const bool navigationPressed = key == Qt::Key_Home || key == Qt::Key_End || key == Qt::Key_PageUp || key == Qt::Key_PageDown || key == Qt::Key_Up
+ || key == Qt::Key_Down || key == Qt::Key_Left || key == Qt::Key_Right;
const int itemCount = m_model->count();
// For horizontal scroll orientation, transform
// the arrow keys to simplify the event handling.
- if (m_view->scrollOrientation() == Qt::Horizontal) {
+ if (horizontalScrolling) {
switch (key) {
- case Qt::Key_Up: key = Qt::Key_Left; break;
- case Qt::Key_Down: key = Qt::Key_Right; break;
- case Qt::Key_Left: key = Qt::Key_Up; break;
- case Qt::Key_Right: key = Qt::Key_Down; break;
- default: break;
+ case Qt::Key_Up:
+ key = Qt::Key_Left;
+ break;
+ case Qt::Key_Down:
+ key = Qt::Key_Right;
+ break;
+ case Qt::Key_Left:
+ key = Qt::Key_Up;
+ break;
+ case Qt::Key_Right:
+ key = Qt::Key_Down;
+ break;
+ default:
+ break;
}
}
case Qt::Key_Up:
updateKeyboardAnchor();
+ if (shiftPressed && !m_selectionManager->isAnchoredSelectionActive() && m_selectionManager->isSelected(index)) {
+ m_selectionManager->beginAnchoredSelection(index);
+ }
index = previousRowIndex(index);
break;
case Qt::Key_Down:
updateKeyboardAnchor();
+ if (shiftPressed && !m_selectionManager->isAnchoredSelectionActive() && m_selectionManager->isSelected(index)) {
+ m_selectionManager->beginAnchoredSelection(index);
+ }
index = nextRowIndex(index);
break;
case Qt::Key_PageUp:
- if (m_view->scrollOrientation() == Qt::Horizontal) {
+ if (horizontalScrolling) {
// The new current index should correspond to the first item in the current column.
int newIndex = qMax(index - 1, 0);
while (newIndex != index && m_view->itemRect(newIndex).topLeft().y() < m_view->itemRect(index).topLeft().y()) {
break;
case Qt::Key_PageDown:
- if (m_view->scrollOrientation() == Qt::Horizontal) {
+ if (horizontalScrolling) {
// The new current index should correspond to the last item in the current column.
int newIndex = qMin(index + 1, m_model->count() - 1);
while (newIndex != index && m_view->itemRect(newIndex).topLeft().y() > m_view->itemRect(index).topLeft().y()) {
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_selectionBehavior != SingleSelection) {
+ if (m_selectionMode) {
+ Q_EMIT selectionModeChangeRequested(false);
+ } else if (m_selectionBehavior != SingleSelection) {
m_selectionManager->clearSelection();
}
m_keyboardManager->cancelSearch();
case Qt::Key_Space:
if (m_selectionBehavior == MultiSelection) {
- if (controlPressed) {
+ if (controlPressed || m_selectionMode) {
// Toggle the selection state of the current item.
m_selectionManager->endAnchoredSelection();
m_selectionManager->setSelected(index, 1, KItemListSelectionManager::Toggle);
}
}
}
- Q_FALLTHROUGH(); // fall through to the default case and add the Space to the current search string.
+ Q_FALLTHROUGH(); // fall through to the default case and add the Space to the current search string.
default:
m_keyboardManager->addKeys(event->text());
// Make sure unconsumed events get propagated up the chain. #302329
break;
case MultiSelection:
- if (controlPressed) {
+ if (controlPressed || (m_selectionMode && !shiftPressed)) {
m_selectionManager->endAnchoredSelection();
}
m_selectionManager->setCurrentItem(index);
- if (!shiftOrControlPressed) {
+ if (!shiftPressed && !controlPressed && !m_selectionMode) {
m_selectionManager->clearSelection();
m_selectionManager->setSelected(index, 1);
}
return true;
}
-void KItemListController::slotChangeCurrentItem(const QString& text, bool searchFromNextItem)
+void KItemListController::slotChangeCurrentItem(const QString &text, bool searchFromNextItem)
{
if (!m_model || m_model->count() == 0) {
return;
}
int index;
- if (searchFromNextItem) {
- const int currentIndex = m_selectionManager->currentItem();
- index = m_model->indexForKeyboardSearch(text, (currentIndex + 1) % m_model->count());
+ // In selection mode, always use the current (underlined) item, or the next item, for search start position.
+ if (m_selectionBehavior == NoSelection || m_selectionMode || m_selectionManager->hasSelection()) {
+ index = m_model->indexForKeyboardSearch(text, searchFromNextItem ? m_selectionManager->currentItem() + 1 : m_selectionManager->currentItem());
} else {
index = m_model->indexForKeyboardSearch(text, 0);
}
if (index >= 0) {
+ if (m_selectionMode) {
+ m_selectionManager->endAnchoredSelection();
+ }
+
m_selectionManager->setCurrentItem(index);
if (m_selectionBehavior != NoSelection) {
- m_selectionManager->replaceSelection(index);
+ if (!m_selectionMode) { // Don't clear the selection in selection mode.
+ m_selectionManager->replaceSelection(index);
+ }
m_selectionManager->beginAnchoredSelection(index);
}
- m_view->scrollToItem(index);
+ m_view->scrollToItem(index, KItemListView::ViewItemPosition::Beginning);
}
}
*
* See Bug 293200 and 305783
*/
- if (m_model->supportsDropping(index) && m_view->isUnderMouse()) {
+ if (m_view->isUnderMouse()) {
if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
const bool expanded = m_model->isExpanded(index);
m_model->setExpanded(index, !expanded);
}
}
-bool KItemListController::inputMethodEvent(QInputMethodEvent* event)
+bool KItemListController::inputMethodEvent(QInputMethodEvent *event)
{
Q_UNUSED(event)
return false;
}
-bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
+bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform)
{
m_mousePress = true;
+ m_pressedMouseGlobalPos = event->screenPos();
if (event->source() == Qt::MouseEventSynthesizedByQt && m_isTouchEvent) {
return false;
return false;
}
- m_pressedMousePos = transform.map(event->pos());
- m_pressedIndex = m_view->itemAt(m_pressedMousePos);
+ const QPointF pressedMousePos = transform.map(event->pos());
+ m_pressedIndex = m_view->itemAt(pressedMousePos);
const Qt::MouseButtons buttons = event->buttons();
- if (!onPress(event->screenPos(), event->pos(), event->modifiers(), buttons)) {
+ if (!onPress(event->pos(), event->modifiers(), buttons)) {
startRubberBand();
return false;
}
return true;
}
-bool KItemListController::mouseMoveEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
+bool KItemListController::mouseMoveEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform)
{
if (!m_view) {
return false;
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()) {
+ const auto distance = (event->screenPos() - m_pressedMouseGlobalPos).manhattanLength();
+ if (distance >= QApplication::startDragDistance()) {
if (!m_selectionManager->isSelected(m_pressedIndex.value())) {
// Always assure that the dragged item gets selected. Usually this is already
// done on the mouse-press event, but when using the selection-toggle on a
}
}
} else {
- KItemListRubberBand* rubberBand = m_view->rubberBand();
+ KItemListRubberBand *rubberBand = m_view->rubberBand();
if (rubberBand->isActive()) {
QPointF endPos = transform.map(event->pos());
if (m_view->scrollOrientation() == Qt::Vertical) {
endPos.ry() += m_view->scrollOffset();
- if (m_view->itemSize().width() < 0) {
- // Use a special rubberband for views that have only one column and
- // expand the rubberband to use the whole width of the view.
- endPos.setX(m_view->size().width());
- }
} else {
endPos.rx() += m_view->scrollOffset();
}
return false;
}
-bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
+bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform)
{
m_mousePress = false;
m_isTouchEvent = false;
return false;
}
+ for (KItemListWidget *widget : m_view->visibleItemListWidgets()) {
+ widget->setPressed(false);
+ }
+
if (m_view->m_tapAndHoldIndicator->isActive()) {
m_view->m_tapAndHoldIndicator->setActive(false);
}
- KItemListRubberBand* rubberBand = m_view->rubberBand();
+ KItemListRubberBand *rubberBand = m_view->rubberBand();
if (event->source() == Qt::MouseEventSynthesizedByQt && !rubberBand->isActive() && m_isTouchEvent) {
return false;
}
return onRelease(transform.map(event->pos()), event->modifiers(), event->button(), false);
}
-bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
+bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform)
{
const QPointF pos = transform.map(event->pos());
const std::optional<int> index = m_view->itemAt(pos);
+ if (event->button() & (Qt::ForwardButton | Qt::BackButton)) {
+ // "Forward" and "Back" are reserved for quickly navigating through the
+ // history. Double-clicking those buttons should be interpreted as two
+ // separate button presses. We arrive here for the second click, which
+ // we now react to just as we would for a singular click
+ Q_EMIT mouseButtonPressed(index.value_or(-1), event->button());
+ return false;
+ }
+
+ if (!index.has_value()) {
+ Q_EMIT doubleClickViewBackground(event->button());
+ return false;
+ }
+
// Expand item if desired - See Bug 295573
if (m_mouseDoubleClickAction != ActivateItemOnly) {
if (m_view && m_model && m_view->supportsItemExpanding() && m_model->isExpandable(index.value_or(-1))) {
}
}
- 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;
+ if (event->button() & ~Qt::LeftButton) {
+ return false;
+ }
+
+ if (m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced) {
+ return false;
}
- bool emitItemActivated = !(m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced) &&
- (event->button() & Qt::LeftButton) &&
- index.has_value() && index.value() < m_model->count();
+ const bool emitItemActivated = index.has_value() && index.value() < m_model->count() && !m_view->isAboveExpansionToggle(index.value(), pos);
if (emitItemActivated) {
+ if (!QApplication::keyboardModifiers()) {
+ m_selectionManager->clearSelection(); // The user does not want to manage/manipulate the item currently, only activate it.
+ }
Q_EMIT itemActivated(index.value());
}
return false;
}
-bool KItemListController::dragEnterEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
+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(pressedItem.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)
Q_UNUSED(transform)
return false;
}
-bool KItemListController::dragLeaveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
+bool KItemListController::dragLeaveEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform)
{
Q_UNUSED(event)
Q_UNUSED(transform)
m_view->setAutoScroll(false);
m_view->hideDropIndicator();
- KItemListWidget* widget = hoveredWidget();
+ KItemListWidget *widget = hoveredWidget();
if (widget) {
widget->setHovered(false);
Q_EMIT itemUnhovered(widget->index());
return false;
}
-bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
+bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform)
{
if (!m_model || !m_view) {
return false;
}
-
QUrl hoveredDir = m_model->directory();
- KItemListWidget* oldHoveredWidget = hoveredWidget();
+ KItemListWidget *oldHoveredWidget = hoveredWidget();
const QPointF pos = transform.map(event->pos());
- KItemListWidget* newHoveredWidget = widgetForPos(pos);
+ KItemListWidget *newHoveredWidget = widgetForDropPos(pos);
+ int index = -1;
if (oldHoveredWidget != newHoveredWidget) {
m_autoActivationTimer->stop();
droppingBetweenItems = (m_view->showDropIndicator(pos) >= 0);
}
- const int index = newHoveredWidget->index();
+ index = newHoveredWidget->index();
if (m_model->isDir(index)) {
hoveredDir = m_model->url(index);
}
if (!droppingBetweenItems) {
- if (m_model->supportsDropping(index)) {
- // Something has been dragged on an item.
- m_view->hideDropIndicator();
- if (!newHoveredWidget->isHovered()) {
- newHoveredWidget->setHovered(true);
- Q_EMIT itemHovered(index);
- }
+ // Something has been dragged on an item.
+ m_view->hideDropIndicator();
+ if (!newHoveredWidget->isHovered()) {
+ newHoveredWidget->setHovered(true);
+ Q_EMIT itemHovered(index);
+ }
- if (!m_autoActivationTimer->isActive() && m_autoActivationTimer->interval() >= 0) {
- m_autoActivationTimer->setProperty("index", index);
- m_autoActivationTimer->start();
- }
+ if (m_autoActivationEnabled && !m_autoActivationTimer->isActive() && m_model->canEnterOnHover(index)) {
+ m_autoActivationTimer->setProperty("index", index);
+ m_autoActivationTimer->start();
+ newHoveredWidget->startActivateSoonAnimation(m_autoActivationTimer->remainingTime());
}
+
} else {
m_autoActivationTimer->stop();
if (newHoveredWidget && newHoveredWidget->isHovered()) {
event->setDropAction(Qt::IgnoreAction);
event->ignore();
} else {
- event->setDropAction(event->proposedAction());
- event->accept();
+ if (m_model->supportsDropping(index)) {
+ event->setDropAction(event->proposedAction());
+ event->accept();
+ } else {
+ event->setDropAction(Qt::IgnoreAction);
+ event->ignore();
+ }
}
return false;
}
-bool KItemListController::dropEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
+bool KItemListController::dropEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform)
{
if (!m_view) {
return false;
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);
return true;
}
-bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
+bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent *event, const QTransform &transform)
{
Q_UNUSED(event)
Q_UNUSED(transform)
return false;
}
-bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
+bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent *event, const QTransform &transform)
{
Q_UNUSED(transform)
if (!m_model || !m_view) {
const auto oldHoveredExpansionWidgetIterator = std::find_if(visibleItemListWidgets.begin(), visibleItemListWidgets.end(), [](auto &widget) {
return widget->expansionAreaHovered();
});
- const auto oldHoveredExpansionWidget = oldHoveredExpansionWidgetIterator == visibleItemListWidgets.end() ?
- std::nullopt : std::make_optional(*oldHoveredExpansionWidgetIterator);
+ const auto oldHoveredExpansionWidget =
+ oldHoveredExpansionWidgetIterator == visibleItemListWidgets.end() ? std::nullopt : std::make_optional(*oldHoveredExpansionWidgetIterator);
const auto unhoverOldHoveredWidget = [&]() {
if (auto oldHoveredWidget = hoveredWidget(); oldHoveredWidget) {
// we also unhover any old icon+text hovers, in case the mouse movement from icon+text to expansion toggle is too fast (i.e. newHoveredWidget is never null between the transition)
unhoverOldHoveredWidget();
-
newHoveredWidget->setExpansionAreaHovered(true);
} else {
// make sure we unhover the old one first if old!=new
// 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) {
return false;
}
-bool KItemListController::hoverLeaveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
+bool KItemListController::hoverLeaveEvent(QGraphicsSceneHoverEvent *event, const QTransform &transform)
{
Q_UNUSED(event)
Q_UNUSED(transform)
}
const auto widgets = m_view->visibleItemListWidgets();
- for (KItemListWidget* widget : widgets) {
+ for (KItemListWidget *widget : widgets) {
+ widget->setPressed(false);
if (widget->isHovered()) {
widget->setHovered(false);
Q_EMIT itemUnhovered(widget->index());
return false;
}
-bool KItemListController::wheelEvent(QGraphicsSceneWheelEvent* event, const QTransform& transform)
+bool KItemListController::wheelEvent(QGraphicsSceneWheelEvent *event, const QTransform &transform)
{
Q_UNUSED(event)
Q_UNUSED(transform)
return false;
}
-bool KItemListController::resizeEvent(QGraphicsSceneResizeEvent* event, const QTransform& transform)
+bool KItemListController::resizeEvent(QGraphicsSceneResizeEvent *event, const QTransform &transform)
{
Q_UNUSED(event)
Q_UNUSED(transform)
return false;
}
-bool KItemListController::gestureEvent(QGestureEvent* event, const QTransform& transform)
+bool KItemListController::gestureEvent(QGestureEvent *event, const QTransform &transform)
{
if (!m_view) {
return false;
//we use this to get the right QWidget
//the only exception is a tap gesture with state GestureStarted, we need to reset some variable
if (!m_mousePress) {
- if (QGesture* tap = event->gesture(Qt::TapGesture)) {
- QTapGesture* tapGesture = static_cast<QTapGesture*>(tap);
+ if (QGesture *tap = event->gesture(Qt::TapGesture)) {
+ QTapGesture *tapGesture = static_cast<QTapGesture *>(tap);
if (tapGesture->state() == Qt::GestureStarted) {
tapTriggered(tapGesture, transform);
}
bool accepted = false;
- if (QGesture* tap = event->gesture(Qt::TapGesture)) {
- tapTriggered(static_cast<QTapGesture*>(tap), transform);
+ if (QGesture *tap = event->gesture(Qt::TapGesture)) {
+ tapTriggered(static_cast<QTapGesture *>(tap), transform);
accepted = true;
}
if (event->gesture(Qt::TapAndHoldGesture)) {
return accepted;
}
-bool KItemListController::touchBeginEvent(QTouchEvent* event, const QTransform& transform)
+bool KItemListController::touchBeginEvent(QTouchEvent *event, const QTransform &transform)
{
Q_UNUSED(event)
Q_UNUSED(transform)
return false;
}
-void KItemListController::tapTriggered(QTapGesture* tap, const QTransform& transform)
+void KItemListController::tapTriggered(QTapGesture *tap, const QTransform &transform)
{
static bool scrollerWasActive = false;
m_view->m_tapAndHoldIndicator->setActive(false);
}
- m_pressedMousePos = transform.map(tap->position());
- m_pressedIndex = m_view->itemAt(m_pressedMousePos);
-
+ const QPointF pressedMousePos = transform.map(tap->position());
+ m_pressedIndex = m_view->itemAt(pressedMousePos);
if (m_dragActionOrRightClick) {
- onPress(tap->hotSpot().toPoint(), tap->position().toPoint(), Qt::NoModifier, Qt::RightButton);
- onRelease(transform.map(tap->position()), Qt::NoModifier, Qt::RightButton, false);
m_dragActionOrRightClick = false;
- }
- else {
- onPress(tap->hotSpot().toPoint(), tap->position().toPoint(), Qt::NoModifier, Qt::LeftButton);
+ } else {
+ onPress(tap->position().toPoint(), Qt::NoModifier, Qt::LeftButton);
onRelease(transform.map(tap->position()), Qt::NoModifier, Qt::LeftButton, true);
}
m_isTouchEvent = false;
}
}
-void KItemListController::tapAndHoldTriggered(QGestureEvent* event, const QTransform& transform)
+void KItemListController::tapAndHoldTriggered(QGestureEvent *event, const QTransform &transform)
{
-
//the Qt TabAndHold gesture is triggerable with a mouse click, we don't want this
if (!m_isTouchEvent) {
return;
}
- const QTapAndHoldGesture* tap = static_cast<QTapAndHoldGesture*>(event->gesture(Qt::TapAndHoldGesture));
+ const QTapAndHoldGesture *tap = static_cast<QTapAndHoldGesture *>(event->gesture(Qt::TapAndHoldGesture));
if (tap->state() == Qt::GestureFinished) {
//if a pinch gesture is in progress we don't want a TabAndHold gesture
if (m_pinchGestureInProgress) {
return;
}
- m_pressedMousePos = transform.map(event->mapToGraphicsScene(tap->position()));
- m_pressedIndex = m_view->itemAt(m_pressedMousePos);
-
- if (m_pressedIndex.has_value() && !m_selectionManager->isSelected(m_pressedIndex.value())) {
- m_selectionManager->clearSelection();
- m_selectionManager->setSelected(m_pressedIndex.value());
- } else if (!m_pressedIndex.has_value()) {
+ const QPointF pressedMousePos = transform.map(event->mapToGraphicsScene(tap->position()));
+ m_pressedIndex = m_view->itemAt(pressedMousePos);
+ if (m_pressedIndex.has_value()) {
+ if (!m_selectionManager->isSelected(m_pressedIndex.value())) {
+ m_selectionManager->clearSelection();
+ m_selectionManager->setSelected(m_pressedIndex.value());
+ }
+ if (!m_selectionMode) {
+ Q_EMIT selectionModeChangeRequested(true);
+ }
+ } else {
m_selectionManager->clearSelection();
startRubberBand();
}
Q_EMIT scrollerStop();
- m_view->m_tapAndHoldIndicator->setStartPosition(m_pressedMousePos);
+ m_view->m_tapAndHoldIndicator->setStartPosition(pressedMousePos);
m_view->m_tapAndHoldIndicator->setActive(true);
m_dragActionOrRightClick = true;
}
}
-void KItemListController::pinchTriggered(QGestureEvent* event, const QTransform& transform)
+void KItemListController::pinchTriggered(QGestureEvent *event, const QTransform &transform)
{
Q_UNUSED(transform)
- const QPinchGesture* pinch = static_cast<QPinchGesture*>(event->gesture(Qt::PinchGesture));
+ const QPinchGesture *pinch = static_cast<QPinchGesture *>(event->gesture(Qt::PinchGesture));
const qreal sensitivityModifier = 0.2;
static qreal counter = 0;
}
}
-void KItemListController::swipeTriggered(QGestureEvent* event, const QTransform& transform)
+void KItemListController::swipeTriggered(QGestureEvent *event, const QTransform &transform)
{
Q_UNUSED(transform)
- const KTwoFingerSwipe* swipe = static_cast<KTwoFingerSwipe*>(event->gesture(m_swipeGesture));
+ const KTwoFingerSwipe *swipe = static_cast<KTwoFingerSwipe *>(event->gesture(m_swipeGesture));
if (!swipe) {
return;
}
}
-void KItemListController::twoFingerTapTriggered(QGestureEvent* event, const QTransform& transform)
+void KItemListController::twoFingerTapTriggered(QGestureEvent *event, const QTransform &transform)
{
- const KTwoFingerTap* twoTap = static_cast<KTwoFingerTap*>(event->gesture(m_twoFingerTapGesture));
+ const KTwoFingerTap *twoTap = static_cast<KTwoFingerTap *>(event->gesture(m_twoFingerTapGesture));
if (!twoTap) {
return;
}
if (twoTap->state() == Qt::GestureStarted) {
- m_pressedMousePos = transform.map(twoTap->pos());
- m_pressedIndex = m_view->itemAt(m_pressedMousePos);
+ const QPointF pressedMousePos = transform.map(twoTap->pos());
+ m_pressedIndex = m_view->itemAt(pressedMousePos);
if (m_pressedIndex.has_value()) {
- onPress(twoTap->screenPos().toPoint(), twoTap->pos().toPoint(), Qt::ControlModifier, Qt::LeftButton);
+ onPress(twoTap->pos().toPoint(), Qt::ControlModifier, Qt::LeftButton);
onRelease(transform.map(twoTap->pos()), Qt::ControlModifier, Qt::LeftButton, false);
}
-
}
}
-bool KItemListController::processEvent(QEvent* event, const QTransform& transform)
+bool KItemListController::processEvent(QEvent *event, const QTransform &transform)
{
if (!event) {
return false;
switch (event->type()) {
case QEvent::KeyPress:
- return keyPressEvent(static_cast<QKeyEvent*>(event));
+ return keyPressEvent(static_cast<QKeyEvent *>(event));
case QEvent::InputMethod:
- return inputMethodEvent(static_cast<QInputMethodEvent*>(event));
+ return inputMethodEvent(static_cast<QInputMethodEvent *>(event));
case QEvent::GraphicsSceneMousePress:
- return mousePressEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
+ return mousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event), QTransform());
case QEvent::GraphicsSceneMouseMove:
- return mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
+ return mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event), QTransform());
case QEvent::GraphicsSceneMouseRelease:
- return mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
+ return mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event), QTransform());
case QEvent::GraphicsSceneMouseDoubleClick:
- return mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
+ 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());
+ return wheelEvent(static_cast<QGraphicsSceneWheelEvent *>(event), QTransform());
case QEvent::GraphicsSceneDragEnter:
- return dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
+ return dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent *>(event), QTransform());
case QEvent::GraphicsSceneDragLeave:
- return dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
+ return dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event), QTransform());
case QEvent::GraphicsSceneDragMove:
- return dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
+ return dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event), QTransform());
case QEvent::GraphicsSceneDrop:
- return dropEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
+ return dropEvent(static_cast<QGraphicsSceneDragDropEvent *>(event), QTransform());
case QEvent::GraphicsSceneHoverEnter:
- return hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
+ return hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent *>(event), QTransform());
case QEvent::GraphicsSceneHoverMove:
- return hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
+ return hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent *>(event), QTransform());
case QEvent::GraphicsSceneHoverLeave:
- return hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
+ return hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent *>(event), QTransform());
case QEvent::GraphicsSceneResize:
- return resizeEvent(static_cast<QGraphicsSceneResizeEvent*>(event), transform);
+ return resizeEvent(static_cast<QGraphicsSceneResizeEvent *>(event), transform);
case QEvent::Gesture:
- return gestureEvent(static_cast<QGestureEvent*>(event), transform);
+ return gestureEvent(static_cast<QGestureEvent *>(event), transform);
case QEvent::TouchBegin:
- return touchBeginEvent(static_cast<QTouchEvent*>(event), transform);
+ return touchBeginEvent(static_cast<QTouchEvent *>(event), transform);
default:
break;
}
return;
}
- KItemListRubberBand* rubberBand = m_view->rubberBand();
+ KItemListRubberBand *rubberBand = m_view->rubberBand();
if (rubberBand->isActive()) {
const qreal diff = current - previous;
// TODO: Ideally just QCursor::pos() should be used as
return;
}
- const KItemListRubberBand* rubberBand = m_view->rubberBand();
+ const KItemListRubberBand *rubberBand = m_view->rubberBand();
const QPointF startPos = rubberBand->startPosition();
const QPointF endPos = rubberBand->endPosition();
QRectF rubberBandRect = QRectF(startPos, endPos).normalized();
if (!m_oldSelection.isEmpty()) {
// Clear the old selection that was available before the rubberband has
// 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) {
+ const bool shiftOrControlPressed = QApplication::keyboardModifiers() & Qt::ShiftModifier || QApplication::keyboardModifiers() & Qt::ControlModifier;
+ if (!shiftOrControlPressed && !m_selectionMode) {
m_oldSelection.clear();
}
}
// Select all visible items that intersect with the rubberband
const auto widgets = m_view->visibleItemListWidgets();
- for (const KItemListWidget* widget : widgets) {
+ for (const KItemListWidget *widget : widgets) {
const int index = widget->index();
const QRectF widgetRect = m_view->itemRect(index);
if (widgetRect.intersects(rubberBandRect)) {
- const QRectF iconRect = widget->iconRect().translated(widgetRect.topLeft());
- const QRectF textRect = widget->textRect().translated(widgetRect.topLeft());
- if (iconRect.intersects(rubberBandRect) || textRect.intersects(rubberBandRect)) {
+ // Select the full row intersecting with the rubberband rectangle
+ const QRectF selectionRect = widget->selectionRectFull().translated(widgetRect.topLeft());
+ if (selectionRect.intersects(rubberBandRect)) {
selectedItems.insert(index);
}
}
// Select all invisible items that intersect with the rubberband. Instead of
// iterating all items only the area which might be touched by the rubberband
// will be checked.
- const bool increaseIndex = scrollVertical ?
- startPos.y() > endPos.y(): startPos.x() > endPos.x();
+ const bool increaseIndex = scrollVertical ? startPos.y() > endPos.y() : startPos.x() > endPos.x();
int index = increaseIndex ? m_view->lastVisibleIndex() + 1 : m_view->firstVisibleIndex() - 1;
bool selectionFinished = false;
if (increaseIndex) {
++index;
- selectionFinished = (index >= m_model->count()) ||
- ( scrollVertical && widgetRect.top() > rubberBandRect.bottom()) ||
- (!scrollVertical && widgetRect.left() > rubberBandRect.right());
+ selectionFinished = (index >= m_model->count()) || (scrollVertical && widgetRect.top() > rubberBandRect.bottom())
+ || (!scrollVertical && widgetRect.left() > rubberBandRect.right());
} else {
--index;
- selectionFinished = (index < 0) ||
- ( scrollVertical && widgetRect.bottom() < rubberBandRect.top()) ||
- (!scrollVertical && widgetRect.right() < rubberBandRect.left());
+ selectionFinished = (index < 0) || (scrollVertical && widgetRect.bottom() < rubberBandRect.top())
+ || (!scrollVertical && widgetRect.right() < rubberBandRect.left());
}
} 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
// 2. all items inside the rubberband which have not been selected previously.
m_selectionManager->setSelectedItems(m_oldSelection ^ selectedItems);
- }
- else {
+ } else {
m_selectionManager->setSelectedItems(selectedItems + m_oldSelection);
}
}
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().
- QDrag* drag = new QDrag(QApplication::activeWindow());
+ QDrag *drag = new QDrag(QApplication::activeWindow());
drag->setMimeData(data);
const QPixmap pixmap = m_view->createDragPixmap(selectedItems);
QAccessible::updateAccessibility(&accessibilityEvent);
}
-KItemListWidget* KItemListController::hoveredWidget() const
+KItemListWidget *KItemListController::hoveredWidget() const
{
Q_ASSERT(m_view);
const auto widgets = m_view->visibleItemListWidgets();
- for (KItemListWidget* widget : widgets) {
+ for (KItemListWidget *widget : widgets) {
if (widget->isHovered()) {
return widget;
}
return nullptr;
}
-KItemListWidget* KItemListController::widgetForPos(const QPointF& pos) const
+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) {
+ 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;
+ }
+ }
+
+ return nullptr;
+}
+
+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);
+ if (widget->selectionRectCore().contains(mappedPos)) {
return widget;
}
}
void KItemListController::updateKeyboardAnchor()
{
- const bool validAnchor = m_keyboardAnchorIndex >= 0 &&
- m_keyboardAnchorIndex < m_model->count() &&
- keyboardAnchorPos(m_keyboardAnchorIndex) == m_keyboardAnchorPos;
+ const bool validAnchor =
+ m_keyboardAnchorIndex >= 0 && m_keyboardAnchorIndex < m_model->count() && keyboardAnchorPos(m_keyboardAnchorIndex) == m_keyboardAnchorPos;
if (!validAnchor) {
const int index = m_selectionManager->currentItem();
m_keyboardAnchorIndex = index;
return index;
}
+ const bool reversed = m_view->layoutDirection() == Qt::RightToLeft && m_view->scrollOrientation() == Qt::Vertical;
+
// Calculate the index of the last column inside the row of the current index
int lastColumnIndex = index;
- while (keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex)) {
+ while ((!reversed && keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex))
+ || (reversed && keyboardAnchorPos(lastColumnIndex + 1) < keyboardAnchorPos(lastColumnIndex))) {
++lastColumnIndex;
if (lastColumnIndex >= maxIndex) {
return index;
int nextRowIndex = lastColumnIndex + 1;
int searchIndex = nextRowIndex;
qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(nextRowIndex));
- while (searchIndex < maxIndex && keyboardAnchorPos(searchIndex + 1) > keyboardAnchorPos(searchIndex)) {
+ while (searchIndex < maxIndex
+ && ((!reversed && keyboardAnchorPos(searchIndex + 1) > keyboardAnchorPos(searchIndex))
+ || (reversed && keyboardAnchorPos(searchIndex + 1) < keyboardAnchorPos(searchIndex)))) {
++searchIndex;
const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex));
if (searchDiff < minDiff) {
return index;
}
+ const bool reversed = m_view->layoutDirection() == Qt::RightToLeft && m_view->scrollOrientation() == Qt::Vertical;
+
// Calculate the index of the first column inside the row of the current index
int firstColumnIndex = index;
- while (keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex)) {
+ while ((!reversed && keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex))
+ || (reversed && keyboardAnchorPos(firstColumnIndex - 1) > keyboardAnchorPos(firstColumnIndex))) {
--firstColumnIndex;
if (firstColumnIndex <= 0) {
return index;
int previousRowIndex = firstColumnIndex - 1;
int searchIndex = previousRowIndex;
qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(previousRowIndex));
- while (searchIndex > 0 && keyboardAnchorPos(searchIndex - 1) < keyboardAnchorPos(searchIndex)) {
+ while (searchIndex > 0
+ && ((!reversed && keyboardAnchorPos(searchIndex - 1) < keyboardAnchorPos(searchIndex))
+ || (reversed && keyboardAnchorPos(searchIndex - 1) > keyboardAnchorPos(searchIndex)))) {
--searchIndex;
const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex));
if (searchDiff < minDiff) {
}
}
-bool KItemListController::onPress(const QPoint& screenPos, const QPointF& pos, const Qt::KeyboardModifiers modifiers, const Qt::MouseButtons buttons)
+bool KItemListController::onPress(const QPointF &pos, const Qt::KeyboardModifiers modifiers, const Qt::MouseButtons buttons)
{
Q_EMIT mouseButtonPressed(m_pressedIndex.value_or(-1), buttons);
return true;
}
- if (m_view->isAboveExpansionToggle(m_pressedIndex.value_or(-1), m_pressedMousePos)) {
+ const QPointF pressedMousePos = m_view->transform().map(pos);
+
+ if (m_view->isAboveExpansionToggle(m_pressedIndex.value_or(-1), pressedMousePos)) {
m_selectionManager->endAnchoredSelection();
m_selectionManager->setCurrentItem(m_pressedIndex.value());
m_selectionManager->beginAnchoredSelection(m_pressedIndex.value());
return true;
}
- m_selectionTogglePressed = m_view->isAboveSelectionToggle(m_pressedIndex.value_or(-1), m_pressedMousePos);
+ m_selectionTogglePressed = m_view->isAboveSelectionToggle(m_pressedIndex.value_or(-1), pressedMousePos);
if (m_selectionTogglePressed) {
m_selectionManager->setSelected(m_pressedIndex.value(), 1, KItemListSelectionManager::Toggle);
// The previous anchored selection has been finished already in
}
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;
+ 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
// - open the context menu and perform an action for all selected items.
const bool shiftOrControlPressed = shiftPressed || controlPressed;
const bool pressedItemAlreadySelected = m_pressedIndex.has_value() && m_selectionManager->isSelected(m_pressedIndex.value());
- const bool clearSelection = m_selectionBehavior == SingleSelection ||
- (!shiftOrControlPressed && !pressedItemAlreadySelected);
-
+ const bool clearSelection = m_selectionBehavior == SingleSelection || (!shiftOrControlPressed && !pressedItemAlreadySelected);
// When this method returns false, a rubberBand selection is created using KItemListController::startRubberBand via the caller.
if (clearSelection) {
const int selectedItemsCount = m_selectionManager->selectedItems().count();
m_selectionManager->clearSelection();
// clear and bail when we got an existing multi-selection
- if (selectedItemsCount > 1 && m_pressedIndex.has_value()) {
+ 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 select it.
- m_selectionManager->setSelected(m_pressedIndex.value(), 1, KItemListSelectionManager::Toggle);
+ // An unselected item was clicked directly while deselecting multiple other items so we mark it "current".
m_selectionManager->setCurrentItem(m_pressedIndex.value());
m_selectionManager->beginAnchoredSelection(m_pressedIndex.value());
+ if (!leftClick) {
+ // We select the item here because this press is not meant to directly activate the item.
+ // We do not want to select items unless the user wants to edit them.
+ m_selectionManager->setSelected(m_pressedIndex.value(), 1, KItemListSelectionManager::Toggle);
+ }
+ }
+ if (leftClick) {
+ row->setPressed(true);
}
return true; // event handled, don't create rubber band
}
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.
// clear the selection in mouseReleaseEvent(), unless the items are dragged.
m_clearSelectionIfItemsAreNotDragged = true;
- if (m_selectionManager->selectedItems().count() == 1 && m_view->isAboveText(m_pressedIndex.value_or(-1), m_pressedMousePos)) {
+ if (m_selectionManager->selectedItems().count() == 1 && m_view->isAboveText(m_pressedIndex.value_or(-1), pressedMousePos)) {
Q_EMIT selectedItemTextPressed(m_pressedIndex.value_or(-1));
}
}
}
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();
+ KItemListRubberBand *rubberBand = m_view->rubberBand();
if (rubberBand->isActive()) {
disconnect(rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListController::slotRubberBandChanged);
rubberBand->setActive(false);
m_view->setAutoScroll(false);
}
+
+ if (!m_pressedIndex.has_value()) {
+ // We have a right-click in an empty region, don't create rubber band.
+ return true;
+ }
}
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));
+
+ 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
- // 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 {
- 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;
case SingleSelection:
- m_selectionManager->setSelected(m_pressedIndex.value());
+ if (!leftClick || shiftOrControlPressed || (!singleClickActivation && !m_singleClickActivationEnforced)) {
+ m_selectionManager->setSelected(m_pressedIndex.value());
+ }
break;
case MultiSelection:
// 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);
}
} else if (!shiftPressed || !m_selectionManager->isAnchoredSelectionActive()) {
// Select the pressed item and start a new anchored selection
- m_selectionManager->setSelected(m_pressedIndex.value(), 1, KItemListSelectionManager::Select);
+ if (!leftClick || shiftOrControlPressed || (!singleClickActivation && !m_singleClickActivationEnforced)) {
+ m_selectionManager->setSelected(m_pressedIndex.value(), 1, KItemListSelectionManager::Select);
+ }
m_selectionManager->beginAnchoredSelection(m_pressedIndex.value());
}
break;
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;
}
-bool KItemListController::onRelease(const QPointF& pos, const Qt::KeyboardModifiers modifiers, const Qt::MouseButtons buttons, bool touch)
+bool KItemListController::onRelease(const QPointF &pos, const Qt::KeyboardModifiers modifiers, const Qt::MouseButtons buttons, bool touch)
{
- const bool isAboveSelectionToggle = m_view->isAboveSelectionToggle(m_pressedIndex.value_or(-1), m_pressedMousePos);
+ const QPointF pressedMousePos = pos;
+ const bool isAboveSelectionToggle = m_view->isAboveSelectionToggle(m_pressedIndex.value_or(-1), pressedMousePos);
if (isAboveSelectionToggle) {
m_selectionTogglePressed = false;
return true;
}
const bool controlPressed = modifiers & Qt::ControlModifier;
- const bool shiftOrControlPressed = modifiers & Qt::ShiftModifier ||
- controlPressed;
+ const bool shiftOrControlPressed = modifiers & Qt::ShiftModifier || controlPressed;
const std::optional<int> index = m_view->itemAt(pos);
- KItemListRubberBand* rubberBand = m_view->rubberBand();
+ KItemListRubberBand *rubberBand = m_view->rubberBand();
bool rubberBandRelease = false;
if (rubberBand->isActive()) {
disconnect(rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListController::slotRubberBandChanged);
} else {
const bool singleClickActivation = m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced;
if (!singleClickActivation) {
- emitItemActivated = touch;
+ emitItemActivated = touch && !m_selectionMode;
} else {
// activate on single click only if we didn't come from a rubber band release
emitItemActivated = !rubberBandRelease;
}
}
- m_pressedMousePos = QPointF();
+ m_pressedMouseGlobalPos = QPointF();
m_pressedIndex = std::nullopt;
m_clearSelectionIfItemsAreNotDragged = false;
return false;
void KItemListController::startRubberBand()
{
if (m_selectionBehavior == MultiSelection) {
- QPointF startPos = m_pressedMousePos;
+ QPoint startPos = m_view->transform().map(m_view->scene()->views().first()->mapFromGlobal(m_pressedMouseGlobalPos.toPoint()));
if (m_view->scrollOrientation() == Qt::Vertical) {
startPos.ry() += m_view->scrollOffset();
- if (m_view->itemSize().width() < 0) {
- // Use a special rubberband for views that have only one column and
- // expand the rubberband to use the whole width of the view.
- startPos.setX(0);
- }
} else {
startPos.rx() += m_view->scrollOffset();
}
m_oldSelection = m_selectionManager->selectedItems();
- KItemListRubberBand* rubberBand = m_view->rubberBand();
+ KItemListRubberBand *rubberBand = m_view->rubberBand();
rubberBand->setStartPosition(startPos);
rubberBand->setEndPosition(startPos);
rubberBand->setActive(true);
m_scrollerIsScrolling = false;
}
}
+
+#include "moc_kitemlistcontroller.cpp"