[Ktutorial-commits] SF.net SVN: ktutorial:[240] trunk/ktutorial/ktutorial-library
Status: Alpha
Brought to you by:
danxuliu
From: <dan...@us...> - 2010-05-18 15:37:19
|
Revision: 240 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=240&view=rev Author: danxuliu Date: 2010-05-18 15:37:12 +0000 (Tue, 18 May 2010) Log Message: ----------- Change the way widgets are highlighted: use a semi-transparent child widget over the widget to be highlighted instead of changing its palette, as some widgets like QToolButtons don't paint a background, so changing its palette had no noticeable visual effect. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp 2010-05-18 15:29:49 UTC (rev 239) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp 2010-05-18 15:37:12 UTC (rev 240) @@ -16,10 +16,9 @@ * along with this program; If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ -#include <QWidget> +#include <QPainter> +#include <QPaintEvent> -#include <KColorUtils> - #include "WidgetHighlighter.h" namespace extendedinformation { @@ -27,12 +26,16 @@ //public: WidgetHighlighter::WidgetHighlighter(QWidget* targetWidget): - QObject(targetWidget), + QWidget(targetWidget), mTargetWidget(targetWidget) { Q_ASSERT(targetWidget); - mOriginalPalette = targetWidget->palette(); + setAttribute(Qt::WA_TransparentForMouseEvents); + setFocusPolicy(Qt::NoFocus); + resize(mTargetWidget->size()); + mTargetWidget->installEventFilter(this); + //TODO Use QPropertyAnimation instead? Increase Qt version requirement in //CMakeLists.txt to Qt 4.6 if done. mProgress = 0; @@ -44,11 +47,20 @@ mProgressForEachTick = interval / (qreal)duration; mTimer.setInterval(interval); - connect(&mTimer, SIGNAL(timeout()), this, SLOT(update())); + connect(&mTimer, SIGNAL(timeout()), this, SLOT(updateProgress())); + + show(); } -WidgetHighlighter::~WidgetHighlighter() { - mTargetWidget->setPalette(mOriginalPalette); +bool WidgetHighlighter::eventFilter(QObject* watched, QEvent* event) { + if (watched != mTargetWidget || event->type() != QEvent::Resize) { + return false; + } + + QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event); + resize(resizeEvent->size()); + + return false; } //public slots: @@ -70,35 +82,59 @@ } } -//private: +//protected: -void WidgetHighlighter::updateColorGroup(QPalette& palette, - QPalette::ColorGroup colorGroup) { - updateColorRole(palette, colorGroup, QPalette::Window, QPalette::Highlight); - updateColorRole(palette, colorGroup, QPalette::WindowText, - QPalette::HighlightedText); - updateColorRole(palette, colorGroup, QPalette::Base, QPalette::Highlight); - updateColorRole(palette, colorGroup, QPalette::Text, - QPalette::HighlightedText); - updateColorRole(palette, colorGroup, QPalette::Button, QPalette::Highlight); - updateColorRole(palette, colorGroup, QPalette::ButtonText, - QPalette::HighlightedText); -} +void WidgetHighlighter::paintEvent(QPaintEvent* event) { + //Painting the WidgetHighlighter over its parent widget with a + //semi-transpaernt color is the best I could get. However, it has some + //flaws. For example, a QMenu does not highlight its menu button. And some + //widgets may be hardly highlighted if their background color is almost the + //same as the highlight color (like QStatusBar in Oxygen style and default + //colors). + // + //Changing the parent widget palette does not work because some widgets + //(like tool buttons) don't paint a background, only paint the text and get + //its parent widget background. Forcing the painting of the background with + //some color role does not help, as it may look ugly in some styles that use + //a gradient for the background. Changing the palette has one benefit, + //though, as it also changes the palette from the children widgets, which + //means that a QMenu would highlight its menu button. + // + //Ideally, the highlighter should lighten its parent widget but when it is + //too bright (for example, the white background of a text edit). In that + //case, the parent widget should be darkened. To do this, however, the + //WidgetHighlighter must know how its parent widget is painted. + // + //Calling QPixmap::grabWidget from the WidgetHighlighter::paintEvent is not + //good, as it triggers a recursive paint event in its parent (provided the + //WidgetHighlighter paintEvent is guarded against a recursive call, else the + //application would directly hang). Calling it from the updateProgress and + //storing the QPixmap in memory would theoretically work, but it showed some + //strange artifacts. + // + //Setting a custom QGraphicsEffect does not seem like a good idea, as Qt can + //be compiled without them, and because, as far as I know, only one effect + //can be used on a widget at a time (as making a Composite design pattern + //with something like QComposedGraphicsEffect class is pretty easy and there + //is no such class, I presume that it is not a good idea to use several + //effects on the same widget, at least for now). + // + //Anyway, I do not know how to check whether a picture is light or dark + //quickly enough to be done in a realtime animation, so... a + //semi-transparent colored child widget is good enough until someone makes + //something better ;) -void WidgetHighlighter::updateColorRole(QPalette& palette, - QPalette::ColorGroup colorGroup, - QPalette::ColorRole base, - QPalette::ColorRole tint) { - qreal amount = 0.6 * mProgress; - QColor color = KColorUtils::tint(palette.color(colorGroup, base), - palette.color(colorGroup, tint), - amount); - palette.setColor(colorGroup, base, color); + QColor color = mTargetWidget->palette().color(QPalette::Highlight); + + QPainter painter(this); + painter.setOpacity(mProgress / 2.0f); + painter.fillRect(event->rect(), color); + painter.end(); } //private slots: -void WidgetHighlighter::update(){ +void WidgetHighlighter::updateProgress(){ if (mIncreasing) { mProgress += mProgressForEachTick; mProgress = qMin<qreal>(1, mProgress); @@ -109,13 +145,8 @@ mIncreasing = mProgress == 0; } - QPalette updatedPalette = mOriginalPalette; - updateColorGroup(updatedPalette, QPalette::Active); - updateColorGroup(updatedPalette, QPalette::Inactive); - updateColorGroup(updatedPalette, QPalette::Disabled); + update(); - mTargetWidget->setPalette(updatedPalette); - if (mStopping && mProgress == 0) { mTimer.stop(); emit stopped(this); Modified: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h 2010-05-18 15:29:49 UTC (rev 239) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h 2010-05-18 15:37:12 UTC (rev 240) @@ -19,32 +19,35 @@ #ifndef WIDGETHIGHLIGHTER_H #define WIDGETHIGHLIGHTER_H -#include <QObject> -#include <QPalette> #include <QTimer> +#include <QWidget> namespace extendedinformation { /** * Utility class to highlight a widget. - * WidgetHighlighter executes an animation that modifies the color palette of - * some widget, changing its colors from normal to highlighted and back again. + * WidgetHighlighter executes an animation that tints its parent widget, + * changing its colors from normal to highlighted and back again. * * Once started, the animation goes on indefinitely until the stop slot is - * called. The colors of the palette are set back to the original colors - * animating the change from the current color of the widget to the normal one. + * called. The parent widget recovers its original appearance animating the + * change from the current color of the widget to the normal one. * If the highlighting is started again while the stop animation is running, the * highlighting animation is started again from the current color (that is, the * stop animation is cancelled and a new highlight animation is started from * that point). * + * To tint the widget, the WidgetHighlighter paints itself over the whole parent + * widget with a semi-transparent highlight color, so the parent widget can + * still be seen but tinted with the highlight color. + * * WidgetHighlighter should not be created directly. Instead, * WidgetHighlighterManager should be used, as it takes care of deleting the * highlighter when no longer needed. * * @see WidgetHighlighterManager */ -class WidgetHighlighter: public QObject { +class WidgetHighlighter: public QWidget { Q_OBJECT public: @@ -56,10 +59,14 @@ explicit WidgetHighlighter(QWidget* targetWidget); /** - * Destroys this WidgetHighlighter. - * It restores the original palette to the target widget. + * Resizes this WidgetHighlighter to the size of its parent widget when it + * receives a resize event. + * + * @param watched The filtered object that received an event. + * @param event The event received. + * @return False, to allow the event to be handled further. */ - virtual ~WidgetHighlighter(); + virtual bool eventFilter(QObject* watched, QEvent* event); public Q_SLOTS: @@ -89,6 +96,16 @@ */ void stopped(extendedinformation::WidgetHighlighter* widgetHighlighter); +protected: + + /** + * Fills the widget with a semi-transparent highlight color. + * The degree of opacity depends on the progress of the animation. + * + * @param event The paint event. + */ + virtual void paintEvent(QPaintEvent* event); + private: /** @@ -97,11 +114,6 @@ QWidget* mTargetWidget; /** - * The original palette used by the widget. - */ - QPalette mOriginalPalette; - - /** * Timer to update the colors. */ QTimer mTimer; @@ -127,33 +139,12 @@ */ bool mStopping; - /** - * Updates the color group of the given palette based on the current - * progress. - * - * @param palette The palette to update its colors. - * @param colorGroup The color group of the palette to update. - */ - void updateColorGroup(QPalette& palette, QPalette::ColorGroup colorGroup); - - /** - * Updates the color role "base" tinting it with the color role "tint". - * How much the base color role is tinted depends on the current progress. - * - * @param palette The palette to update its colors. - * @param colorGroup The color group of the palette to update. - * @param base The color role to update. - * @param tint The color role to tint the base color role with. - */ - void updateColorRole(QPalette& palette, QPalette::ColorGroup colorGroup, - QPalette::ColorRole from, QPalette::ColorRole to); - private Q_SLOTS: /** - * Updates the palette of the target widget based on the animation progress. + * Updates the animation progress. */ - void update(); + void updateProgress(); }; Modified: trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp 2010-05-18 15:29:49 UTC (rev 239) +++ trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp 2010-05-18 15:37:12 UTC (rev 240) @@ -21,7 +21,11 @@ #include <QWidget> #include "WidgetHighlighterManager.h" +#define protected public +#define private public #include "WidgetHighlighter.h" +#undef private +#undef protected namespace extendedinformation { @@ -39,6 +43,10 @@ void testDeleteWidgetWhileHighlighting(); +private: + + WidgetHighlighter* highlighterOf(const QWidget* widget) const; + }; void WidgetHighlighterManagerTest::testSelf() { @@ -52,7 +60,6 @@ void WidgetHighlighterManagerTest::testHighlight() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); manager->highlight(&widget); @@ -61,8 +68,8 @@ QTest::qWait(100); QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 1); - QVERIFY(widget.findChild<WidgetHighlighter*>()); - QVERIFY(widget.palette() != palette); + QVERIFY(highlighterOf(&widget)); + QVERIFY(highlighterOf(&widget)->mProgress > 0); } void WidgetHighlighterManagerTest::testHighlightWidgetAlreadyHighlighted() { @@ -71,12 +78,12 @@ manager->highlight(&widget); - WidgetHighlighter* highlighter = widget.findChild<WidgetHighlighter*>(); + WidgetHighlighter* highlighter = highlighterOf(&widget); manager->highlight(&widget); QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 1); - QCOMPARE(widget.findChild<WidgetHighlighter*>(), highlighter); + QCOMPARE(highlighterOf(&widget), highlighter); } void WidgetHighlighterManagerTest::testHighlightAfterStopHighlighting() { @@ -85,28 +92,25 @@ manager->highlight(&widget); - QPointer<WidgetHighlighter> highlighter = - widget.findChild<WidgetHighlighter*>(); + QPointer<WidgetHighlighter> highlighter = highlighterOf(&widget); QVERIFY(highlighter); manager->stopHighlighting(&widget); QVERIFY(!highlighter); - QPalette palette = widget.palette(); manager->highlight(&widget); //Give it some time to update QTest::qWait(100); QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 1); - QVERIFY(widget.findChild<WidgetHighlighter*>()); - QVERIFY(widget.palette() != palette); + QVERIFY(highlighterOf(&widget)); + QVERIFY(highlighterOf(&widget)->mProgress > 0); } void WidgetHighlighterManagerTest::testStopHighlighting() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); manager->highlight(&widget); @@ -120,7 +124,6 @@ QTest::qWait(200); QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 0); - QVERIFY(widget.palette() == palette); } void WidgetHighlighterManagerTest::testDeleteWidgetWhileHighlighting() { @@ -137,8 +140,20 @@ //No explicit check is made, if it does not crash everything is fine ;) } +WidgetHighlighter* WidgetHighlighterManagerTest::highlighterOf( + const QWidget* widget) const { + WidgetHighlighter* highlighter = widget->findChild<WidgetHighlighter*>(); + + //Ensure that it is a direct child + if (widget->children().contains(highlighter)) { + return highlighter; + } + + return 0; } +} + QTEST_MAIN(extendedinformation::WidgetHighlighterManagerTest) #include "WidgetHighlighterManagerTest.moc" Modified: trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp 2010-05-18 15:29:49 UTC (rev 239) +++ trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp 2010-05-18 15:37:12 UTC (rev 240) @@ -39,47 +39,33 @@ void testConstructor(); - void testDestructor(); - void testStart(); void testStartAlreadyStarted(); void testStartWhileStopAnimationIsRunning(); - void testUpdate(); - void testUpdateWhenProgressIsAlmostOne(); - void testUpdateWhenProgressIsAlmostZero(); + void testUpdateProgress(); + void testUpdateProgressWhenProgressIsAlmostOne(); + void testUpdateProgressWhenProgressIsAlmostZero(); void testStop(); void testStopAfterStopAnimationEnded(); void testStopImmediatelyAfterStart(); + void testParentWidgetResized(); + }; void WidgetHighlighterTest::testConstructor() { QWidget widget; - widget.setPalette(Qt::blue); - QApplication::setPalette(Qt::green); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); QCOMPARE(highlighter->parent(), &widget); - QVERIFY(highlighter->mOriginalPalette != QApplication::palette()); - QVERIFY(highlighter->mOriginalPalette == widget.palette()); + QCOMPARE(highlighter->size(), widget.size()); + QCOMPARE(highlighter->mProgress, 0.0); } -void WidgetHighlighterTest::testDestructor() { - QWidget widget; - QPalette palette = widget.palette(); - WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); - - widget.setPalette(QPalette(Qt::green)); - delete highlighter; - - QVERIFY(widget.palette() == palette); -} - void WidgetHighlighterTest::testStart() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->start(); @@ -87,12 +73,11 @@ //Give it some time to update QTest::qWait(100); - QVERIFY(widget.palette() != palette); + QVERIFY(highlighter->mProgress > 0); } void WidgetHighlighterTest::testStartAlreadyStarted() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->start(); @@ -105,12 +90,10 @@ //Ensure that progress is not reseted QCOMPARE(highlighter->mProgress, previousProgress); - QVERIFY(widget.palette() != palette); } void WidgetHighlighterTest::testStartWhileStopAnimationIsRunning() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->mProgress = 0.5; @@ -132,82 +115,59 @@ QVERIFY(highlighter->mProgress > 0.5); QVERIFY(highlighter->mIncreasing); QVERIFY(!highlighter->mStopping); - QVERIFY(widget.palette() != palette); } -void WidgetHighlighterTest::testUpdate() { +void WidgetHighlighterTest::testUpdateProgress() { QWidget widget; WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->mTimer.stop(); - QPalette palette = widget.palette(); int previousProgress = highlighter->mProgress; - highlighter->update(); + highlighter->updateProgress(); QCOMPARE(highlighter->mProgress, previousProgress + highlighter->mProgressForEachTick); - QVERIFY(widget.palette().color(QPalette::Window) != - palette.color(QPalette::Window)); - QVERIFY(widget.palette().color(QPalette::WindowText) != - palette.color(QPalette::WindowText)); - QVERIFY(widget.palette().color(QPalette::Base) != - palette.color(QPalette::Base)); - QVERIFY(widget.palette().color(QPalette::Text) != - palette.color(QPalette::Text)); - QVERIFY(widget.palette().color(QPalette::Button) != - palette.color(QPalette::Button)); - QVERIFY(widget.palette().color(QPalette::ButtonText) != - palette.color(QPalette::ButtonText)); } -void WidgetHighlighterTest::testUpdateWhenProgressIsAlmostOne() { +void WidgetHighlighterTest::testUpdateProgressWhenProgressIsAlmostOne() { QWidget widget; WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->mTimer.stop(); highlighter->mProgress = 0.995; highlighter->mIncreasing = true; - highlighter->update(); + highlighter->updateProgress(); - //Don't check palette changes here, as with such a small update it could - //have been left unchanged QCOMPARE(highlighter->mProgress, 1.0); QCOMPARE(highlighter->mIncreasing, false); - QPalette palette = widget.palette(); - highlighter->update(); + highlighter->updateProgress(); QCOMPARE(highlighter->mProgress, 1 - highlighter->mProgressForEachTick); QCOMPARE(highlighter->mIncreasing, false); - QVERIFY(widget.palette() != palette); } -void WidgetHighlighterTest::testUpdateWhenProgressIsAlmostZero() { +void WidgetHighlighterTest::testUpdateProgressWhenProgressIsAlmostZero() { QWidget widget; WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->mTimer.stop(); highlighter->mProgress = 0.005; highlighter->mIncreasing = false; - highlighter->update(); + highlighter->updateProgress(); - //Don't check palette changes here, as with such a small update it could - //have been left unchanged QCOMPARE(highlighter->mProgress, 0.0); QCOMPARE(highlighter->mIncreasing, true); - QPalette palette = widget.palette(); - highlighter->update(); + highlighter->updateProgress(); QCOMPARE(highlighter->mProgress, highlighter->mProgressForEachTick); QCOMPARE(highlighter->mIncreasing, true); - QVERIFY(widget.palette() != palette); } void WidgetHighlighterTest::testStop() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->mProgress = 0.5; @@ -225,12 +185,10 @@ QVERIFY(highlighter->mProgress < 0.5); QVERIFY(!highlighter->mIncreasing); QVERIFY(highlighter->mStopping); - QVERIFY(widget.palette() != palette); } void WidgetHighlighterTest::testStopAfterStopAnimationEnded() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->start(); @@ -252,7 +210,7 @@ QTest::qWait(200); QVERIFY(!highlighter->mTimer.isActive()); - QVERIFY(widget.palette() == palette); + QCOMPARE(highlighter->mProgress, 0.0); QCOMPARE(stoppedSpy.count(), 1); QVariant argument = stoppedSpy.at(0).at(0); QCOMPARE(argument.userType(), widgetHighlighterStarType); @@ -262,7 +220,6 @@ void WidgetHighlighterTest::testStopImmediatelyAfterStart() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->start(); @@ -278,7 +235,7 @@ highlighter->stop(); QVERIFY(!highlighter->mTimer.isActive()); - QVERIFY(widget.palette() == palette); + QCOMPARE(highlighter->mProgress, 0.0); QCOMPARE(stoppedSpy.count(), 1); QVariant argument = stoppedSpy.at(0).at(0); QCOMPARE(argument.userType(), widgetHighlighterStarType); @@ -286,8 +243,23 @@ highlighter); } +void WidgetHighlighterTest::testParentWidgetResized() { + QWidget widget; + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + + //The widget must be visible to ensure that it receives the resize event + widget.show(); + + //Resize twice to prevent a false test if the first size was the current + //widget size + widget.resize(4, 4); + widget.resize(108, 108); + + QCOMPARE(highlighter->size(), widget.size()); } +} + QTEST_MAIN(extendedinformation::WidgetHighlighterTest) #include "WidgetHighlighterTest.moc" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |