Fixed the QTreeView expansion/collpasing when animated
authorThierry Bastian <thierryb@filewave.com>
Wed, 29 Aug 2012 12:24:40 +0000 (14:24 +0200)
committerQt by Nokia <qt-info@nokia.com>
Wed, 29 Aug 2012 18:40:17 +0000 (20:40 +0200)
If you had a QTreeView with expandable items, if you tried to expand and while
the animation was still running you'd try to collpase the node,
the display would be completely broken: the items below that items would
not be visible any more except for a fraction of a second when expanding
or collapsing it again.
The problem is in the fact that when starting an animation the QTreeView
stores the state before animating. And it does that even if an animation
is already running. So the stateBeforeAnimation becomes AnimatingState and
when the animation finishes, AnimatingState is the state that is restored
breaking the painting.
Unit test is included.

Change-Id: I015212c1ed8962e6df705655099a5660f195caf3
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
Reviewed-by: Stephen Kelly <stephen.kelly@kdab.com>
src/widgets/itemviews/qtreeview.cpp
tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp

index 2a4ffb4..9477301 100644 (file)
@@ -2892,7 +2892,9 @@ void QTreeViewPrivate::expand(int item, bool emitSignal)
     if (emitSignal && animationsEnabled)
         prepareAnimatedOperation(item, QVariantAnimation::Forward);
 #endif //QT_NO_ANIMATION
-    stateBeforeAnimation = state;
+     //if already animating, stateBeforeAnimation is set to the correct value
+    if (state != QAbstractItemView::AnimatingState)
+        stateBeforeAnimation = state;
     q->setState(QAbstractItemView::ExpandingState);
     const QModelIndex index = viewItems.at(item).index;
     storeExpanded(index);
@@ -2971,7 +2973,9 @@ void QTreeViewPrivate::collapse(int item, bool emitSignal)
         prepareAnimatedOperation(item, QVariantAnimation::Backward);
 #endif //QT_NO_ANIMATION
 
-    stateBeforeAnimation = state;
+    //if already animating, stateBeforeAnimation is set to the correct value
+    if (state != QAbstractItemView::AnimatingState)
+        stateBeforeAnimation = state;
     q->setState(QAbstractItemView::CollapsingState);
     expandedIndexes.erase(it);
     viewItems[item].expanded = false;
index f15a32e..4d4f4a4 100644 (file)
@@ -70,8 +70,10 @@ static void initStandardTreeModel(QStandardItemModel *model)
     model->insertRow(2, item);
 }
 
+class tst_QTreeView;
 struct PublicView : public QTreeView
 {
+    friend class tst_QTreeView;
     inline void executeDelayedItemsLayout()
     { QTreeView::executeDelayedItemsLayout(); }
 
@@ -170,6 +172,9 @@ private slots:
     void expandAndCollapse();
     void expandAndCollapseAll();
     void expandWithNoChildren();
+#ifndef QT_NO_ANIMATION
+    void quickExpandCollapse();
+#endif
     void keyboardNavigation();
     void headerSections();
     void moveCursor_data();
@@ -3546,8 +3551,6 @@ void tst_QTreeView::task230123_setItemsExpandable()
 
     QTest::keyClick(&tree, Qt::Key_Left);
     QVERIFY(!root.isExpanded());
-
-
 }
 
 void tst_QTreeView::task202039_closePersistentEditor()
@@ -4146,5 +4149,39 @@ void tst_QTreeView::taskQTBUG_18539_emitLayoutChanged()
     QCOMPARE(afterRISpy.size(), 0);
 }
 
+#ifndef QT_NO_ANIMATION
+void tst_QTreeView::quickExpandCollapse()
+{
+    //this unit tests makes sure the state after the animation is restored correctly
+    //after starting a 2nd animation while the first one was still on-going
+    //this tests that the stateBeforeAnimation is not set to AnimatingState
+    PublicView tree;
+    tree.setAnimated(true);
+    QStandardItemModel model;
+    QStandardItem *root = new QStandardItem("root");
+    root->appendRow(new QStandardItem("subnode"));
+    model.appendRow(root);
+    tree.setModel(&model);
+
+    QModelIndex rootIndex = root->index();
+    QVERIFY(rootIndex.isValid());
+
+    tree.show();
+    QTest::qWaitForWindowShown(&tree);
+
+    int initialState = tree.state();
+
+    tree.expand(rootIndex);
+    QCOMPARE(tree.state(), (int)PublicView::AnimatingState);
+
+    tree.collapse(rootIndex);
+    QCOMPARE(tree.state(), (int)PublicView::AnimatingState);
+
+    QTest::qWait(500); //the animation lasts for 250ms max so 500 should be enough
+
+    QCOMPARE(tree.state(), initialState);
+}
+#endif
+
 QTEST_MAIN(tst_QTreeView)
 #include "tst_qtreeview.moc"