KTutorial
0.5.1
|
00001 /*************************************************************************** 00002 * Copyright (C) 2010 by Daniel Calviño Sánchez <danxuliu@gmail.com> * 00003 * Copyright (C) 2011 by Daniel Calviño Sánchez <danxuliu@gmail.com> * 00004 * Copyright (C) 2012 by Daniel Calviño Sánchez <danxuliu@gmail.com> * 00005 * * 00006 * This program is free software; you can redistribute it and/or modify * 00007 * it under the terms of the GNU General Public License as published by * 00008 * the Free Software Foundation; either version 2 of the License, or * 00009 * (at your option) any later version. * 00010 * * 00011 * This program is distributed in the hope that it will be useful, * 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00014 * GNU General Public License for more details. * 00015 * * 00016 * You should have received a copy of the GNU General Public License * 00017 * along with this program; If not, see <http://www.gnu.org/licenses/>. * 00018 ***************************************************************************/ 00019 00020 #include "WidgetHighlighter.h" 00021 00022 #include <QPainter> 00023 #include <QPaintEvent> 00024 00025 namespace ktutorial { 00026 namespace extendedinformation { 00027 00028 //public: 00029 00030 WidgetHighlighter::WidgetHighlighter(QWidget* targetWidget): 00031 QWidget(targetWidget), 00032 mTargetWidget(targetWidget) { 00033 Q_ASSERT(targetWidget); 00034 00035 setAttribute(Qt::WA_TransparentForMouseEvents); 00036 setFocusPolicy(Qt::NoFocus); 00037 00038 resize(mTargetWidget->size()); 00039 mTargetWidget->installEventFilter(this); 00040 00041 //TODO Use QPropertyAnimation instead? Increase Qt version requirement in 00042 //CMakeLists.txt to Qt 4.6 if done. 00043 mProgress = 0; 00044 mIncreasing = true; 00045 mStopping = false; 00046 00047 int interval = 80; 00048 int duration = 1000; 00049 mProgressForEachTick = interval / (qreal)duration; 00050 00051 mTimer.setInterval(interval); 00052 connect(&mTimer, SIGNAL(timeout()), this, SLOT(updateProgress())); 00053 00054 mFrameWidth = 6; 00055 00056 show(); 00057 } 00058 00059 bool WidgetHighlighter::eventFilter(QObject* watched, QEvent* event) { 00060 if (watched != mTargetWidget || event->type() != QEvent::Resize) { 00061 return false; 00062 } 00063 00064 QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event); 00065 resize(resizeEvent->size()); 00066 00067 return false; 00068 } 00069 00070 //public slots: 00071 00072 void WidgetHighlighter::start() { 00073 mStopping = false; 00074 mIncreasing = true; 00075 00076 mTimer.start(); 00077 } 00078 00079 void WidgetHighlighter::stop() { 00080 mStopping = true; 00081 mIncreasing = false; 00082 00083 if (mProgress == 0) { 00084 mTimer.stop(); 00085 emit stopped(this); 00086 } 00087 } 00088 00089 //protected: 00090 00091 void WidgetHighlighter::paintEvent(QPaintEvent* event) { 00092 Q_UNUSED(event); 00093 00094 //Painting the WidgetHighlighter over its parent widget with a 00095 //semi-transparent color is the best I could get. However, it has some 00096 //flaws. For example, a QMenu does not highlight its menu button. And some 00097 //widgets may be hardly highlighted if their background color is almost the 00098 //same as the highlight color (although it should not happen very often). 00099 // 00100 //Changing the parent widget palette does not work because some widgets 00101 //(like tool buttons) don't paint a background, only paint the text and get 00102 //its parent widget background. Forcing the painting of the background with 00103 //some color role does not help, as it may look ugly in some styles that use 00104 //a gradient for the background. Changing the palette has one benefit, 00105 //though, as it also changes the palette from the children widgets, which 00106 //means that a QMenu would highlight its menu button. 00107 // 00108 //Ideally, the highlighter should lighten its parent widget but when it is 00109 //too bright (for example, the white background of a text edit). In that 00110 //case, the parent widget should be darkened. To do this, however, the 00111 //WidgetHighlighter must know how its parent widget is painted. 00112 // 00113 //Calling QPixmap::grabWidget from the WidgetHighlighter::paintEvent is not 00114 //good, as it triggers a recursive paint event in its parent (provided the 00115 //WidgetHighlighter paintEvent is guarded against a recursive call, else the 00116 //application would directly hang). Calling it from the updateProgress and 00117 //storing the QPixmap in memory would theoretically work, but it showed some 00118 //strange artifacts. 00119 // 00120 //Setting a custom QGraphicsEffect does not seem like a good idea, as Qt can 00121 //be compiled without them, and because, as far as I know, only one effect 00122 //can be used on a widget at a time (as making a Composite design pattern 00123 //with something like QComposedGraphicsEffect class is pretty easy and there 00124 //is no such class, I presume that it is not a good idea to use several 00125 //effects on the same widget, at least for now). 00126 // 00127 //Anyway, I do not know how to check whether a picture is light or dark 00128 //quickly enough to be done in a realtime animation, so... a 00129 //semi-transparent colored child widget is good enough until someone makes 00130 //something better ;) 00131 // 00132 //However, filling the whole widget and animating it is too CPU intensive 00133 //when tinting a parent widget with lots of child widgets (for example, the 00134 //main widget in showfoto). So, instead of tinting the whole parent only 00135 //a frame is painted and animated. In this case, the frame gets its full 00136 //opacity at the peak of the animation, instead of half the opacity as used 00137 //when tinting the whole widget. 00138 00139 //The inactive palette does not usually have a lot of contrast between 00140 //normal color and highlight color (for example, a QStatusBar in Oxygen 00141 //style). The highlight color from the active palette is used in every case 00142 //to ensure that the widget is clearly highlighted, even if the window is 00143 //not the active one. 00144 QColor color = mTargetWidget->palette().color(QPalette::Active, 00145 QPalette::Highlight); 00146 00147 QPainter painter(this); 00148 painter.setOpacity(mProgress); 00149 00150 QPen pen; 00151 pen.setWidth(mFrameWidth); 00152 pen.setColor(color); 00153 painter.setPen(pen); 00154 00155 //Third and fourth arguments are width and height, not end coordinates 00156 painter.drawRect(pen.width() / 2, pen.width() / 2, 00157 width() - pen.width(), height() - pen.width()); 00158 painter.end(); 00159 } 00160 00161 //private slots: 00162 00163 void WidgetHighlighter::updateProgress(){ 00164 if (mIncreasing) { 00165 mProgress += mProgressForEachTick; 00166 mProgress = qMin<qreal>(1, mProgress); 00167 mIncreasing = mProgress < 1; 00168 } else { 00169 mProgress -= mProgressForEachTick; 00170 mProgress = qMax<qreal>(0, mProgress); 00171 mIncreasing = mProgress == 0; 00172 } 00173 00174 //Update just the frame of the widget instead of the whole widget for 00175 //performance 00176 update(0, 0, width(), mFrameWidth); 00177 update(0, 0, mFrameWidth, height()); 00178 update(width() - mFrameWidth, 0, width(), height()); 00179 update(0, height() - mFrameWidth, width(), height()); 00180 00181 if (mStopping && mProgress == 0) { 00182 mTimer.stop(); 00183 emit stopped(this); 00184 } 00185 } 00186 00187 } 00188 }