From 3dbc4ad00f809b110f143f2473c230bc5c481391 Mon Sep 17 00:00:00 2001 From: Leonardo Sobral Cunha Date: Fri, 2 Oct 2009 17:13:11 +0200 Subject: In QAbstractAnimation::setState setCurrentTime when animation pauses If we want to avoid timer ticks in QPauseAnimation, We need to update the current time when an animation pauses, for being able to resume correctly. Reviewed-by: thierry --- src/corelib/animation/qabstractanimation.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index 2769040..a4a8853 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -292,22 +292,23 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) switch (state) { case QAbstractAnimation::Paused: + q->setCurrentTime(currentTime); + if (!guard) + return; + QUnifiedTimer::instance()->unregisterAnimation(q); + break; case QAbstractAnimation::Running: - //this ensures that the value is updated now that the animation is running - if(oldState == QAbstractAnimation::Stopped) { + // this ensures that the value is updated now that the animation is running + if (oldState == QAbstractAnimation::Stopped) { q->setCurrentTime(currentTime); if (!guard) return; } - // Register timer if our parent is not running. if (state == QAbstractAnimation::Running) { - if (!group || group->state() == QAbstractAnimation::Stopped) { + // Register timer if our parent is not running + if (!group || group->state() == QAbstractAnimation::Stopped) QUnifiedTimer::instance()->registerAnimation(q); - } - } else { - //new state is paused - QUnifiedTimer::instance()->unregisterAnimation(q); } break; case QAbstractAnimation::Stopped: -- cgit v0.12 From 232212660535bddee054f3c60b7e8a9dfcc8c582 Mon Sep 17 00:00:00 2001 From: Leonardo Sobral Cunha Date: Tue, 6 Oct 2009 19:11:06 +0200 Subject: Removed unused function QUnifiedTimer::elapsedTime from animation api private Reviewed-by: thierry --- src/corelib/animation/qabstractanimation_p.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index 1a79f40..8066d32 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -134,7 +134,6 @@ public: */ void setConsistentTiming(bool consistent) { consistentTiming = consistent; } - int elapsedTime() const { return lastTick; } protected: void timerEvent(QTimerEvent *); -- cgit v0.12 From cd32bfdd74a9615d28670a197296821d7cf2844d Mon Sep 17 00:00:00 2001 From: Leonardo Sobral Cunha Date: Tue, 6 Oct 2009 19:00:34 +0200 Subject: Avoids timer ticks when there are only pause animations running When there are only pause animations running, the timer will stop and restart when the closest pause animation finishes. While there are only pause animations running, there are no additional timer ticks, but if there is at least one animation running that is not a group or a pause, then the global animation timer will restore it's update interval. Includes a new auto-test for the QPauseAnimation class. Task-number: QT-941 Reviewed-by: thierry Reviewed-by: janarve --- src/corelib/animation/qabstractanimation.cpp | 187 +++++++--- src/corelib/animation/qabstractanimation_p.h | 41 ++- src/corelib/animation/qanimationgroup_p.h | 4 +- src/corelib/animation/qpauseanimation.cpp | 1 + tests/auto/auto.pro | 1 + tests/auto/qpauseanimation/qpauseanimation.pro | 5 + tests/auto/qpauseanimation/tst_qpauseanimation.cpp | 392 +++++++++++++++++++++ 7 files changed, 584 insertions(+), 47 deletions(-) create mode 100644 tests/auto/qpauseanimation/qpauseanimation.pro create mode 100644 tests/auto/qpauseanimation/tst_qpauseanimation.cpp diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index a4a8853..9e50784 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -144,6 +144,7 @@ #include "qabstractanimation.h" #include "qanimationgroup.h" + #include #include "qabstractanimation_p.h" @@ -176,7 +177,8 @@ Q_GLOBAL_STATIC(QThreadStorage, unifiedTimer) QUnifiedTimer::QUnifiedTimer() : QObject(), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL), - currentAnimationIdx(0), consistentTiming(false) + currentAnimationIdx(0), consistentTiming(false), isPauseTimerActive(false), + runningLeafAnimations(0) { } @@ -192,50 +194,96 @@ QUnifiedTimer *QUnifiedTimer::instance() return inst; } +void QUnifiedTimer::ensureTimerUpdate(QAbstractAnimation *animation) +{ + if (isPauseTimerActive) { + updateAnimationsTime(); + } else { + // this code is needed when ensureTimerUpdate is called from setState because we update + // the currentTime when an animation starts running (otherwise we could remove it) + animation->setCurrentTime(animation->currentTime()); + } +} + +void QUnifiedTimer::updateAnimationsTime() +{ + // this is simply the time we last received a tick + const int oldLastTick = lastTick; + // ignore consistentTiming in case the pause timer is active + if (consistentTiming && !isPauseTimerActive) + lastTick = oldLastTick + timingInterval; + else + lastTick = time.elapsed(); + const int delta = lastTick - oldLastTick; + + //we make sure we only call update time if the time has actually changed + //it might happen in some cases that the time doesn't change because events are delayed + //when the CPU load is high + if (delta) { + for (currentAnimationIdx = 0; currentAnimationIdx < animations.count(); ++currentAnimationIdx) { + QAbstractAnimation *animation = animations.at(currentAnimationIdx); + int elapsed = QAbstractAnimationPrivate::get(animation)->totalCurrentTime + + (animation->direction() == QAbstractAnimation::Forward ? delta : -delta); + animation->setCurrentTime(elapsed); + } + currentAnimationIdx = 0; + } +} + +void QUnifiedTimer::restartAnimationTimer() +{ + if (runningLeafAnimations == 0 && !runningPauseAnimations.isEmpty()) { + int closestTimeToFinish = closestPauseAnimationTimeToFinish(); + animationTimer.start(closestTimeToFinish, this); + isPauseTimerActive = true; + } else if (!animationTimer.isActive() || isPauseTimerActive) { + animationTimer.start(timingInterval, this); + isPauseTimerActive = false; + } +} + void QUnifiedTimer::timerEvent(QTimerEvent *event) { if (event->timerId() == startStopAnimationTimer.timerId()) { startStopAnimationTimer.stop(); + //we transfer the waiting animations into the "really running" state animations += animationsToStart; animationsToStart.clear(); if (animations.isEmpty()) { animationTimer.stop(); - } else if (!animationTimer.isActive()) { - animationTimer.start(timingInterval, this); - lastTick = 0; - time.start(); - } - } else if (event->timerId() == animationTimer.timerId()) { - //this is simply the time we last received a tick - const int oldLastTick = lastTick; - lastTick = consistentTiming ? oldLastTick + timingInterval : time.elapsed(); - - //we make sure we only call update time if the time has actually changed - //it might happen in some cases that the time doesn't change because events are delayed - //when the CPU load is high - if (const int delta = lastTick - oldLastTick) { - for (currentAnimationIdx = 0; currentAnimationIdx < animations.count(); ++currentAnimationIdx) { - QAbstractAnimation *animation = animations.at(currentAnimationIdx); - int elapsed = QAbstractAnimationPrivate::get(animation)->totalCurrentTime - + (animation->direction() == QAbstractAnimation::Forward ? delta : -delta); - animation->setCurrentTime(elapsed); + isPauseTimerActive = false; + // invalidate the start reference time + time = QTime(); + } else { + restartAnimationTimer(); + if (!time.isValid()) { + lastTick = 0; + time.start(); } - currentAnimationIdx = 0; } + } else if (event->timerId() == animationTimer.timerId()) { + // update current time on all top level animations + updateAnimationsTime(); + restartAnimationTimer(); } } -void QUnifiedTimer::registerAnimation(QAbstractAnimation *animation) +void QUnifiedTimer::registerAnimation(QAbstractAnimation *animation, bool isTopLevel) { - Q_ASSERT(!QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer); - QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer = true; - animationsToStart << animation; - startStopAnimationTimer.start(STARTSTOP_TIMER_DELAY, this); + registerRunningAnimation(animation); + if (isTopLevel) { + Q_ASSERT(!QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer); + QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer = true; + animationsToStart << animation; + startStopAnimationTimer.start(STARTSTOP_TIMER_DELAY, this); + } } void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) { + unregisterRunningAnimation(animation); + if (!QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer) return; @@ -253,6 +301,46 @@ void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer = false; } +void QUnifiedTimer::registerRunningAnimation(QAbstractAnimation *animation) +{ + if (QAbstractAnimationPrivate::get(animation)->isGroup) + return; + + if (QAbstractAnimationPrivate::get(animation)->isPause) + runningPauseAnimations << animation; + else + runningLeafAnimations++; +} + +void QUnifiedTimer::unregisterRunningAnimation(QAbstractAnimation *animation) +{ + if (QAbstractAnimationPrivate::get(animation)->isGroup) + return; + + if (QAbstractAnimationPrivate::get(animation)->isPause) + runningPauseAnimations.removeOne(animation); + else + runningLeafAnimations--; +} + +int QUnifiedTimer::closestPauseAnimationTimeToFinish() +{ + int closestTimeToFinish = INT_MAX; + for (int i = 0; i < runningPauseAnimations.size(); ++i) { + QAbstractAnimation *animation = runningPauseAnimations.at(i); + int timeToFinish; + + if (animation->direction() == QAbstractAnimation::Forward) + timeToFinish = animation->totalDuration() - QAbstractAnimationPrivate::get(animation)->totalCurrentTime; + else + timeToFinish = QAbstractAnimationPrivate::get(animation)->totalCurrentTime; + + if (timeToFinish < closestTimeToFinish) + closestTimeToFinish = timeToFinish; + } + return closestTimeToFinish; +} + void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) { Q_Q(QAbstractAnimation); @@ -270,7 +358,7 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) //here we reset the time if needed //we don't call setCurrentTime because this might change the way the animation //behaves: changing the state or changing the current value - totalCurrentTime = currentTime =(direction == QAbstractAnimation::Forward) ? + totalCurrentTime = currentTime = (direction == QAbstractAnimation::Forward) ? 0 : (loopCount == -1 ? q->duration() : q->totalDuration()); } @@ -292,23 +380,31 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) switch (state) { case QAbstractAnimation::Paused: - q->setCurrentTime(currentTime); + if (hasRegisteredTimer) + // currentTime needs to be updated if pauseTimer is active + QUnifiedTimer::instance()->ensureTimerUpdate(q); if (!guard) return; QUnifiedTimer::instance()->unregisterAnimation(q); break; case QAbstractAnimation::Running: - // this ensures that the value is updated now that the animation is running - if (oldState == QAbstractAnimation::Stopped) { - q->setCurrentTime(currentTime); - if (!guard) - return; - } + { + bool isTopLevel = !group || group->state() == QAbstractAnimation::Stopped; + + // this ensures that the value is updated now that the animation is running + if (oldState == QAbstractAnimation::Stopped) { + if (isTopLevel) + // currentTime needs to be updated if pauseTimer is active + QUnifiedTimer::instance()->ensureTimerUpdate(q); + if (!guard) + return; + } - if (state == QAbstractAnimation::Running) { - // Register timer if our parent is not running - if (!group || group->state() == QAbstractAnimation::Stopped) - QUnifiedTimer::instance()->registerAnimation(q); + // test needed in case we stop in the setCurrentTime inside ensureTimerUpdate (zero duration) + if (state == QAbstractAnimation::Running) { + // register timer if our parent is not running + QUnifiedTimer::instance()->registerAnimation(q, isTopLevel); + } } break; case QAbstractAnimation::Stopped: @@ -453,7 +549,6 @@ void QAbstractAnimation::setDirection(Direction direction) if (d->direction == direction) return; - d->direction = direction; if (state() == Stopped) { if (direction == Backward) { d->currentTime = duration(); @@ -463,7 +558,19 @@ void QAbstractAnimation::setDirection(Direction direction) d->currentLoop = 0; } } + + // the commands order below is important: first we need to setCurrentTime with the old direction, + // then update the direction on this and all children and finally restart the pauseTimer if needed + if (d->hasRegisteredTimer) + QUnifiedTimer::instance()->ensureTimerUpdate(this); + + d->direction = direction; updateDirection(direction); + + if (d->hasRegisteredTimer) + // needed to update the timer interval in case of a pause animation + QUnifiedTimer::instance()->restartAnimationTimer(); + emit directionChanged(direction); } @@ -660,7 +767,7 @@ void QAbstractAnimation::stop() /*! Pauses the animation. When the animation is paused, state() returns Paused. - The value of currentTime will remain unchanged until resume() or start() + The value of currentTime will remain unchanged until resume() or start() is called. If you want to continue from the current time, call resume(). \sa start(), state(), resume() diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index 8066d32..bef0499 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -70,12 +70,14 @@ public: QAbstractAnimationPrivate() : state(QAbstractAnimation::Stopped), direction(QAbstractAnimation::Forward), - deleteWhenStopped(false), totalCurrentTime(0), currentTime(0), loopCount(1), currentLoop(0), + deleteWhenStopped(false), hasRegisteredTimer(false), + isPause(false), + isGroup(false), group(0) { } @@ -89,7 +91,6 @@ public: QAbstractAnimation::State state; QAbstractAnimation::Direction direction; - bool deleteWhenStopped; void setState(QAbstractAnimation::State state); int totalCurrentTime; @@ -97,7 +98,10 @@ public: int loopCount; int currentLoop; + bool deleteWhenStopped; bool hasRegisteredTimer; + bool isPause; + bool isGroup; QAnimationGroup *group; @@ -115,14 +119,14 @@ public: //XXX this is needed by dui static Q_CORE_EXPORT QUnifiedTimer *instance(); - void registerAnimation(QAbstractAnimation *animation); + void registerAnimation(QAbstractAnimation *animation, bool isTopLevel); void unregisterAnimation(QAbstractAnimation *animation); //defines the timing interval. Default is DEFAULT_TIMER_INTERVAL void setTimingInterval(int interval) { timingInterval = interval; - if (animationTimer.isActive()) { + if (animationTimer.isActive() && !isPauseTimerActive) { //we changed the timing interval animationTimer.start(timingInterval, this); } @@ -134,21 +138,46 @@ public: */ void setConsistentTiming(bool consistent) { consistentTiming = consistent; } + /* + this is used for updating the currentTime of all animations in case the pause + timer is active or, otherwise, only of the animation passed as parameter. + */ + void ensureTimerUpdate(QAbstractAnimation *animation); + + /* + this will evaluate the need of restarting the pause timer in case there is still + some pause animations running. + */ + void restartAnimationTimer(); protected: void timerEvent(QTimerEvent *); private: - // timer used for all active animations + // timer used for all active (running) animations QBasicTimer animationTimer; - // timer used to delay the check if we should start/stop the global timer + // timer used to delay the check if we should start/stop the animation timer QBasicTimer startStopAnimationTimer; + QTime time; int lastTick; int timingInterval; int currentAnimationIdx; bool consistentTiming; + // bool to indicate that only pause animations are active + bool isPauseTimerActive; + QList animations, animationsToStart; + + // this is the count of running animations that are not a group neither a pause animation + int runningLeafAnimations; + QList runningPauseAnimations; + + void registerRunningAnimation(QAbstractAnimation *animation); + void unregisterRunningAnimation(QAbstractAnimation *animation); + + void updateAnimationsTime(); + int closestPauseAnimationTimeToFinish(); }; QT_END_NAMESPACE diff --git a/src/corelib/animation/qanimationgroup_p.h b/src/corelib/animation/qanimationgroup_p.h index 45eab58..bb1cfb3 100644 --- a/src/corelib/animation/qanimationgroup_p.h +++ b/src/corelib/animation/qanimationgroup_p.h @@ -68,7 +68,9 @@ class QAnimationGroupPrivate : public QAbstractAnimationPrivate Q_DECLARE_PUBLIC(QAnimationGroup) public: QAnimationGroupPrivate() - { } + { + isGroup = true; + } virtual void animationInsertedAt(int index) { Q_UNUSED(index) }; virtual void animationRemovedAt(int index); diff --git a/src/corelib/animation/qpauseanimation.cpp b/src/corelib/animation/qpauseanimation.cpp index 2fd12aa..d90f001 100644 --- a/src/corelib/animation/qpauseanimation.cpp +++ b/src/corelib/animation/qpauseanimation.cpp @@ -75,6 +75,7 @@ class QPauseAnimationPrivate : public QAbstractAnimationPrivate public: QPauseAnimationPrivate() : QAbstractAnimationPrivate(), duration(0) { + isPause = true; } int duration; diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index f3ecdae..60da6c7 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -234,6 +234,7 @@ SUBDIRS += \ qpainterpath \ qpalette \ qparallelanimationgroup \ + qpauseanimation \ qpathclipper \ qpen \ qpicture \ diff --git a/tests/auto/qpauseanimation/qpauseanimation.pro b/tests/auto/qpauseanimation/qpauseanimation.pro new file mode 100644 index 0000000..4599cf0 --- /dev/null +++ b/tests/auto/qpauseanimation/qpauseanimation.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT = core gui +SOURCES += tst_qpauseanimation.cpp + + diff --git a/tests/auto/qpauseanimation/tst_qpauseanimation.cpp b/tests/auto/qpauseanimation/tst_qpauseanimation.cpp new file mode 100644 index 0000000..0c742af --- /dev/null +++ b/tests/auto/qpauseanimation/tst_qpauseanimation.cpp @@ -0,0 +1,392 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include +#include + +#include + +//TESTED_CLASS=QPauseAnimation +//TESTED_FILES= + +class TestablePauseAnimation : public QPauseAnimation +{ + Q_OBJECT +public: + TestablePauseAnimation(QObject *parent = 0) + : QPauseAnimation(parent), + m_updateCurrentTimeCount(0) + { + } + + int m_updateCurrentTimeCount; +protected: + void updateCurrentTime(int currentTime) + { + //qDebug() << this << "update current time: " << currentTime; + QPauseAnimation::updateCurrentTime(currentTime); + ++m_updateCurrentTimeCount; + } +}; + +class tst_QPauseAnimation : public QObject +{ + Q_OBJECT +public: + tst_QPauseAnimation(); + virtual ~tst_QPauseAnimation(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private slots: + void changeDirectionWhileRunning(); + void noTimerUpdates_data(); + void noTimerUpdates(); + void mulitplePauseAnimations(); + void pauseAndPropertyAnimations(); + void pauseResume(); + void sequentialPauseGroup(); + void sequentialGroupWithPause(); + void multipleSequentialGroups(); + void zeroDuration(); +}; + +tst_QPauseAnimation::tst_QPauseAnimation() +{ +} + +tst_QPauseAnimation::~tst_QPauseAnimation() +{ +} + +void tst_QPauseAnimation::init() +{ + qRegisterMetaType("QAbstractAnimation::State"); + qRegisterMetaType("QAbstractAnimation::DeletionPolicy"); +} + +void tst_QPauseAnimation::cleanup() +{ +} + +void tst_QPauseAnimation::changeDirectionWhileRunning() +{ + QUnifiedTimer *timer = QUnifiedTimer::instance(); + timer->setConsistentTiming(true); + + TestablePauseAnimation animation; + animation.setDuration(400); + animation.start(); + QTest::qWait(100); + QVERIFY(animation.state() == QAbstractAnimation::Running); + animation.setDirection(QAbstractAnimation::Backward); + QTest::qWait(animation.totalDuration() + 50); + QVERIFY(animation.state() == QAbstractAnimation::Stopped); + + timer->setConsistentTiming(false); +} + +void tst_QPauseAnimation::noTimerUpdates_data() +{ + QTest::addColumn("duration"); + QTest::addColumn("loopCount"); + + QTest::newRow("0") << 200 << 1; + QTest::newRow("1") << 160 << 1; + QTest::newRow("2") << 160 << 2; + QTest::newRow("3") << 200 << 3; +} + +void tst_QPauseAnimation::noTimerUpdates() +{ + QUnifiedTimer *timer = QUnifiedTimer::instance(); + timer->setConsistentTiming(true); + + QFETCH(int, duration); + QFETCH(int, loopCount); + + TestablePauseAnimation animation; + animation.setDuration(duration); + animation.setLoopCount(loopCount); + animation.start(); + QTest::qWait(animation.totalDuration() + 100); + QVERIFY(animation.state() == QAbstractAnimation::Stopped); + QCOMPARE(animation.m_updateCurrentTimeCount, 2); + + timer->setConsistentTiming(false); +} + +void tst_QPauseAnimation::mulitplePauseAnimations() +{ + QUnifiedTimer *timer = QUnifiedTimer::instance(); + timer->setConsistentTiming(true); + + TestablePauseAnimation animation; + animation.setDuration(200); + + TestablePauseAnimation animation2; + animation2.setDuration(800); + + animation.start(); + animation2.start(); + QTest::qWait(animation.totalDuration() + 100); + QVERIFY(animation.state() == QAbstractAnimation::Stopped); + QVERIFY(animation2.state() == QAbstractAnimation::Running); + QCOMPARE(animation.m_updateCurrentTimeCount, 2); + QCOMPARE(animation2.m_updateCurrentTimeCount, 2); + + QTest::qWait(550); + QVERIFY(animation2.state() == QAbstractAnimation::Stopped); + QCOMPARE(animation2.m_updateCurrentTimeCount, 3); + + timer->setConsistentTiming(false); +} + +void tst_QPauseAnimation::pauseAndPropertyAnimations() +{ + QUnifiedTimer *timer = QUnifiedTimer::instance(); + timer->setConsistentTiming(true); + + TestablePauseAnimation pause; + pause.setDuration(200); + + QObject o; + o.setProperty("ole", 42); + + QPropertyAnimation animation(&o, "ole"); + animation.setEndValue(43); + + pause.start(); + + QTest::qWait(100); + animation.start(); + + QVERIFY(animation.state() == QAbstractAnimation::Running); + QVERIFY(pause.state() == QAbstractAnimation::Running); + QCOMPARE(pause.m_updateCurrentTimeCount, 2); + + QTest::qWait(animation.totalDuration() + 100); + + QVERIFY(animation.state() == QAbstractAnimation::Stopped); + QVERIFY(pause.state() == QAbstractAnimation::Stopped); + QVERIFY(pause.m_updateCurrentTimeCount > 3); + + timer->setConsistentTiming(false); +} + +void tst_QPauseAnimation::pauseResume() +{ + TestablePauseAnimation animation; + animation.setDuration(400); + animation.start(); + QVERIFY(animation.state() == QAbstractAnimation::Running); + QTest::qWait(200); + animation.pause(); + QVERIFY(animation.state() == QAbstractAnimation::Paused); + animation.start(); + QTest::qWait(250); + QVERIFY(animation.state() == QAbstractAnimation::Stopped); + QCOMPARE(animation.m_updateCurrentTimeCount, 3); +} + +void tst_QPauseAnimation::sequentialPauseGroup() +{ + QSequentialAnimationGroup group; + + TestablePauseAnimation animation1(&group); + animation1.setDuration(200); + TestablePauseAnimation animation2(&group); + animation2.setDuration(200); + TestablePauseAnimation animation3(&group); + animation3.setDuration(200); + + group.start(); + + QVERIFY(group.state() == QAbstractAnimation::Running); + QVERIFY(animation1.state() == QAbstractAnimation::Running); + QVERIFY(animation2.state() == QAbstractAnimation::Stopped); + QVERIFY(animation3.state() == QAbstractAnimation::Stopped); + + QTest::qWait(250); + + QVERIFY(group.state() == QAbstractAnimation::Running); + QVERIFY(animation1.state() == QAbstractAnimation::Stopped); + QCOMPARE(&animation2, group.currentAnimation()); + QVERIFY(animation2.state() == QAbstractAnimation::Running); + QVERIFY(animation3.state() == QAbstractAnimation::Stopped); + + QTest::qWait(250); + + QVERIFY(group.state() == QAbstractAnimation::Running); + QVERIFY(animation1.state() == QAbstractAnimation::Stopped); + QVERIFY(animation2.state() == QAbstractAnimation::Stopped); + QCOMPARE(&animation3, group.currentAnimation()); + QVERIFY(animation3.state() == QAbstractAnimation::Running); + + QTest::qWait(250); + + QVERIFY(group.state() == QAbstractAnimation::Stopped); + QVERIFY(animation1.state() == QAbstractAnimation::Stopped); + QVERIFY(animation2.state() == QAbstractAnimation::Stopped); + QVERIFY(animation3.state() == QAbstractAnimation::Stopped); + + QCOMPARE(animation1.m_updateCurrentTimeCount, 2); + QCOMPARE(animation2.m_updateCurrentTimeCount, 2); + QCOMPARE(animation3.m_updateCurrentTimeCount, 2); +} + +void tst_QPauseAnimation::sequentialGroupWithPause() +{ + QSequentialAnimationGroup group; + + QObject o; + o.setProperty("ole", 42); + + QPropertyAnimation animation(&o, "ole", &group); + animation.setEndValue(43); + TestablePauseAnimation pause(&group); + pause.setDuration(250); + + group.start(); + + QVERIFY(group.state() == QAbstractAnimation::Running); + QVERIFY(animation.state() == QAbstractAnimation::Running); + QVERIFY(pause.state() == QAbstractAnimation::Stopped); + + QTest::qWait(300); + + QVERIFY(group.state() == QAbstractAnimation::Running); + QVERIFY(animation.state() == QAbstractAnimation::Stopped); + QCOMPARE(&pause, group.currentAnimation()); + QVERIFY(pause.state() == QAbstractAnimation::Running); + + QTest::qWait(300); + + QVERIFY(group.state() == QAbstractAnimation::Stopped); + QVERIFY(animation.state() == QAbstractAnimation::Stopped); + QVERIFY(pause.state() == QAbstractAnimation::Stopped); + + QCOMPARE(pause.m_updateCurrentTimeCount, 2); +} + +void tst_QPauseAnimation::multipleSequentialGroups() +{ + QUnifiedTimer *timer = QUnifiedTimer::instance(); + timer->setConsistentTiming(true); + + QParallelAnimationGroup group; + group.setLoopCount(2); + + QSequentialAnimationGroup subgroup1(&group); + + QObject o; + o.setProperty("ole", 42); + + QPropertyAnimation animation(&o, "ole", &subgroup1); + animation.setEndValue(43); + animation.setDuration(300); + TestablePauseAnimation pause(&subgroup1); + pause.setDuration(200); + + QSequentialAnimationGroup subgroup2(&group); + + o.setProperty("ole2", 42); + QPropertyAnimation animation2(&o, "ole2", &subgroup2); + animation2.setEndValue(43); + animation2.setDuration(200); + TestablePauseAnimation pause2(&subgroup2); + pause2.setDuration(250); + + QSequentialAnimationGroup subgroup3(&group); + + TestablePauseAnimation pause3(&subgroup3); + pause3.setDuration(400); + + o.setProperty("ole3", 42); + QPropertyAnimation animation3(&o, "ole3", &subgroup3); + animation3.setEndValue(43); + animation3.setDuration(200); + + QSequentialAnimationGroup subgroup4(&group); + + TestablePauseAnimation pause4(&subgroup4); + pause4.setDuration(310); + + TestablePauseAnimation pause5(&subgroup4); + pause5.setDuration(60); + + group.start(); + + QVERIFY(group.state() == QAbstractAnimation::Running); + QVERIFY(subgroup1.state() == QAbstractAnimation::Running); + QVERIFY(subgroup2.state() == QAbstractAnimation::Running); + QVERIFY(subgroup3.state() == QAbstractAnimation::Running); + QVERIFY(subgroup4.state() == QAbstractAnimation::Running); + + QTest::qWait(group.totalDuration() + 100); + + QVERIFY(group.state() == QAbstractAnimation::Stopped); + QVERIFY(subgroup1.state() == QAbstractAnimation::Stopped); + QVERIFY(subgroup2.state() == QAbstractAnimation::Stopped); + QVERIFY(subgroup3.state() == QAbstractAnimation::Stopped); + QVERIFY(subgroup4.state() == QAbstractAnimation::Stopped); + + QCOMPARE(pause5.m_updateCurrentTimeCount, 4); + + timer->setConsistentTiming(false); +} + +void tst_QPauseAnimation::zeroDuration() +{ + TestablePauseAnimation animation; + animation.start(); + QTest::qWait(animation.totalDuration() + 100); + QVERIFY(animation.state() == QAbstractAnimation::Stopped); + QCOMPARE(animation.m_updateCurrentTimeCount, 1); +} + +QTEST_MAIN(tst_QPauseAnimation) +#include "tst_qpauseanimation.moc" -- cgit v0.12 From df886d78631a8a7ea3aa8fdbb9da18d5a3387ed1 Mon Sep 17 00:00:00 2001 From: Leonardo Sobral Cunha Date: Wed, 7 Oct 2009 16:36:55 +0200 Subject: In QParallelAnimationGroup, only stop the children if they arent stopped The previous code was settingCurrentTime on all animatios, even on those that had already finished long ago. Reviewed-by: thierry --- src/corelib/animation/qparallelanimationgroup.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/corelib/animation/qparallelanimationgroup.cpp b/src/corelib/animation/qparallelanimationgroup.cpp index 2812854..0a04c14 100644 --- a/src/corelib/animation/qparallelanimationgroup.cpp +++ b/src/corelib/animation/qparallelanimationgroup.cpp @@ -136,7 +136,9 @@ void QParallelAnimationGroup::updateCurrentTime(int currentTime) int dura = duration(); if (dura > 0) { for (int i = 0; i < d->animations.size(); ++i) { - d->animations.at(i)->setCurrentTime(dura); // will stop + QAbstractAnimation *animation = d->animations.at(i); + if (animation->state() != QAbstractAnimation::Stopped) + d->animations.at(i)->setCurrentTime(dura); // will stop } } } else if (d->currentLoop < d->lastLoop) { @@ -160,7 +162,7 @@ void QParallelAnimationGroup::updateCurrentTime(int currentTime) QAbstractAnimation *animation = d->animations.at(i); const int dura = animation->totalDuration(); //if the loopcount is bigger we should always start all animations - if (d->currentLoop > d->lastLoop + if (d->currentLoop > d->lastLoop //if we're at the end of the animation, we need to start it if it wasn't already started in this loop //this happens in Backward direction where not all animations are started at the same time || d->shouldAnimationStart(animation, d->lastCurrentTime > dura /*startIfAtEnd*/)) { @@ -283,7 +285,7 @@ bool QParallelAnimationGroupPrivate::shouldAnimationStart(QAbstractAnimation *an void QParallelAnimationGroupPrivate::applyGroupState(QAbstractAnimation *animation) { - switch (state) + switch (state) { case QAbstractAnimation::Running: animation->start(); -- cgit v0.12 From 0054b444a202d08167c49a1a94fd2d306d14f91f Mon Sep 17 00:00:00 2001 From: Alexis Menard Date: Thu, 8 Oct 2009 16:06:12 +0200 Subject: Fix context menu and menu key in QGraphicsView. This fixes a weird bug when pressing the menu key on a proxy that embed a lineedit (but the proxy don't have the focus). At each time the key was pressed a context menu was created. When we press the menu key, QWidgetMapper will create a context menu event that will be sent to the QGraphicsView and will end up in QGraphicsScene. QGraphicsScene will then try to find all items under the mouse and will try to find any item from this list that accept this contextMenuEvent. In our case the proxy will always accept it and therefor will always send it to the widget that it owns and that's why we had this infinite numbers of QMenu. In QWidget world the context menu event is always sent to the widget that has the focus. If you checked any QWidget subclasses you'll see that all of them assume that when they get a context menu event everything is fine focus wise. We could have changed QGraphicsScene::contextMenuEvent but this will breaks some existing code. Instead we just checked that QGraphicsProxyWidget has the focus before we actually send the contextMenuEvent to the widget it owns. I have modified an existing auto-test to cover that. Be careful widget->setContextMenuPolicy(Qt::ActionsContextMenu) means that you will get no contextMenuEvent. :D. In this test i cover the standard use case and the one with Qt::ActionsContextMenu with and without the focus on the proxy. Task-number:QTBUG-3787 Reviewed-by:jan-arve --- src/gui/graphicsview/qgraphicsproxywidget.cpp | 2 +- .../tst_qgraphicsproxywidget.cpp | 57 +++++++++++++++++----- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp index 15b9ff3..b7a3962 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.cpp +++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp @@ -973,7 +973,7 @@ void QGraphicsProxyWidget::hideEvent(QHideEvent *event) void QGraphicsProxyWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { Q_D(QGraphicsProxyWidget); - if (!event || !d->widget || !d->widget->isVisible()) + if (!event || !d->widget || !d->widget->isVisible() || !hasFocus()) return; // Find widget position and receiver. diff --git a/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp index 76e7202..d8d97e8 100644 --- a/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp +++ b/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -170,6 +170,7 @@ private slots: void dontCrashWhenDie(); void createProxyForChildWidget(); void actionsContextMenu(); + void actionsContextMenu_data(); void deleteProxyForChildWidget(); void bypassGraphicsProxyWidget_data(); void bypassGraphicsProxyWidget(); @@ -3014,30 +3015,64 @@ private slots: } }; +void tst_QGraphicsProxyWidget::actionsContextMenu_data() +{ + QTest::addColumn("actionsContextMenu"); + QTest::addColumn("hasFocus"); + + QTest::newRow("without actionsContextMenu and with focus") << false << true; + QTest::newRow("without actionsContextMenu and without focus") << false << false; + QTest::newRow("with actionsContextMenu and focus") << true << true; + QTest::newRow("with actionsContextMenu without focus") << true << false; +} + void tst_QGraphicsProxyWidget::actionsContextMenu() { - ContextMenuWidget *widget = new ContextMenuWidget; - widget->addAction(new QAction("item 1", widget)); - widget->addAction(new QAction("item 2", widget)); - widget->addAction(new QAction("item 3", widget)); - widget->setContextMenuPolicy(Qt::ActionsContextMenu); + QFETCH(bool, hasFocus); + QFETCH(bool, actionsContextMenu); + ContextMenuWidget *widget = new ContextMenuWidget; + if (actionsContextMenu) { + widget->addAction(new QAction("item 1", widget)); + widget->addAction(new QAction("item 2", widget)); + widget->addAction(new QAction("item 3", widget)); + widget->setContextMenuPolicy(Qt::ActionsContextMenu); + } QGraphicsScene scene; - scene.addWidget(widget); + + if (hasFocus) + scene.addWidget(widget)->setFocus(); + else + scene.addWidget(widget)->clearFocus(); QGraphicsView view(&scene); view.show(); -#ifdef Q_WS_X11 - qt_x11_wait_for_window_manager(&view); -#endif + QTest::qWaitForWindowShown(&view); + view.setFocus(); QContextMenuEvent contextMenuEvent(QContextMenuEvent::Mouse, view.viewport()->rect().center(), view.viewport()->mapToGlobal(view.viewport()->rect().center())); contextMenuEvent.accept(); qApp->sendEvent(view.viewport(), &contextMenuEvent); - QVERIFY(widget->embeddedPopup); - QVERIFY(!widget->gotContextMenuEvent); + QApplication::processEvents(); + + if (hasFocus) { + if (actionsContextMenu) { + //actionsContextMenu embedded popup but no contextMenuEvent (widget has focus) + QVERIFY(widget->embeddedPopup); + QVERIFY(!widget->gotContextMenuEvent); + } else { + //no embedded popup but contextMenuEvent (widget has focus) + QVERIFY(!widget->embeddedPopup); + QVERIFY(widget->gotContextMenuEvent); + } + } else { + //qgraphicsproxywidget doesn't have the focus, the widget must not receive any contextMenuEvent and must not create any QMenu + QVERIFY(!widget->embeddedPopup); + QVERIFY(!widget->gotContextMenuEvent); + } + } -- cgit v0.12 From fc3dfc20d487cb4fd2f93bd9fa36eef85a7467a3 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 9 Oct 2009 09:45:45 +0200 Subject: Fixed a potential crash in QGraphicsScenePrivate::_q_polishItems() This patch make sure that we always start from the beginning of the unpolished items list and we erase the first value at each iteration. The patch also convert the list to a set that is more appropriate here. Merge-request: 1707 Reviewed-by: Alexis Menard --- src/gui/graphicsview/qgraphicsscene.cpp | 11 ++++++---- src/gui/graphicsview/qgraphicsscene_p.h | 2 +- tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp | 28 ++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 961f44f..056a7ce 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -420,8 +420,12 @@ void QGraphicsScenePrivate::unregisterTopLevelItem(QGraphicsItem *item) */ void QGraphicsScenePrivate::_q_polishItems() { + QSet::Iterator it; const QVariant booleanTrueVariant(true); - foreach (QGraphicsItem *item, unpolishedItems) { + while (!unpolishedItems.isEmpty()) { + it = unpolishedItems.begin(); + QGraphicsItem *item = *it; + unpolishedItems.erase(it); if (!item->d_ptr->explicitlyHidden) { item->itemChange(QGraphicsItem::ItemVisibleChange, booleanTrueVariant); item->itemChange(QGraphicsItem::ItemVisibleHasChanged, booleanTrueVariant); @@ -431,7 +435,6 @@ void QGraphicsScenePrivate::_q_polishItems() QApplication::sendEvent((QGraphicsWidget *)item, &event); } } - unpolishedItems.clear(); } void QGraphicsScenePrivate::_q_processDirtyItems() @@ -549,7 +552,7 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) selectedItems.remove(item); hoverItems.removeAll(item); cachedItemsUnderMouse.removeAll(item); - unpolishedItems.removeAll(item); + unpolishedItems.remove(item); resetDirtyItem(item); //We remove all references of item from the sceneEventFilter arrays @@ -2484,7 +2487,7 @@ void QGraphicsScene::addItem(QGraphicsItem *item) if (!item->d_ptr->explicitlyHidden) { if (d->unpolishedItems.isEmpty()) QMetaObject::invokeMethod(this, "_q_polishItems", Qt::QueuedConnection); - d->unpolishedItems << item; + d->unpolishedItems.insert(item); } // Reenable selectionChanged() for individual items diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 5000860..8073695 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -108,7 +108,7 @@ public: QPainterPath selectionArea; int selectionChanging; QSet selectedItems; - QList unpolishedItems; + QSet unpolishedItems; QList topLevelItems; bool needSortTopLevelItems; bool holesInTopLevelSiblingIndex; diff --git a/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp b/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp index 8459331..6c5fe90 100644 --- a/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp +++ b/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp @@ -266,6 +266,7 @@ private slots: void dispatchHoverOnPress(); void initialFocus_data(); void initialFocus(); + void polishItems(); // task specific tests below me void task139710_bspTreeCrash(); @@ -3884,5 +3885,32 @@ void tst_QGraphicsScene::initialFocus() QCOMPARE(rect->hasFocus(), shouldHaveFocus); } +class PolishItem : public QGraphicsTextItem +{ +public: + PolishItem(QGraphicsItem *parent = 0) : QGraphicsTextItem(parent) { } + +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant& value) + { + if (change == ItemVisibleChange) { + if (value.toBool()) + qDeleteAll(childItems()); + } + return QGraphicsItem::itemChange(change, value); + } +}; + +void tst_QGraphicsScene::polishItems() +{ + QGraphicsScene scene; + PolishItem *parent = new PolishItem; + scene.addItem(parent); + PolishItem *child = new PolishItem(parent); + Q_UNUSED(child) + // test that QGraphicsScenePrivate::_q_polishItems() doesn't crash + QMetaObject::invokeMethod(&scene,"_q_polishItems"); +} + QTEST_MAIN(tst_QGraphicsScene) #include "tst_qgraphicsscene.moc" -- cgit v0.12 From b8eb2784a1c1a9812d09fca7c8722624f12dbd1c Mon Sep 17 00:00:00 2001 From: Alexis Menard Date: Fri, 9 Oct 2009 10:19:33 +0200 Subject: Add a test case for task QTBUG-4439. Commit fc3dfc20d487cb4fd2f93bd9fa36eef85a7467a3 fixes the problem. The unpolished items list was modified in between the iteration which means that invokeMethod was never recall if you add an item inside the polishEvent handler. The invokeMethod is called in addItem only when the list is empty right before we add the item in the list. Task-number:QTBUG-4439 Reviewed-by:TrustMe --- tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp b/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp index 26021e0..6b5ad09 100644 --- a/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp +++ b/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp @@ -159,6 +159,7 @@ private slots: void ensureClipping(); void widgetSendsGeometryChanges(); void respectHFW(); + void addChildInpolishEvent(); // Task fixes void task236127_bspTreeIndexFails(); @@ -2716,6 +2717,58 @@ void tst_QGraphicsWidget::respectHFW() #endif } +class PolishWidget : public QGraphicsWidget +{ +public: + + PolishWidget(Qt::GlobalColor color, QGraphicsItem *parent=0) : + QGraphicsWidget(parent), mColor(color) + { + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + { + painter->setBrush(QBrush(mColor)); + painter->drawRect(boundingRect()); + } + + void polishEvent() + { + if (!parentWidget()) { + //We add a child in the polish event for the parent + PolishWidget *childWidget = new PolishWidget(Qt::black, this); + childWidget->setGeometry(QRectF(10,10,30,30)); + } + + QGraphicsWidget::polishEvent(); + mColor = Qt::red; + update(); + numberOfPolish++; + } + + static int numberOfPolish; + +private: + Qt::GlobalColor mColor; +}; + +int PolishWidget::numberOfPolish = 0; + +void tst_QGraphicsWidget::addChildInpolishEvent() +{ + QGraphicsScene scene; + + PolishWidget *parentWidget = new PolishWidget(Qt::white); + scene.addItem(parentWidget); + + QGraphicsView view(&scene); + view.resize(200, 200); + view.show(); + QTest::qWaitForWindowShown(&view); + QCOMPARE(PolishWidget::numberOfPolish, 2); +} + + QTEST_MAIN(tst_QGraphicsWidget) #include "tst_qgraphicswidget.moc" -- cgit v0.12 From b81f1099d84d9be5c99905b506ab86d477cd625b Mon Sep 17 00:00:00 2001 From: Alexis Menard Date: Fri, 9 Oct 2009 16:37:43 +0200 Subject: Fix an auto-test regression. Here lot of views were floating around and the processEvent was not called in the right place. Reviewed-by:ogoffart --- .../tst_qgraphicsproxywidget.cpp | 30 +++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp index d8d97e8..d016461 100644 --- a/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp +++ b/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -1757,6 +1757,8 @@ void tst_QGraphicsProxyWidget::tabFocus_simpleWidget() QTRY_VERIFY(leftDial->hasFocus()); QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2); QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2); + + delete view; } void tst_QGraphicsProxyWidget::tabFocus_simpleTwoWidgets() @@ -1879,6 +1881,8 @@ void tst_QGraphicsProxyWidget::tabFocus_simpleTwoWidgets() QVERIFY(leftDial->hasFocus()); QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2); QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2); + + delete view; } void tst_QGraphicsProxyWidget::tabFocus_complexWidget() @@ -1989,6 +1993,8 @@ void tst_QGraphicsProxyWidget::tabFocus_complexWidget() QApplication::processEvents(); QVERIFY(!box->hasFocus()); leftDial->hasFocus(); + + delete view; } void tst_QGraphicsProxyWidget::tabFocus_complexTwoWidgets() @@ -2157,6 +2163,8 @@ void tst_QGraphicsProxyWidget::tabFocus_complexTwoWidgets() QApplication::processEvents(); QVERIFY(!box->hasFocus()); leftDial->hasFocus(); + + delete view; } void tst_QGraphicsProxyWidget::setFocus_simpleWidget() @@ -2223,6 +2231,8 @@ void tst_QGraphicsProxyWidget::setFocus_simpleWidget() // Symmetry editProxy->clearFocus(); QVERIFY(!edit->hasFocus()); + + delete view; } void tst_QGraphicsProxyWidget::setFocus_simpleTwoWidgets() @@ -2273,6 +2283,8 @@ void tst_QGraphicsProxyWidget::setFocus_simpleTwoWidgets() QVERIFY(!editProxy->hasFocus()); QVERIFY(edit2->hasFocus()); QVERIFY(edit2Proxy->hasFocus()); + + delete view; } void tst_QGraphicsProxyWidget::setFocus_complexTwoWidgets() @@ -2392,6 +2404,8 @@ void tst_QGraphicsProxyWidget::setFocus_complexTwoWidgets() QCOMPARE(eventSpyBox.counts[QEvent::FocusOut], 1); QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0); QCOMPARE(eventSpyBox_2.counts[QEvent::FocusOut], 0); + + delete view; } void tst_QGraphicsProxyWidget::popup_basic() @@ -2897,6 +2911,9 @@ void tst_QGraphicsProxyWidget::dontCrashWhenDie() QTest::qWait(100); QTest::mouseMove(w->view->viewport(), w->view->mapFromScene(w->widget->mapToScene(w->widget->boundingRect().center()))); delete w->item; + + QApplication::processEvents(); + delete w; } void tst_QGraphicsProxyWidget::createProxyForChildWidget() @@ -3040,23 +3057,24 @@ void tst_QGraphicsProxyWidget::actionsContextMenu() } QGraphicsScene scene; + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + view.setFocus(); + if (hasFocus) scene.addWidget(widget)->setFocus(); else scene.addWidget(widget)->clearFocus(); - QGraphicsView view(&scene); - view.show(); - QTest::qWaitForWindowShown(&view); - view.setFocus(); + QApplication::processEvents(); + QContextMenuEvent contextMenuEvent(QContextMenuEvent::Mouse, view.viewport()->rect().center(), view.viewport()->mapToGlobal(view.viewport()->rect().center())); contextMenuEvent.accept(); qApp->sendEvent(view.viewport(), &contextMenuEvent); - QApplication::processEvents(); - if (hasFocus) { if (actionsContextMenu) { //actionsContextMenu embedded popup but no contextMenuEvent (widget has focus) -- cgit v0.12 From 9b70924fe2ff5b4bc7246c46a9e3af764bb8bbc8 Mon Sep 17 00:00:00 2001 From: Leonardo Sobral Cunha Date: Fri, 9 Oct 2009 16:55:30 +0200 Subject: Update lastTick on all timerTicks in QAbstractAnimation This is needed in case we change consistentTime while the animation is running. Reviewed-by: thierry --- src/corelib/animation/qabstractanimation.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index 9e50784..c775a00 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -207,14 +207,10 @@ void QUnifiedTimer::ensureTimerUpdate(QAbstractAnimation *animation) void QUnifiedTimer::updateAnimationsTime() { - // this is simply the time we last received a tick - const int oldLastTick = lastTick; // ignore consistentTiming in case the pause timer is active - if (consistentTiming && !isPauseTimerActive) - lastTick = oldLastTick + timingInterval; - else - lastTick = time.elapsed(); - const int delta = lastTick - oldLastTick; + const int delta = (consistentTiming && !isPauseTimerActive) ? + timingInterval : time.elapsed() - lastTick; + lastTick = time.elapsed(); //we make sure we only call update time if the time has actually changed //it might happen in some cases that the time doesn't change because events are delayed @@ -293,6 +289,7 @@ void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) // this is needed if we unregister an animation while its running if (idx <= currentAnimationIdx) --currentAnimationIdx; + if (animations.isEmpty()) startStopAnimationTimer.start(STARTSTOP_TIMER_DELAY, this); } else { -- cgit v0.12 From d98012baf3315fad975d0c0acbab13a54ea15caa Mon Sep 17 00:00:00 2001 From: Leonardo Sobral Cunha Date: Fri, 9 Oct 2009 16:57:00 +0200 Subject: Fixes QPauseAnimation autotests for unreliable timer intervals on Windows The timer interval used currently on Windows is 16 ms, but we get ticks at every 32 ms on average, so the consistent timing is not reliable on windows. We should use the multimedia timer instead (use 15 ms for QTimer), once qt is able to handle events while native event loops are running. When this is done, the ifdefs introduced in this commit should be removed. Reviewed-by: thierry --- tests/auto/qpauseanimation/tst_qpauseanimation.cpp | 43 +++++++++++++++------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/tests/auto/qpauseanimation/tst_qpauseanimation.cpp b/tests/auto/qpauseanimation/tst_qpauseanimation.cpp index 0c742af..62b43c4 100644 --- a/tests/auto/qpauseanimation/tst_qpauseanimation.cpp +++ b/tests/auto/qpauseanimation/tst_qpauseanimation.cpp @@ -70,6 +70,21 @@ protected: } }; +class EnableConsistentTiming +{ +public: + EnableConsistentTiming() + { + QUnifiedTimer *timer = QUnifiedTimer::instance(); + timer->setConsistentTiming(true); + } + ~EnableConsistentTiming() + { + QUnifiedTimer *timer = QUnifiedTimer::instance(); + timer->setConsistentTiming(false); + } +}; + class tst_QPauseAnimation : public QObject { Q_OBJECT @@ -187,8 +202,7 @@ void tst_QPauseAnimation::mulitplePauseAnimations() void tst_QPauseAnimation::pauseAndPropertyAnimations() { - QUnifiedTimer *timer = QUnifiedTimer::instance(); - timer->setConsistentTiming(true); + EnableConsistentTiming enabled; TestablePauseAnimation pause; pause.setDuration(200); @@ -210,11 +224,13 @@ void tst_QPauseAnimation::pauseAndPropertyAnimations() QTest::qWait(animation.totalDuration() + 100); +#ifdef Q_OS_WIN + if (animation.state() != QAbstractAnimation::Stopped) + QEXPECT_FAIL("", "On windows, consistent timing is not working properly due to bad timer resolution", Abort); +#endif QVERIFY(animation.state() == QAbstractAnimation::Stopped); QVERIFY(pause.state() == QAbstractAnimation::Stopped); QVERIFY(pause.m_updateCurrentTimeCount > 3); - - timer->setConsistentTiming(false); } void tst_QPauseAnimation::pauseResume() @@ -250,7 +266,7 @@ void tst_QPauseAnimation::sequentialPauseGroup() QVERIFY(animation2.state() == QAbstractAnimation::Stopped); QVERIFY(animation3.state() == QAbstractAnimation::Stopped); - QTest::qWait(250); + group.setCurrentTime(250); QVERIFY(group.state() == QAbstractAnimation::Running); QVERIFY(animation1.state() == QAbstractAnimation::Stopped); @@ -258,7 +274,7 @@ void tst_QPauseAnimation::sequentialPauseGroup() QVERIFY(animation2.state() == QAbstractAnimation::Running); QVERIFY(animation3.state() == QAbstractAnimation::Stopped); - QTest::qWait(250); + group.setCurrentTime(500); QVERIFY(group.state() == QAbstractAnimation::Running); QVERIFY(animation1.state() == QAbstractAnimation::Stopped); @@ -266,7 +282,7 @@ void tst_QPauseAnimation::sequentialPauseGroup() QCOMPARE(&animation3, group.currentAnimation()); QVERIFY(animation3.state() == QAbstractAnimation::Running); - QTest::qWait(250); + group.setCurrentTime(750); QVERIFY(group.state() == QAbstractAnimation::Stopped); QVERIFY(animation1.state() == QAbstractAnimation::Stopped); @@ -296,14 +312,14 @@ void tst_QPauseAnimation::sequentialGroupWithPause() QVERIFY(animation.state() == QAbstractAnimation::Running); QVERIFY(pause.state() == QAbstractAnimation::Stopped); - QTest::qWait(300); + group.setCurrentTime(300); QVERIFY(group.state() == QAbstractAnimation::Running); QVERIFY(animation.state() == QAbstractAnimation::Stopped); QCOMPARE(&pause, group.currentAnimation()); QVERIFY(pause.state() == QAbstractAnimation::Running); - QTest::qWait(300); + group.setCurrentTime(600); QVERIFY(group.state() == QAbstractAnimation::Stopped); QVERIFY(animation.state() == QAbstractAnimation::Stopped); @@ -314,8 +330,7 @@ void tst_QPauseAnimation::sequentialGroupWithPause() void tst_QPauseAnimation::multipleSequentialGroups() { - QUnifiedTimer *timer = QUnifiedTimer::instance(); - timer->setConsistentTiming(true); + EnableConsistentTiming enabled; QParallelAnimationGroup group; group.setLoopCount(2); @@ -368,6 +383,10 @@ void tst_QPauseAnimation::multipleSequentialGroups() QTest::qWait(group.totalDuration() + 100); +#ifdef Q_OS_WIN + if (group.state() != QAbstractAnimation::Stopped) + QEXPECT_FAIL("", "On windows, consistent timing is not working properly due to bad timer resolution", Abort); +#endif QVERIFY(group.state() == QAbstractAnimation::Stopped); QVERIFY(subgroup1.state() == QAbstractAnimation::Stopped); QVERIFY(subgroup2.state() == QAbstractAnimation::Stopped); @@ -375,8 +394,6 @@ void tst_QPauseAnimation::multipleSequentialGroups() QVERIFY(subgroup4.state() == QAbstractAnimation::Stopped); QCOMPARE(pause5.m_updateCurrentTimeCount, 4); - - timer->setConsistentTiming(false); } void tst_QPauseAnimation::zeroDuration() -- cgit v0.12 From acc509a77b6deeed0b3994a933d5cde291e17df6 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 12 Oct 2009 11:31:24 +0200 Subject: Add usefull debug output to the bic test --- tests/auto/bic/tst_bic.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auto/bic/tst_bic.cpp b/tests/auto/bic/tst_bic.cpp index 82c8dc0..8c6056e 100644 --- a/tests/auto/bic/tst_bic.cpp +++ b/tests/auto/bic/tst_bic.cpp @@ -252,6 +252,7 @@ QBic::Info tst_Bic::getCurrentInfo(const QString &libName) } if (proc.exitCode() != 0) { qWarning() << "gcc returned with" << proc.exitCode(); + qDebug() << proc.readAllStandardError(); return QBic::Info(); } @@ -268,6 +269,7 @@ QBic::Info tst_Bic::getCurrentInfo(const QString &libName) qFatal("Could not locate the GCC output file, update this test"); return QBic::Info(); } else if (files.size() > 1) { + qDebug() << files; qFatal("Located more than one output file, please clean up before running this test"); return QBic::Info(); } -- cgit v0.12 From 92dab5ea276732944915a9423fc174db6d84e572 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 12 Oct 2009 12:59:16 +0200 Subject: GraphicsView tests on mac When a widget is shown we get two paint avent on Mac --- tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp | 2 +- tests/auto/qgraphicsview/tst_qgraphicsview.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp index e4eaf4e..c6ee237 100644 --- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp @@ -6183,7 +6183,7 @@ void tst_QGraphicsItem::opacity2() MyGraphicsView view(&scene); view.show(); QTest::qWaitForWindowShown(&view); - QTRY_COMPARE(view.repaints, 1); + QTRY_VERIFY(view.repaints > 1); #define RESET_REPAINT_COUNTERS \ parent->repaints = 0; \ diff --git a/tests/auto/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/qgraphicsview/tst_qgraphicsview.cpp index df3ebef..f7c8d35 100644 --- a/tests/auto/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/qgraphicsview/tst_qgraphicsview.cpp @@ -115,6 +115,7 @@ public: } int count() const { return _count; } + void reset() { _count = 0; } protected: bool eventFilter(QObject *watched, QEvent *event) @@ -2938,10 +2939,11 @@ void tst_QGraphicsView::task239729_noViewUpdate() view->show(); QTest::qWaitForWindowShown(view); - QTRY_COMPARE(spy.count(), 1); + QTRY_VERIFY(spy.count() > 1); + spy.reset(); scene.update(); QApplication::processEvents(); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE(spy.count(), 1); delete view; } -- cgit v0.12 From 106678fe1f27e1df161b2e55ccc88b244fa6d478 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 8 Oct 2009 17:49:50 +0200 Subject: Fix the QWidgetAction test on Carbon --- tests/auto/qwidgetaction/tst_qwidgetaction.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auto/qwidgetaction/tst_qwidgetaction.cpp b/tests/auto/qwidgetaction/tst_qwidgetaction.cpp index 586a707..50b3337 100644 --- a/tests/auto/qwidgetaction/tst_qwidgetaction.cpp +++ b/tests/auto/qwidgetaction/tst_qwidgetaction.cpp @@ -125,6 +125,7 @@ void tst_QWidgetAction::defaultWidget() tb1.addAction(action); QVERIFY(combo->parent() == &tb1); qApp->processEvents(); + qApp->processEvents(); QVERIFY(combo->isVisible()); // not supported, not supposed to work, hence the parent() check @@ -139,6 +140,7 @@ void tst_QWidgetAction::defaultWidget() tb2.addAction(action); qApp->processEvents(); //the call to hide is delayd by the toolbar layout + qApp->processEvents(); QVERIFY(combo->parent() == &tb2); QVERIFY(combo->isVisible()); -- cgit v0.12 From dc6138cd793bf5b39617cb834ff0ee7c536384b9 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 12 Oct 2009 13:24:01 +0200 Subject: Fix GraphicsView test on mac On mac, we always get full update. --- tests/auto/qgraphicsview/tst_qgraphicsview.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/auto/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/qgraphicsview/tst_qgraphicsview.cpp index f7c8d35..926335f 100644 --- a/tests/auto/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/qgraphicsview/tst_qgraphicsview.cpp @@ -84,6 +84,12 @@ Q_DECLARE_METATYPE(QPolygonF) Q_DECLARE_METATYPE(QRectF) Q_DECLARE_METATYPE(Qt::ScrollBarPolicy) +#ifdef Q_WS_MAC +//On mac we get full update. So check that the expected region is contained inside the actual +#define COMPARE_REGIONS(ACTUAL, EXPECTED) QVERIFY((EXPECTED).subtracted(ACTUAL).isEmpty()) +#else +#define COMPARE_REGIONS QCOMPARE +#endif static void sendMousePress(QWidget *widget, const QPoint &point, Qt::MouseButton button = Qt::LeftButton) { @@ -3179,7 +3185,7 @@ void tst_QGraphicsView::moveItemWhileScrolling() int a = adjustForAntialiasing ? 2 : 1; expectedRegion += QRect(40, 50, 10, 10).adjusted(-a, -a, a, a); expectedRegion += QRect(40, 60, 10, 10).adjusted(-a, -a, a, a); - QCOMPARE(view.lastPaintedRegion, expectedRegion); + COMPARE_REGIONS(view.lastPaintedRegion, expectedRegion); } void tst_QGraphicsView::centerOnDirtyItem() -- cgit v0.12 From 857b7443b1bd4311a02753bea7b8c7839f1ad311 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 12 Oct 2009 13:43:11 +0200 Subject: QFileSystemModel autotest: sorting might be delayed --- tests/auto/qfilesystemmodel/tst_qfilesystemmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auto/qfilesystemmodel/tst_qfilesystemmodel.cpp b/tests/auto/qfilesystemmodel/tst_qfilesystemmodel.cpp index 29e4fe6..3b24352 100644 --- a/tests/auto/qfilesystemmodel/tst_qfilesystemmodel.cpp +++ b/tests/auto/qfilesystemmodel/tst_qfilesystemmodel.cpp @@ -874,7 +874,7 @@ void tst_QFileSystemModel::sort() } else { for(int i = 0; i < myModel->rowCount(parent); ++i) { - QVERIFY(dirPath + QChar('/') + myModel->index(i, 1, parent).data(QFileSystemModel::FileNameRole).toString() == expectedOrder.at(i)); + QTRY_COMPARE(dirPath + QChar('/') + myModel->index(i, 1, parent).data(QFileSystemModel::FileNameRole).toString(), expectedOrder.at(i)); } } -- cgit v0.12 From 8da7252de0badb818302763cbe62c38ad699f1f3 Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Mon, 12 Oct 2009 14:28:33 +0200 Subject: Fixed keyboard navigation for QTableView Keyboard navigation didn't work in the following cases: - The last column was disabled and we pressed the tab key when at the 2nd last column. (See ref. task). - Spans with their anchor column or row hidden or disabled. - Navigation would not preserve the original row/column when traversing a span horizontally/vertically. Auto-tests submitted with this commit. Task-number: QTBUG-3878 Reviewed-by: Olivier --- src/gui/itemviews/qabstractitemview.cpp | 3 + src/gui/itemviews/qtableview.cpp | 204 ++++++++++++++++++++----------- src/gui/itemviews/qtableview_p.h | 4 +- tests/auto/qtableview/tst_qtableview.cpp | 166 ++++++++++++++++++++++++- 4 files changed, 304 insertions(+), 73 deletions(-) diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index 37f4184..268e78e 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -2185,6 +2185,9 @@ void QAbstractItemView::keyPressEvent(QKeyEvent *event) } else { d->selectionModel->setCurrentIndex(newCurrent, command); d->pressedPosition = visualRect(newCurrent).center() + d->offset(); + // We copy the same behaviour as for mousePressEvent(). + QRect rect(d->pressedPosition - d->offset(), QSize(1, 1)); + setSelection(rect, command); } return; } diff --git a/src/gui/itemviews/qtableview.cpp b/src/gui/itemviews/qtableview.cpp index 15bd445..2d98258 100644 --- a/src/gui/itemviews/qtableview.cpp +++ b/src/gui/itemviews/qtableview.cpp @@ -779,8 +779,6 @@ void QTableViewPrivate::drawAndClipSpans(const QRegion &area, QPainter *painter, foreach (QSpanCollection::Span *span, visibleSpans) { int row = span->top(); int col = span->left(); - if (isHidden(row, col)) - continue; QModelIndex index = model->index(row, col, root); if (!index.isValid()) continue; @@ -1480,12 +1478,30 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi ++column; while (isRowHidden(d->logicalRow(row)) && row < bottom) ++row; + d->visualCursor = QPoint(column, row); return d->model->index(d->logicalRow(row), d->logicalColumn(column), d->root); } - int visualRow = d->visualRow(current.row()); + // Update visual cursor if current index has changed. + QPoint visualCurrent(d->visualColumn(current.column()), d->visualRow(current.row())); + if (visualCurrent != d->visualCursor) { + if (d->hasSpans()) { + QSpanCollection::Span span = d->span(current.row(), current.column()); + if (span.top() > d->visualCursor.y() || d->visualCursor.y() > span.bottom() + || span.left() > d->visualCursor.x() || d->visualCursor.x() > span.right()) + d->visualCursor = visualCurrent; + } else { + d->visualCursor = visualCurrent; + } + } + + int visualRow = d->visualCursor.y(); + if (visualRow > bottom) + visualRow = bottom; Q_ASSERT(visualRow != -1); - int visualColumn = d->visualColumn(current.column()); + int visualColumn = d->visualCursor.x(); + if (visualColumn > right) + visualColumn = right; Q_ASSERT(visualColumn != -1); if (isRightToLeft()) { @@ -1496,22 +1512,33 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi } switch (cursorAction) { - case MoveUp: + case MoveUp: { + int originalRow = visualRow; #ifdef QT_KEYPAD_NAVIGATION if (QApplication::keypadNavigationEnabled() && visualRow == 0) visualRow = d->visualRow(model()->rowCount() - 1) + 1; + // FIXME? visualRow = bottom + 1; #endif - --visualRow; - while (visualRow > 0 && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn)) + int r = d->logicalRow(visualRow); + int c = d->logicalColumn(visualColumn); + if (r != -1 && d->hasSpans()) { + QSpanCollection::Span span = d->span(r, c); + if (span.width() > 1 || span.height() > 1) + visualRow = d->visualRow(span.top()); + } + while (visualRow >= 0) { --visualRow; - if (d->hasSpans()) { - int row = d->logicalRow(visualRow); - QSpanCollection::Span span = d->span(row, current.column()); - visualRow = d->visualRow(span.top()); - visualColumn = d->visualColumn(span.left()); + r = d->logicalRow(visualRow); + c = d->logicalColumn(visualColumn); + if (r == -1 || (!isRowHidden(r) && d->isCellEnabled(r, c))) + break; } + if (visualRow < 0) + visualRow = originalRow; break; - case MoveDown: + } + case MoveDown: { + int originalRow = visualRow; if (d->hasSpans()) { QSpanCollection::Span span = d->span(current.row(), current.column()); visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height())); @@ -1520,71 +1547,106 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi if (QApplication::keypadNavigationEnabled() && visualRow >= bottom) visualRow = -1; #endif - ++visualRow; - while (visualRow < bottom && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn)) + int r = d->logicalRow(visualRow); + int c = d->logicalColumn(visualColumn); + if (r != -1 && d->hasSpans()) { + QSpanCollection::Span span = d->span(r, c); + if (span.width() > 1 || span.height() > 1) + visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height())); + } + while (visualRow <= bottom) { ++visualRow; - if (d->hasSpans()) { - int row = d->logicalRow(visualRow); - QSpanCollection::Span span = d->span(row, current.column()); - visualColumn = d->visualColumn(span.left()); + r = d->logicalRow(visualRow); + c = d->logicalColumn(visualColumn); + if (r == -1 || (!isRowHidden(r) && d->isCellEnabled(r, c))) + break; } + if (visualRow > bottom) + visualRow = originalRow; break; - case MovePrevious: { - int left = 0; - while (d->isVisualColumnHiddenOrDisabled(visualRow, left) && left < right) - ++left; - if (visualColumn == left) { - visualColumn = right; - int top = 0; - while (top < bottom && d->isVisualRowHiddenOrDisabled(top, visualColumn)) - ++top; - if (visualRow == top) + } + case MovePrevious: + case MoveLeft: { + int originalRow = visualRow; + int originalColumn = visualColumn; + bool firstTime = true; + bool looped = false; + bool wrapped = false; + do { + int r = d->logicalRow(visualRow); + int c = d->logicalColumn(visualColumn); + if (firstTime && c != -1 && d->hasSpans()) { + firstTime = false; + QSpanCollection::Span span = d->span(r, c); + if (span.width() > 1 || span.height() > 1) + visualColumn = d->visualColumn(span.left()); + } + while (visualColumn >= 0) { + --visualColumn; + r = d->logicalRow(visualRow); + c = d->logicalColumn(visualColumn); + if (r == -1 || c == -1 || (!isRowHidden(r) && !isColumnHidden(c) && d->isCellEnabled(r, c))) + break; + if (wrapped && (originalRow < visualRow || (originalRow == visualRow && originalColumn <= visualColumn))) { + looped = true; + break; + } + } + if (cursorAction == MoveLeft || visualColumn >= 0) + break; + visualColumn = right + 1; + if (visualRow == 0) { + wrapped == true; visualRow = bottom; - else - --visualRow; - while (visualRow > 0 && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn)) + } else { --visualRow; - break; - } // else MoveLeft - } - case MoveLeft: - --visualColumn; - while (visualColumn > 0 && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn)) - --visualColumn; - if (d->hasSpans()) { - int column = d->logicalColumn(visualColumn); - QSpanCollection::Span span = d->span(current.row(), column); - visualRow = d->visualRow(span.top()); - visualColumn = d->visualColumn(span.left()); - } + } + } while (!looped); + if (visualColumn < 0) + visualColumn = originalColumn; break; + } case MoveNext: - if (visualColumn == right) { - visualColumn = 0; - while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn)) + case MoveRight: { + int originalRow = visualRow; + int originalColumn = visualColumn; + bool firstTime = true; + bool looped = false; + bool wrapped = false; + do { + int r = d->logicalRow(visualRow); + int c = d->logicalColumn(visualColumn); + if (firstTime && c != -1 && d->hasSpans()) { + firstTime = false; + QSpanCollection::Span span = d->span(r, c); + if (span.width() > 1 || span.height() > 1) + visualColumn = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width())); + } + while (visualColumn <= right) { ++visualColumn; - if (visualRow == bottom) + r = d->logicalRow(visualRow); + c = d->logicalColumn(visualColumn); + if (r == -1 || c == -1 || (!isRowHidden(r) && !isColumnHidden(c) && d->isCellEnabled(r, c))) + break; + if (wrapped && (originalRow > visualRow || (originalRow == visualRow && originalColumn >= visualColumn))) { + looped = true; + break; + } + } + if (cursorAction == MoveRight || visualColumn <= right) + break; + visualColumn = -1; + if (visualRow == bottom) { + wrapped = true; visualRow = 0; - else - ++visualRow; - while (visualRow < bottom && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn)) + } else { ++visualRow; - break; - } // else MoveRight - case MoveRight: - if (d->hasSpans()) { - QSpanCollection::Span span = d->span(current.row(), current.column()); - visualColumn = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width())); - } - ++visualColumn; - while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn)) - ++visualColumn; - if (d->hasSpans()) { - int column = d->logicalColumn(visualColumn); - QSpanCollection::Span span = d->span(current.row(), column); - visualRow = d->visualRow(span.top()); - } + } + } while (!looped); + if (visualColumn > right) + visualColumn = originalColumn; break; + } case MoveHome: visualColumn = 0; while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn)) @@ -1613,14 +1675,15 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi return d->model->index(newRow, current.column(), d->root); }} + d->visualCursor = QPoint(visualColumn, visualRow); int logicalRow = d->logicalRow(visualRow); int logicalColumn = d->logicalColumn(visualColumn); if (!d->model->hasIndex(logicalRow, logicalColumn, d->root)) return QModelIndex(); QModelIndex result = d->model->index(logicalRow, logicalColumn, d->root); - if (!isIndexHidden(result) && d->isIndexEnabled(result)) - return d->model->index(logicalRow, logicalColumn, d->root); + if (!d->isRowHidden(logicalRow) && !d->isColumnHidden(logicalColumn) && d->isIndexEnabled(result)) + return result; return QModelIndex(); } @@ -2375,7 +2438,8 @@ bool QTableView::isCornerButtonEnabled() const QRect QTableView::visualRect(const QModelIndex &index) const { Q_D(const QTableView); - if (!d->isIndexValid(index) || index.parent() != d->root || isIndexHidden(index) ) + if (!d->isIndexValid(index) || index.parent() != d->root + || (!d->hasSpans() && isIndexHidden(index))) return QRect(); d->executePostedLayout(); diff --git a/src/gui/itemviews/qtableview_p.h b/src/gui/itemviews/qtableview_p.h index c785bd7..9fa14c2 100644 --- a/src/gui/itemviews/qtableview_p.h +++ b/src/gui/itemviews/qtableview_p.h @@ -135,7 +135,8 @@ public: rowSectionAnchor(-1), columnSectionAnchor(-1), columnResizeTimerID(0), rowResizeTimerID(0), horizontalHeader(0), verticalHeader(0), - sortingEnabled(false), geometryRecursionBlock(false) + sortingEnabled(false), geometryRecursionBlock(false), + visualCursor(QPoint()) { wrapItemText = true; #ifndef QT_NO_DRAGANDDROP @@ -183,6 +184,7 @@ public: QWidget *cornerWidget; bool sortingEnabled; bool geometryRecursionBlock; + QPoint visualCursor; // (Row,column) cell coordinates to track through span navigation. QSpanCollection spans; diff --git a/tests/auto/qtableview/tst_qtableview.cpp b/tests/auto/qtableview/tst_qtableview.cpp index bb0e226..4ea1b19 100644 --- a/tests/auto/qtableview/tst_qtableview.cpp +++ b/tests/auto/qtableview/tst_qtableview.cpp @@ -100,6 +100,9 @@ private slots: void moveCursor_data(); void moveCursor(); + void moveCursorStrikesBack_data(); + void moveCursorStrikesBack(); + void hideRows_data(); void hideRows(); @@ -252,12 +255,43 @@ public: row_count(rows), column_count(columns), can_fetch_more(false), - fetch_more_count(0) {} + fetch_more_count(0), + disabled_rows(), + disabled_columns() {} int rowCount(const QModelIndex& = QModelIndex()) const { return row_count; } int columnCount(const QModelIndex& = QModelIndex()) const { return column_count; } bool isEditable(const QModelIndex &) const { return true; } + Qt::ItemFlags flags(const QModelIndex &index) const + { + Qt::ItemFlags index_flags = QAbstractTableModel::flags(index); + if (disabled_rows.contains(index.row()) + || disabled_columns.contains(index.column())) + index_flags &= ~Qt::ItemIsEnabled; + return index_flags; + } + + void disableRow(int row) + { + disabled_rows.insert(row); + } + + void enableRow(int row) + { + disabled_rows.remove(row); + } + + void disableColumn(int column) + { + disabled_columns.insert(column); + } + + void enableColumn(int column) + { + disabled_columns.remove(column); + } + QVariant data(const QModelIndex &idx, int role) const { if (!idx.isValid() || idx.row() >= row_count || idx.column() >= column_count) { @@ -363,6 +397,8 @@ public: int column_count; bool can_fetch_more; int fetch_more_count; + QSet disabled_rows; + QSet disabled_columns; }; class QtTestTableView : public QTableView @@ -834,7 +870,7 @@ void tst_QTableView::moveCursor_data() << 4 << 4 << -1 << 2 << 0 << 2 << int(QtTestTableView::MoveNext) << int(Qt::NoModifier) - << 1 << 0 << IntPair(0,0) << IntPair(3,0); + << 1 << 3 << IntPair(0,0) << IntPair(3,0); // MoveLeft QTest::newRow("MoveLeft (0,0)") @@ -1137,6 +1173,132 @@ void tst_QTableView::moveCursor() QCOMPARE(newIndex.column(), expectedColumn); } +void tst_QTableView::moveCursorStrikesBack_data() +{ + QTest::addColumn("hideRow"); + QTest::addColumn("hideColumn"); + QTest::addColumn("disableRows"); + QTest::addColumn("disableColumns"); + QTest::addColumn("span"); + + QTest::addColumn("startRow"); + QTest::addColumn("startColumn"); + QTest::addColumn("cursorMoveActions"); + QTest::addColumn("expectedRow"); + QTest::addColumn("expectedColumn"); + + QTest::newRow("Last column disabled. Task QTBUG-3878") << -1 << -1 + << IntList() + << (IntList() << 6) + << QRect() + << 0 << 5 << (IntList() << int(QtTestTableView::MoveNext)) + << 1 << 0; + + QTest::newRow("Span, anchor column hidden") << -1 << 1 + << IntList() + << IntList() + << QRect(1, 2, 2, 3) + << 2 << 0 << (IntList() << int(QtTestTableView::MoveNext)) + << 2 << 2; + + QTest::newRow("Span, anchor column disabled") << -1 << -1 + << IntList() + << (IntList() << 1) + << QRect(1, 2, 2, 3) + << 2 << 0 << (IntList() << int(QtTestTableView::MoveNext)) + << 2 << 2; + + QTest::newRow("Span, anchor row hidden") << 2 << -1 + << IntList() + << IntList() + << QRect(1, 2, 2, 3) + << 1 << 2 << (IntList() << int(QtTestTableView::MoveDown)) + << 3 << 2; + + QTest::newRow("Span, anchor row disabled") << -1 << -1 + << (IntList() << 2) + << IntList() + << QRect(1, 2, 2, 3) + << 1 << 2 << (IntList() << int(QtTestTableView::MoveDown)) + << 3 << 2; + + QTest::newRow("Move through span right") << -1 << -1 + << IntList() + << IntList() + << QRect(1, 2, 2, 3) + << 3 << 0 << (IntList() << int(QtTestTableView::MoveRight) << int(QtTestTableView::MoveRight)) + << 3 << 3; + + QTest::newRow("Move through span left") << -1 << -1 + << IntList() + << IntList() + << QRect(1, 2, 2, 3) + << 3 << 3 << (IntList() << int(QtTestTableView::MoveLeft) << int(QtTestTableView::MoveLeft)) + << 3 << 0; + + QTest::newRow("Move through span down") << -1 << -1 + << IntList() + << IntList() + << QRect(1, 2, 2, 3) + << 1 << 2 << (IntList() << int(QtTestTableView::MoveDown) << int(QtTestTableView::MoveDown)) + << 5 << 2; + + QTest::newRow("Move through span up") << -1 << -1 + << IntList() + << IntList() + << QRect(1, 2, 2, 3) + << 5 << 2 << (IntList() << int(QtTestTableView::MoveUp) << int(QtTestTableView::MoveUp)) + << 1 << 2; +} + +void tst_QTableView::moveCursorStrikesBack() +{ + QFETCH(int, hideRow); + QFETCH(int, hideColumn); + QFETCH(IntList, disableRows); + QFETCH(IntList, disableColumns); + QFETCH(QRect, span); + + QFETCH(int, startRow); + QFETCH(int, startColumn); + QFETCH(IntList, cursorMoveActions); + QFETCH(int, expectedRow); + QFETCH(int, expectedColumn); + + QtTestTableModel model(7, 7); + QtTestTableView view; + view.setModel(&model); + view.hideRow(hideRow); + view.hideColumn(hideColumn); + + foreach (int row, disableRows) + model.disableRow(row); + foreach (int column, disableColumns) + model.disableColumn(column); + + if (span.height() && span.width()) + view.setSpan(span.top(), span.left(), span.height(), span.width()); + view.show(); + + QModelIndex index = model.index(startRow, startColumn); + view.setCurrentIndex(index); + + int newRow = -1; + int newColumn = -1; + foreach (int cursorMoveAction, cursorMoveActions) { + QModelIndex newIndex = view.moveCursor((QtTestTableView::CursorAction)cursorMoveAction, 0); + view.setCurrentIndex(newIndex); + newRow = newIndex.row(); + newColumn = newIndex.column(); + } + + // expected fails, task 119433 + if(newRow == -1) + return; + QCOMPARE(newRow, expectedRow); + QCOMPARE(newColumn, expectedColumn); +} + void tst_QTableView::hideRows_data() { QTest::addColumn("rowCount"); -- cgit v0.12 From f55d36cea933d801d421562f3d8b6b2ff0fb0fc1 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 12 Oct 2009 18:01:19 +0200 Subject: Fixes tst_QProxyWidget::palettePropagation on Mac QPalette() and QApplication::palette("QLineEdit") are not the same on Mac We really want QPalette() here because the graphics widget is not supposed to inherit from the widget it is proxying. Reviewed-by: Alexis --- tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp index d016461..9db34bb 100644 --- a/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp +++ b/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -2795,13 +2795,13 @@ void tst_QGraphicsProxyWidget::palettePropagation() QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 0); QVERIFY(edit->testAttribute(Qt::WA_SetPalette)); QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette)); - QCOMPARE(proxy.palette(), lineEditPalette); + QCOMPARE(proxy.palette(), QPalette()); edit->setPalette(QPalette()); QCOMPARE(editSpy.counts[QEvent::PaletteChange], 2); QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 0); QVERIFY(!edit->testAttribute(Qt::WA_SetPalette)); QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette)); - QCOMPARE(proxy.palette(), lineEditPalette); + QCOMPARE(proxy.palette(), QPalette()); // Proxy to widget proxy.setPalette(palette); -- cgit v0.12 From 6a1be803bf94b8af4a4766e31df9b441d1c81eed Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 12 Oct 2009 20:00:17 +0200 Subject: QGraphicsView tests on X11 --- tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp | 2 +- tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp | 2 ++ tests/auto/qgraphicsview/tst_qgraphicsview.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp index c6ee237..49b76ac 100644 --- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp @@ -6183,7 +6183,7 @@ void tst_QGraphicsItem::opacity2() MyGraphicsView view(&scene); view.show(); QTest::qWaitForWindowShown(&view); - QTRY_VERIFY(view.repaints > 1); + QTRY_VERIFY(view.repaints >= 1); #define RESET_REPAINT_COUNTERS \ parent->repaints = 0; \ diff --git a/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp index 9db34bb..58d7896 100644 --- a/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp +++ b/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -3059,8 +3059,10 @@ void tst_QGraphicsProxyWidget::actionsContextMenu() QGraphicsView view(&scene); view.show(); + QApplication::setActiveWindow(&view); QTest::qWaitForWindowShown(&view); view.setFocus(); + QTRY_VERIFY(view.hasFocus()); if (hasFocus) scene.addWidget(widget)->setFocus(); diff --git a/tests/auto/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/qgraphicsview/tst_qgraphicsview.cpp index 926335f..8acaa72 100644 --- a/tests/auto/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/qgraphicsview/tst_qgraphicsview.cpp @@ -2945,7 +2945,7 @@ void tst_QGraphicsView::task239729_noViewUpdate() view->show(); QTest::qWaitForWindowShown(view); - QTRY_VERIFY(spy.count() > 1); + QTRY_VERIFY(spy.count() >= 1); spy.reset(); scene.update(); QApplication::processEvents(); -- cgit v0.12