QAbstractItemView: don't block dragging after double click

After d6551fe12520 it was no longer possible to start a drag with a
double click (where the first click selects an item, and the second
press+move starts the drag). Resetting the pressedItem variable to block
the emission of the clicked() signal had this unwanted side effect.

Instead, use an explicit boolean to store that the next release event
will be the result of a double click, so that the clicked() signal is not
emitted again (preventing the double-emission was the purpose of change
d6551fe12520).

Task-number: QTBUG-77771
Fixes: QTBUG-94087
Change-Id: I082c5169d89eb980dcd7985ef3d302b6ff060fb9
Reviewed-by: Samuel Gaist <samuel.gaist@idiap.ch>
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
Reviewed-by: Olivier BARTHELEMY <perso.olivier.barthelemy@gmail.com>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
(cherry picked from commit 17c1ebf8bfd254ff75cc55e335d1c1fb01da547f)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Volker Hilsheimer 2021-05-28 16:32:52 +02:00 committed by Qt Cherry-pick Bot
parent 03f0a26e84
commit cc02da23a8
4 changed files with 102 additions and 3 deletions

View File

@ -89,6 +89,7 @@ QAbstractItemViewPrivate::QAbstractItemViewPrivate()
pressedModifiers(Qt::NoModifier),
pressedPosition(QPoint(-1, -1)),
pressedAlreadySelected(false),
releaseFromDoubleClick(false),
viewportEnteredNeeded(false),
state(QAbstractItemView::NoState),
stateBeforeAnimation(QAbstractItemView::NoState),
@ -1913,6 +1914,8 @@ void QAbstractItemView::mouseMoveEvent(QMouseEvent *event)
void QAbstractItemView::mouseReleaseEvent(QMouseEvent *event)
{
Q_D(QAbstractItemView);
const bool releaseFromDoubleClick = d->releaseFromDoubleClick;
d->releaseFromDoubleClick = false;
QPoint pos = event->position().toPoint();
QPersistentModelIndex index = indexAt(pos);
@ -1925,7 +1928,7 @@ void QAbstractItemView::mouseReleaseEvent(QMouseEvent *event)
return;
}
bool click = (index == d->pressedIndex && index.isValid());
bool click = (index == d->pressedIndex && index.isValid() && !releaseFromDoubleClick);
bool selectedClicked = click && (event->button() == Qt::LeftButton) && d->pressedAlreadySelected;
EditTrigger trigger = (selectedClicked ? SelectedClicked : NoEditTriggers);
const bool edited = click ? edit(index, trigger, event) : false;
@ -1980,7 +1983,7 @@ void QAbstractItemView::mouseDoubleClickEvent(QMouseEvent *event)
if ((event->button() == Qt::LeftButton) && !edit(persistent, DoubleClicked, event)
&& !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this))
emit activated(persistent);
d->pressedIndex = QModelIndex();
d->releaseFromDoubleClick = true;
}
#if QT_CONFIG(draganddrop)

View File

@ -371,6 +371,7 @@ public:
Qt::KeyboardModifiers pressedModifiers;
QPoint pressedPosition;
bool pressedAlreadySelected;
bool releaseFromDoubleClick;
//forces the next mouseMoveEvent to send the viewportEntered signal
//if the mouse is over the viewport and not over an item

View File

