From 6af93841cb12f708336daabe79169939888180f9 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 5 Apr 2012 21:23:50 -0400 Subject: [PATCH] Try out some fading for album covers --- src/libtomahawk/CMakeLists.txt | 1 + src/libtomahawk/album.cpp | 8 +- src/libtomahawk/album.h | 1 + src/libtomahawk/artist.cpp | 7 +- src/libtomahawk/artist.h | 1 + .../playlist/albumitemdelegate.cpp | 41 +++- src/libtomahawk/playlist/albumitemdelegate.h | 12 +- src/libtomahawk/playlist/albumview.cpp | 2 + src/libtomahawk/playlist/albumview.h | 3 + src/libtomahawk/utils/PixmapDelegateFader.cpp | 221 ++++++++++++++++++ src/libtomahawk/utils/PixmapDelegateFader.h | 74 ++++++ src/libtomahawk/utils/closure.cpp | 5 +- src/libtomahawk/utils/closure.h | 3 + 13 files changed, 362 insertions(+), 17 deletions(-) create mode 100644 src/libtomahawk/utils/PixmapDelegateFader.cpp create mode 100644 src/libtomahawk/utils/PixmapDelegateFader.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 78401e69c..797f2acb1 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -120,6 +120,7 @@ set( libGuiSources utils/proxystyle.cpp utils/tomahawkutilsgui.cpp utils/closure.cpp + utils/PixmapDelegateFader.cpp widgets/animatedcounterlabel.cpp widgets/checkdirtree.cpp diff --git a/src/libtomahawk/album.cpp b/src/libtomahawk/album.cpp index 3b52e7166..02e7a5c8e 100644 --- a/src/libtomahawk/album.cpp +++ b/src/libtomahawk/album.cpp @@ -111,7 +111,7 @@ Album::cover( const QSize& size, bool forceLoad ) const { if ( !forceLoad ) return QPixmap(); - + m_uuid = uuid(); Tomahawk::InfoSystem::InfoStringHash trackInfo; @@ -127,7 +127,7 @@ Album::cover( const QSize& size, bool forceLoad ) const connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); - + connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); @@ -178,6 +178,8 @@ Album::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVaria if ( ba.length() ) { m_coverBuffer = ba; + + emit coverChanged(); } } } @@ -193,7 +195,7 @@ Album::infoSystemFinished( QString target ) disconnect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), this, SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); - + disconnect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), this, SLOT( infoSystemFinished( QString ) ) ); diff --git a/src/libtomahawk/album.h b/src/libtomahawk/album.h index 07ab9a75f..47f281c92 100644 --- a/src/libtomahawk/album.h +++ b/src/libtomahawk/album.h @@ -60,6 +60,7 @@ public: signals: void tracksAdded( const QList& tracks ); void updated(); + void coverChanged(); private slots: void onTracksAdded( const QList& tracks ); diff --git a/src/libtomahawk/artist.cpp b/src/libtomahawk/artist.cpp index d2b124240..4a019e624 100644 --- a/src/libtomahawk/artist.cpp +++ b/src/libtomahawk/artist.cpp @@ -117,7 +117,7 @@ Artist::cover( const QSize& size, bool forceLoad ) const connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); - + connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); @@ -168,6 +168,7 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari if ( ba.length() ) { m_coverBuffer = ba; + emit coverChanged(); } } } @@ -177,13 +178,13 @@ void Artist::infoSystemFinished( QString target ) { Q_UNUSED( target ); - + if ( target != m_uuid ) return; disconnect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), this, SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); - + disconnect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), this, SLOT( infoSystemFinished( QString ) ) ); diff --git a/src/libtomahawk/artist.h b/src/libtomahawk/artist.h index 8c8153441..28f47fa46 100644 --- a/src/libtomahawk/artist.h +++ b/src/libtomahawk/artist.h @@ -59,6 +59,7 @@ public: signals: void tracksAdded( const QList& tracks ); void updated(); + void coverChanged(); private slots: void onTracksAdded( const QList& tracks ); diff --git a/src/libtomahawk/playlist/albumitemdelegate.cpp b/src/libtomahawk/playlist/albumitemdelegate.cpp index b8c9072d2..8bf8c4d90 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.cpp +++ b/src/libtomahawk/playlist/albumitemdelegate.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011-2012, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,9 +29,12 @@ #include "utils/tomahawkutils.h" #include "utils/logger.h" +#include "utils/PixmapDelegateFader.h" +#include #include "playlist/albumitem.h" #include "playlist/albumproxymodel.h" +#include "albumview.h" #include #include @@ -40,6 +44,8 @@ AlbumItemDelegate::AlbumItemDelegate( QAbstractItemView* parent, AlbumProxyModel , m_view( parent ) , m_model( proxy ) { + if ( m_view && m_view->metaObject()->indexOfSignal( "modelChanged()" ) > -1 ) + connect( m_view, SIGNAL( modelChanged() ), this, SLOT( modelChanged() ) ); } @@ -89,18 +95,16 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, } QRect r = option.rect.adjusted( 6, 5, -6, -41 ); - QPixmap cover; - if ( !item->album().isNull() ) + + if ( !m_covers.contains( index ) ) { - cover = item->album()->cover( r.size() ); - if ( cover.isNull() ) - cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::CoverInCase, r.size() ); - } - else if ( !item->artist().isNull() ) - { - cover = item->artist()->cover( r.size() ); + m_covers.insert( index, QSharedPointer< Tomahawk::PixmapDelegateFader >( new Tomahawk::PixmapDelegateFader( item->album(), r.size() ) ) ); + _detail::Closure* closure = NewClosure( m_covers[ index ], SIGNAL( repaintRequest() ), const_cast(this), SLOT( doUpdateIndex( const QPersistentModelIndex& ) ), QPersistentModelIndex( index ) ); + closure->setAutoDelete( false ); } + const QPixmap cover = m_covers[ index ]->currentPixmap(); + if ( option.state & QStyle::State_Selected ) { #if defined(Q_WS_MAC) || defined(Q_WS_WIN) @@ -258,3 +262,22 @@ AlbumItemDelegate::whitespaceMouseEvent() emit updateIndex( old ); } } + + +void +AlbumItemDelegate::modelChanged() +{ + m_artistNameRects.clear(); + m_hoveringOver = QPersistentModelIndex(); + + if ( AlbumView* view = qobject_cast< AlbumView* >( m_view ) ) + m_model = view->proxyModel(); +} + + +void +AlbumItemDelegate::doUpdateIndex( const QPersistentModelIndex& idx ) +{ + emit updateIndex( idx ); +} + diff --git a/src/libtomahawk/playlist/albumitemdelegate.h b/src/libtomahawk/playlist/albumitemdelegate.h index 7d29f6c81..eabf4a8d4 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.h +++ b/src/libtomahawk/playlist/albumitemdelegate.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011-2012, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +24,10 @@ #include "dllmacro.h" +namespace Tomahawk { + class PixmapDelegateFader; +} + class QEvent; class AlbumProxyModel; @@ -40,16 +45,21 @@ protected: QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); -// QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const; signals: void updateIndex( const QModelIndex& idx ); +private slots: + void modelChanged(); + void doUpdateIndex( const QPersistentModelIndex& idx ); + private: QAbstractItemView* m_view; AlbumProxyModel* m_model; mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects; + mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_covers; + QPersistentModelIndex m_hoveringOver; QPixmap m_shadowPixmap; diff --git a/src/libtomahawk/playlist/albumview.cpp b/src/libtomahawk/playlist/albumview.cpp index 98b5f0f37..27eadd6d3 100644 --- a/src/libtomahawk/playlist/albumview.cpp +++ b/src/libtomahawk/playlist/albumview.cpp @@ -112,6 +112,8 @@ AlbumView::setAlbumModel( AlbumModel* model ) connect( m_model, SIGNAL( itemCountChanged( unsigned int ) ), SLOT( onItemCountChanged( unsigned int ) ) ); connect( m_model, SIGNAL( loadingStarted() ), m_loadingSpinner, SLOT( fadeIn() ) ); connect( m_model, SIGNAL( loadingFinished() ), m_loadingSpinner, SLOT( fadeOut() ) ); + + emit modelChanged(); } diff --git a/src/libtomahawk/playlist/albumview.h b/src/libtomahawk/playlist/albumview.h index e19975a5d..fa4df45dd 100644 --- a/src/libtomahawk/playlist/albumview.h +++ b/src/libtomahawk/playlist/albumview.h @@ -66,6 +66,9 @@ public: public slots: void onItemActivated( const QModelIndex& index ); +signals: + void modelChanged(); + protected: virtual void startDrag( Qt::DropActions supportedActions ); diff --git a/src/libtomahawk/utils/PixmapDelegateFader.cpp b/src/libtomahawk/utils/PixmapDelegateFader.cpp new file mode 100644 index 000000000..814f7502a --- /dev/null +++ b/src/libtomahawk/utils/PixmapDelegateFader.cpp @@ -0,0 +1,221 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2012, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "PixmapDelegateFader.h" +#include "tomahawkutilsgui.h" + +#include +#include + +using namespace Tomahawk; + +#define INITIAL_FADEIN 250 +#define TRANSITION_LENGTH 1000 + +PixmapDelegateFader::PixmapDelegateFader( const artist_ptr& artist, const QSize& size ) + : m_artist( artist ) + , m_size( size ) +{ + if ( !m_artist.isNull() ) + { + connect( m_artist.data(), SIGNAL( coverChanged() ), this, SLOT( artistChanged() ) ); + m_currentReference = m_artist->cover( size ); + } + + init(); +} + +PixmapDelegateFader::PixmapDelegateFader( const album_ptr& album, const QSize& size ) + : m_album( album ) + , m_size( size ) +{ + if ( !m_album.isNull() ) + { + connect( m_album.data(), SIGNAL( coverChanged() ), this, SLOT( albumChanged() ) ); + m_currentReference = m_album->cover( size ); + } + + init(); +} + + +PixmapDelegateFader::~PixmapDelegateFader() +{ + +} + + +void +PixmapDelegateFader::init() +{ + m_current = QPixmap( m_size ); + m_current.fill( Qt::transparent ); + + if ( m_currentReference.isNull() ) + { + // No cover loaded yet, use default + m_currentReference = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::CoverInCase, m_size ); + } + + m_initialTimeline.setDuration( INITIAL_FADEIN ); + m_initialTimeline.setUpdateInterval( 20 ); + m_initialTimeline.setFrameRange( 0, 1000 ); + m_initialTimeline.setDirection( QTimeLine::Forward ); + connect( &m_initialTimeline, SIGNAL( frameChanged( int ) ), this, SLOT( onAnimationStep( int ) ) ); + connect( &m_initialTimeline, SIGNAL( finished() ), this, SLOT( onAnimationFinished() ) ); + + m_crossfadeTimeline.setDuration( TRANSITION_LENGTH ); + m_crossfadeTimeline.setUpdateInterval( 20 ); + m_crossfadeTimeline.setFrameRange( 0, 1000 ); + m_crossfadeTimeline.setDirection( QTimeLine::Forward ); + connect( &m_crossfadeTimeline, SIGNAL( frameChanged( int ) ), this, SLOT( onAnimationStep( int ) ) ); + connect( &m_crossfadeTimeline, SIGNAL( finished() ), this, SLOT( onAnimationFinished() ) ); + + m_initialTimeline.start(); +} + + +void +PixmapDelegateFader::albumChanged() +{ + if ( m_album.isNull() || m_album->cover( m_size ).isNull() ) + return; + + setPixmap( m_album->cover( m_size ) ); +} + +void +PixmapDelegateFader::artistChanged() +{ + if ( m_artist.isNull() || m_artist->cover( m_size ).isNull() ) + return; + + setPixmap( m_artist->cover( m_size ) ); +} + + +void +PixmapDelegateFader::setPixmap( const QPixmap& pixmap ) +{ + if ( pixmap.isNull() ) + return; + + if ( m_crossfadeTimeline.state() == QTimeLine::Running || m_initialTimeline.state() == QTimeLine::Running ) + { + m_pixmapQueue.enqueue( pixmap ); + return; + } + + qDebug() << Q_FUNC_INFO << "Setting album or artist pixmap to fade into"; + m_oldReference = m_currentReference; + m_currentReference = pixmap; + + m_crossfadeTimeline.start(); +} + + +void +PixmapDelegateFader::onAnimationStep( int step ) +{ + const qreal opacity = ((qreal)step / 1000.); + const qreal oldOpacity = ( 1000. - step ) / 1000. ; + m_current.fill( Qt::transparent ); + + // Update our pixmap with the new opacity + QPainter p( &m_current ); + + if ( !m_oldReference.isNull() ) + { + qDebug() << Q_FUNC_INFO << "Drawing old pixmap w/ opacity;" << oldOpacity; + p.setOpacity( oldOpacity ); + p.drawPixmap( 0, 0, m_oldReference ); + } + + Q_ASSERT( !m_currentReference.isNull() ); + if ( !m_currentReference.isNull() ) // Should never be null.. + { + qDebug() << Q_FUNC_INFO << "Drawing NEW pixmap w/ opacity;" << opacity; + p.setOpacity( opacity ); + p.drawPixmap( 0, 0, m_currentReference ); + } + + p.end(); + + emit repaintRequest(); + /** + * Avoids using setOpacity that is slow on X11 (turns off graphics-backed painting, forces fallback to + * software rasterizer. + * + * but a bit buggy. + */ + /* + const int opacity = ((float)step* / 1000.) * 255; + const int oldOpacity = 255 - opacity; + if ( !m_oldReference.isNull() ) + { + p.setCompositionMode( QPainter::CompositionMode_Source ); + p.drawPixmap( 0, 0, m_oldReference ); + + // Reduce the source opacity by the value of the alpha channel + p.setCompositionMode( QPainter::CompositionMode_DestinationIn ); + qDebug() << Q_FUNC_INFO << "Drawing old pixmap w/ opacity;" << oldOpacity; + p.fillRect( m_current.rect(), QColor( 0, 0, 0, oldOpacity ) ); + } + + Q_ASSERT( !m_currentReference.isNull() ); + if ( !m_currentReference.isNull() ) // Should never be null.. + { + QPixmap temp( m_size ); + temp.fill( Qt::transparent ); + + QPainter p2( &temp ); + p2.drawPixmap( 0, 0, m_currentReference ); + + p2.setCompositionMode( QPainter::CompositionMode_DestinationIn ); + qDebug() << Q_FUNC_INFO << "Drawing NEW pixmap w/ opacity;" << opacity; + + p2.fillRect( temp.rect(), QColor( 0, 0, 0, opacity ) ); + p2.end(); + + p.setCompositionMode( QPainter::CompositionMode_Source ); + p.drawPixmap( 0, 0, temp ); + } + */ + +} + + +void +PixmapDelegateFader::onAnimationFinished() +{ + m_oldReference = QPixmap(); + onAnimationStep( 1000 ); + + if ( !m_pixmapQueue.isEmpty() ) + { + setPixmap( m_pixmapQueue.dequeue() ); + } +} + + + +QPixmap +PixmapDelegateFader::currentPixmap() const +{ + return m_current; +} diff --git a/src/libtomahawk/utils/PixmapDelegateFader.h b/src/libtomahawk/utils/PixmapDelegateFader.h new file mode 100644 index 000000000..79bdc3875 --- /dev/null +++ b/src/libtomahawk/utils/PixmapDelegateFader.h @@ -0,0 +1,74 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011-2012, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef PIXMAPDELEGATEFADER_H +#define PIXMAPDELEGATEFADER_H + +#include "artist.h" +#include "album.h" + +#include +#include +#include + +namespace Tomahawk +{ + +/** + * No parent, manage it yourself! + * + * TODO: Handle changing sizes + */ + +class PixmapDelegateFader : public QObject +{ + Q_OBJECT +public: + PixmapDelegateFader( const artist_ptr& artist, const QSize& size ); + PixmapDelegateFader( const album_ptr& album, const QSize& size ); + + virtual ~PixmapDelegateFader(); + + void setPixmap( const QPixmap& pixmap ); + + QPixmap currentPixmap() const; + +signals: + void repaintRequest(); + +private slots: + void artistChanged(); + void albumChanged(); + + void onAnimationStep( int ); + void onAnimationFinished(); +private: + void init(); + + artist_ptr m_artist; + album_ptr m_album; + QSize m_size; + + QQueue m_pixmapQueue; + QTimeLine m_crossfadeTimeline, m_initialTimeline; + QPixmap m_currentReference, m_oldReference, m_current; +}; + +} + +#endif // PIXMAPDELEGATEFADER_H diff --git a/src/libtomahawk/utils/closure.cpp b/src/libtomahawk/utils/closure.cpp index 8a68b63e1..4249c45a4 100644 --- a/src/libtomahawk/utils/closure.cpp +++ b/src/libtomahawk/utils/closure.cpp @@ -29,6 +29,7 @@ Closure::Closure(QObject* sender, const ClosureArgumentWrapper* val3) : QObject(receiver), callback_(NULL), + autoDelete_( true ), val0_(val0), val1_(val1), val2_(val2), @@ -72,7 +73,9 @@ void Closure::Invoked() { val2_ ? val2_->arg() : QGenericArgument(), val3_ ? val3_->arg() : QGenericArgument()); } - deleteLater(); + + if ( autoDelete_ ) + deleteLater(); } void Closure::Cleanup() { diff --git a/src/libtomahawk/utils/closure.h b/src/libtomahawk/utils/closure.h index 8458bbb36..a2ff25769 100644 --- a/src/libtomahawk/utils/closure.h +++ b/src/libtomahawk/utils/closure.h @@ -63,6 +63,8 @@ class Closure : public QObject, boost::noncopyable { Closure(QObject* sender, const char* signal, std::tr1::function callback); + void setAutoDelete( bool autoDelete ) { autoDelete_ = autoDelete; } + virtual ~Closure(); private slots: @@ -74,6 +76,7 @@ class Closure : public QObject, boost::noncopyable { QMetaMethod slot_; std::tr1::function callback_; + bool autoDelete_; boost::scoped_ptr val0_; boost::scoped_ptr val1_;