File indexing completed on 2024-05-05 04:49:24
0001 /**************************************************************************************** 0002 * Copyright (c) 2009 Thomas Luebking <thomas.luebking@web.de> * 0003 * Copyright (c) 2010 Mark Kretschmann <kretschmann@kde.org> * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify it under * 0006 * the terms of the GNU General Public License as published by the Free Software * 0007 * Foundation; either version 2 of the License, or (at your option) any later * 0008 * version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0012 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0013 * * 0014 * You should have received a copy of the GNU General Public License along with * 0015 * this program. If not, see <http://www.gnu.org/licenses/>. * 0016 ****************************************************************************************/ 0017 0018 #include "AnimatedLabelStack.h" 0019 0020 #include <QPainter> 0021 #include <QPaintEvent> 0022 #include <QTimer> 0023 0024 static const int frameTime = 50; 0025 static const int normalDisplayTime = 7000; 0026 0027 AnimatedLabelStack::AnimatedLabelStack( const QStringList &data, QWidget *p, Qt::WindowFlags f ): QWidget(p, f) 0028 , m_align(Qt::AlignCenter) 0029 , m_animTimer(0) 0030 , m_sleepTimer(0) 0031 , m_time(0) 0032 , m_fadeTime(300) 0033 , m_displayTime(normalDisplayTime) 0034 , m_index(0) 0035 , m_visibleIndex(0) 0036 , m_opacity(255) 0037 , m_targetOpacity(255) 0038 , m_animated(true) 0039 , m_pulsating(false) 0040 , m_pulseRequested(false) 0041 , m_explicit(false) 0042 , m_isClick(false) 0043 { 0044 setContentsMargins( 0, 0, 0, 0 ); 0045 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding); 0046 setData( data ); 0047 } 0048 0049 void 0050 AnimatedLabelStack::activateOnEnter() 0051 { 0052 if ( m_data.isEmpty() || !underMouse() || m_pulsating || m_explicit ) 0053 return; 0054 if ( m_animated ) 0055 { 0056 m_pulseRequested = true; 0057 if ( m_time > m_fadeTime && m_time < (m_displayTime - m_fadeTime) ) 0058 m_time = m_displayTime - m_fadeTime; 0059 wakeUp(); 0060 } 0061 else 0062 setPulsating( true ); 0063 } 0064 0065 void 0066 AnimatedLabelStack::ensureAnimationStatus() 0067 { 0068 if ( m_data.count() > 1 && ( m_animated || m_pulsating ) ) 0069 { 0070 wakeUp(); 0071 } 0072 else 0073 { 0074 if ( m_animTimer ) 0075 { 0076 killTimer( m_animTimer ); 0077 m_animTimer = 0; 0078 } 0079 if ( m_sleepTimer ) 0080 { 0081 killTimer( m_sleepTimer ); 0082 m_sleepTimer = 0; 0083 } 0084 m_opacity = m_targetOpacity; 0085 update(); 0086 } 0087 } 0088 0089 void 0090 AnimatedLabelStack::enterEvent( QEvent * ) 0091 { 0092 // wait a short time, then pulse through entries 0093 m_explicit = false; 0094 QTimer::singleShot(300, this, &AnimatedLabelStack::activateOnEnter ); 0095 } 0096 0097 void 0098 AnimatedLabelStack::hideEvent( QHideEvent *e ) 0099 { 0100 QWidget::hideEvent( e ); 0101 if ( m_animTimer ) 0102 { 0103 killTimer( m_animTimer ); 0104 m_animTimer = 0; 0105 } 0106 if ( m_sleepTimer ) 0107 { 0108 killTimer( m_sleepTimer ); 0109 m_sleepTimer = 0; 0110 } 0111 m_opacity = m_targetOpacity; 0112 } 0113 0114 void 0115 AnimatedLabelStack::leaveEvent( QEvent * ) 0116 { 0117 m_explicit = false; 0118 m_pulseRequested = false; 0119 } 0120 0121 void 0122 AnimatedLabelStack::mousePressEvent( QMouseEvent *me ) 0123 { 0124 if ( me->button() != Qt::LeftButton || m_data.isEmpty() ) 0125 return; 0126 0127 m_isClick = true; 0128 me->accept(); 0129 } 0130 0131 void 0132 AnimatedLabelStack::mouseReleaseEvent( QMouseEvent *me ) 0133 { 0134 if ( me->button() != Qt::LeftButton || m_data.isEmpty() ) 0135 return; 0136 0137 me->accept(); 0138 if ( m_isClick && underMouse() ) 0139 { 0140 m_isClick = false; 0141 if ( !m_data.isEmpty() ) 0142 Q_EMIT clicked ( m_data.at( m_visibleIndex ) ); 0143 } 0144 } 0145 0146 void 0147 AnimatedLabelStack::paintEvent( QPaintEvent * pe ) 0148 { 0149 if ( m_data.isEmpty() ) 0150 return; 0151 0152 QPainter p(this); 0153 p.setClipRegion( pe->region() ); 0154 0155 QColor c( palette().color( foregroundRole() ) ); 0156 c.setAlpha( m_targetOpacity ); 0157 0158 if ( m_animTimer ) // currently animated 0159 { 0160 if ( m_opacity != m_targetOpacity ) // we're in transition period 0161 { 0162 if ( !m_pulsating ) 0163 { 0164 c.setAlpha( qAbs(m_targetOpacity - m_opacity) ); 0165 p.setPen( c ); 0166 int index = m_visibleIndex - 1; 0167 if (index < 0) 0168 index = m_data.count() - 1; 0169 0170 p.drawText( textRect(), m_align | Qt::TextSingleLine, elidedText( m_data.at( index ) ) ); 0171 } 0172 0173 c.setAlpha( m_opacity ); 0174 } 0175 } 0176 0177 p.setPen( c ); 0178 p.drawText( textRect(), m_align | Qt::TextSingleLine, elidedText( m_data.at( m_visibleIndex ) ) ); 0179 p.end(); 0180 } 0181 0182 void 0183 AnimatedLabelStack::showEvent( QShowEvent *e ) 0184 { 0185 ensureAnimationStatus(); 0186 QWidget::showEvent( e ); 0187 } 0188 0189 0190 QString 0191 AnimatedLabelStack::elidedText( const QString& text ) const 0192 { 0193 const QFontMetrics fontMetrics( font() ); 0194 0195 QString newText = fontMetrics.elidedText( text, Qt::ElideRight, textRect().width() - 2 ); 0196 0197 // Insert a whitespace between text and "..." (looks nicer) 0198 if( newText != text ) 0199 newText.insert( newText.length() -1, ' ' ); 0200 0201 0202 return newText; 0203 } 0204 0205 void 0206 AnimatedLabelStack::pulse( int /*cycles*/, int /*minimum*/ ) 0207 { 0208 //TODO: handle parameters... 0209 activateOnEnter(); 0210 } 0211 0212 void 0213 AnimatedLabelStack::setAlign( Qt::Alignment align ) 0214 { 0215 m_align = Qt::AlignVCenter; 0216 if ( align & Qt::AlignLeft ) 0217 m_align |= Qt::AlignLeft; 0218 else if ( align & Qt::AlignRight ) 0219 m_align |= Qt::AlignRight; 0220 else 0221 m_align = Qt::AlignCenter; 0222 } 0223 0224 0225 void 0226 AnimatedLabelStack::setAnimated( bool on ) 0227 { 0228 m_animated = on; 0229 ensureAnimationStatus(); 0230 } 0231 0232 void 0233 AnimatedLabelStack::setBold( bool bold ) 0234 { 0235 QFont fnt = font(); 0236 fnt.setBold(bold); 0237 setFont(fnt); 0238 setMinimumHeight( QFontMetrics(fnt).height() + 4 ); 0239 } 0240 0241 void 0242 AnimatedLabelStack::setData( const QStringList &data ) 0243 { 0244 if ( data == m_data ) 0245 return; 0246 m_data = data; 0247 m_time = 0; 0248 m_index = 0; 0249 m_visibleIndex = 0; 0250 ensureAnimationStatus(); 0251 update(); 0252 } 0253 0254 void 0255 AnimatedLabelStack::setPadding( int left, int right ) 0256 { 0257 m_padding[0] = left; 0258 m_padding[1] = right; 0259 update(); 0260 } 0261 0262 void 0263 AnimatedLabelStack::setPulsating( bool on ) 0264 { 0265 if ( m_pulseRequested == on && m_pulsating == on ) 0266 return; 0267 m_pulseRequested = on; 0268 m_pulsating = on; 0269 if ( m_pulsating ) 0270 { 0271 m_displayTime = 1200; 0272 m_fadeTime = 300; 0273 if ( m_time > m_fadeTime && m_time < m_displayTime - m_fadeTime ) 0274 m_time = m_displayTime - m_fadeTime + 1; // for instant reaction 0275 } 0276 else 0277 { 0278 m_displayTime = normalDisplayTime; 0279 m_fadeTime = 300; 0280 if ( !m_animated ) 0281 m_time = m_fadeTime + 1; 0282 } 0283 ensureAnimationStatus(); 0284 Q_EMIT pulsing( on ); 0285 } 0286 0287 void 0288 AnimatedLabelStack::sleep( int ms ) 0289 { 0290 if ( m_animTimer ) 0291 { 0292 killTimer( m_animTimer ); 0293 m_animTimer = 0; 0294 } 0295 if ( !m_sleepTimer ) 0296 m_sleepTimer = startTimer( ms ); 0297 } 0298 0299 void 0300 AnimatedLabelStack::wakeUp() 0301 { 0302 if ( m_sleepTimer ) 0303 { 0304 killTimer( m_sleepTimer ); 0305 m_sleepTimer = 0; 0306 } 0307 if ( !m_animTimer ) 0308 m_animTimer = startTimer( frameTime ); 0309 } 0310 0311 void 0312 AnimatedLabelStack::timerEvent( QTimerEvent * te ) 0313 { 0314 0315 if ( !isVisible() ) 0316 return; 0317 if ( te->timerId() == m_sleepTimer ) 0318 wakeUp(); 0319 else if ( te->timerId() != m_animTimer ) 0320 return; 0321 0322 if ( m_explicit ) 0323 return; // the user explicitly altered content by wheeling, don't take it away 0324 0325 if ( m_time < m_fadeTime || m_time > (m_displayTime - m_fadeTime) ) 0326 update(); 0327 0328 m_time += frameTime; 0329 if ( m_time > m_displayTime ) 0330 { 0331 m_time = 0; 0332 if ( m_pulsating && !m_pulseRequested ) 0333 m_visibleIndex = m_index; 0334 else 0335 { 0336 ++m_visibleIndex; 0337 if ( m_visibleIndex >= m_data.count() ) 0338 m_visibleIndex = 0; 0339 } 0340 if ( !m_pulsating ) 0341 m_index = m_visibleIndex; 0342 } 0343 0344 if ( m_time < m_fadeTime ) // fade in 0345 { 0346 if ( m_pulseRequested && !m_pulsating ) 0347 setPulsating( true ); 0348 m_opacity = m_targetOpacity*m_time/m_fadeTime; 0349 wakeUp(); 0350 } 0351 else if ( m_pulsating && m_time > (m_displayTime - m_fadeTime) ) // fade out 0352 { 0353 m_opacity = m_targetOpacity*(m_displayTime - m_time)/m_fadeTime; 0354 wakeUp(); 0355 } 0356 else // (ensure) no fade 0357 { 0358 if ( !m_pulsating && m_time < (m_displayTime - m_fadeTime) ) 0359 { 0360 m_time = m_displayTime - m_fadeTime + 1; 0361 sleep( m_time ); 0362 } 0363 0364 m_opacity = m_targetOpacity; // to be sure 0365 0366 if ( m_pulsating && !m_pulseRequested && m_index == m_visibleIndex ) 0367 setPulsating( false ); 0368 } 0369 } 0370 0371 void 0372 AnimatedLabelStack::wheelEvent( QWheelEvent * we ) 0373 { 0374 if ( we->modifiers() & Qt::ControlModifier ) 0375 { 0376 we->accept(); 0377 if ( m_data.count() < 2 ) 0378 return; 0379 0380 setPulsating( false ); 0381 0382 if ( we->angleDelta().y() < 0 ) //FIXME: check if .x() must be used 0383 { 0384 ++m_visibleIndex; 0385 if ( m_visibleIndex >= m_data.count() ) 0386 m_visibleIndex = 0; 0387 } 0388 else 0389 { 0390 --m_visibleIndex; 0391 if ( m_visibleIndex < 0 ) 0392 m_visibleIndex = m_data.count() - 1; 0393 } 0394 m_index = m_visibleIndex; 0395 m_time = m_fadeTime + 1; 0396 m_explicit = true; 0397 update(); 0398 } 0399 else 0400 we->ignore(); 0401 } 0402