@ -1992,7 +1992,7 @@ void QTreeView::mouseDoubleClickEvent(QMouseEvent *event)
if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this))
emit activated(persistent);
d->pressedIndex = QModelIndex();
d->releaseFromDoubleClick = true;
d->executePostedLayout(); // we need to make sure viewItems is updated
if (d->itemsExpandable
&& d->expandsOnDoubleClick

View File

@ -154,6 +154,8 @@ private slots:
void checkFocusAfterActivationChanges_data();
void checkFocusAfterActivationChanges();
void dragSelectAfterNewPress();
void dragWithSecondClick_data();
void dragWithSecondClick();
void selectionCommand_data();
void selectionCommand();
private:
@ -2600,6 +2602,99 @@ void tst_QAbstractItemView::dragSelectAfterNewPress()
QVERIFY(selected.contains(model.index(i, 0)));
}
void tst_QAbstractItemView::dragWithSecondClick_data()
{
QTest::addColumn<QString>("viewClass");
QTest::addColumn<bool>("doubleClick");
for (QString viewClass : {"QListView", "QTreeView"}) {
QTest::addRow("DoubleClick") << viewClass << true;
QTest::addRow("Two Single Clicks") << viewClass << false;
}
}
// inject the ability to record which indexes get dragged into any QAbstractItemView class
struct DragRecorder
{
virtual ~DragRecorder() = default;
bool dragStarted = false;
QModelIndexList draggedIndexes;
QAbstractItemView *view;
};
template<class ViewClass>
class DragRecorderView : public ViewClass, public DragRecorder
{
public:
DragRecorderView()
{ view = this; }
protected:
void startDrag(Qt::DropActions) override
{
draggedIndexes = ViewClass::selectedIndexes();
dragStarted = true;
}
};
void tst_QAbstractItemView::dragWithSecondClick()
{
QFETCH(QString, viewClass);
QFETCH(bool, doubleClick);
QStandardItemModel model;
QStandardItem *parentItem = model.invisibleRootItem();
for (int i = 0; i < 10; ++i) {
QStandardItem *item = new QStandardItem(QString("item %0").arg(i));
item->setDragEnabled(true);
item->setEditable(false);
parentItem->appendRow(item);
}
std::unique_ptr<DragRecorder> dragRecorder;
if (viewClass == "QTreeView")
dragRecorder.reset(new DragRecorderView<QTreeView>);
else if (viewClass == "QListView")
dragRecorder.reset(new DragRecorderView<QListView>);
QAbstractItemView *view = dragRecorder->view;
view->setModel(&model);
view->setFixedSize(160, 650); // Minimum width for windows with frame on Windows 8
view->setSelectionMode(QAbstractItemView::MultiSelection);
view->setDragDropMode(QAbstractItemView::InternalMove);
centerOnScreen(view);
moveCursorAway(view);
view->show();
QVERIFY(QTest::qWaitForWindowExposed(view));
QModelIndex index0 = model.index(0, 0);
QModelIndex index1 = model.index(1, 0);
// Select item 0 using a single click
QTest::mouseClick(view->viewport(), Qt::LeftButton, Qt::NoModifier,
view->visualRect(index0).center());
QCOMPARE(view->currentIndex(), index0);
if (doubleClick) {
// press on same item within the double click interval
QTest::mouseDClick(view->viewport(), Qt::LeftButton, Qt::NoModifier,
view->visualRect(index0).center());
} else {
// or on different item with a slow second press
QTest::mousePress(view->viewport(), Qt::LeftButton, Qt::NoModifier,
view->visualRect(index1).center());
}
// then drag far enough with left button held
const QPoint dragTo = view->visualRect(index1).center()
+ QPoint(2 * QApplication::startDragDistance(),
2 * QApplication::startDragDistance());
QMouseEvent mouseMoveEvent(QEvent::MouseMove, dragTo,
Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
QVERIFY(QApplication::sendEvent(view->viewport(), &mouseMoveEvent));
// twice since the view will first enter dragging state, then start the drag
// (not necessary to actually move the mouse)
QVERIFY(QApplication::sendEvent(view->viewport(), &mouseMoveEvent));
QVERIFY(dragRecorder->dragStarted);
QTest::mouseRelease(view->viewport(), Qt::LeftButton, Qt::NoModifier, dragTo);
}
void tst_QAbstractItemView::selectionCommand_data()
{
QTest::addColumn<QAbstractItemView::SelectionMode>("selectionMode");