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