From 87a43fad9d2224631a28987aa7c18076239176a6 Mon Sep 17 00:00:00 2001
From: Christian Muehlhaeuser <muesli@gmail.com>
Date: Wed, 13 Aug 2014 07:49:21 +0200
Subject: [PATCH] * Simplify GridItemDelegate using HoverControls widget and
 adopt new style.

---
 src/libtomahawk/playlist/GridItemDelegate.cpp | 288 +++++++++---------
 src/libtomahawk/playlist/GridItemDelegate.h   |  13 +-
 2 files changed, 143 insertions(+), 158 deletions(-)

diff --git a/src/libtomahawk/playlist/GridItemDelegate.cpp b/src/libtomahawk/playlist/GridItemDelegate.cpp
index 2a9d606a0..68b8e3251 100644
--- a/src/libtomahawk/playlist/GridItemDelegate.cpp
+++ b/src/libtomahawk/playlist/GridItemDelegate.cpp
@@ -1,6 +1,6 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
- *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
+ *   Copyright 2010-2014, Christian Muehlhaeuser <muesli@tomahawk-player.org>
  *   Copyright 2011-2012, Leo Franchi            <lfranchi@kde.org>
  *
  *   Tomahawk is free software: you can redistribute it and/or modify
@@ -34,6 +34,7 @@
 #include "audio/AudioEngine.h"
 #include "playlist/PlayableItem.h"
 #include "playlist/PlayableProxyModel.h"
+#include "widgets/HoverControls.h"
 #include "widgets/ImageButton.h"
 #include "utils/TomahawkStyle.h"
 #include "utils/TomahawkUtilsGui.h"
@@ -43,7 +44,7 @@
 #include "utils/Logger.h"
 
 namespace {
-    static const int FADE_DURATION = 200;
+    static const int FADE_DURATION = 400;
 };
 
 
@@ -88,9 +89,11 @@ GridItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
 
     QStyleOptionViewItemV4 opt = option;
     initStyleOption( &opt, QModelIndex() );
-    qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter );
+//    qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter );
 
     QRect r = option.rect;
+    r.setHeight( r.width() );
+
     QString top, bottom;
     if ( !item->album().isNull() )
     {
@@ -138,94 +141,93 @@ GridItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
     QSharedPointer< Tomahawk::PixmapDelegateFader > fader = m_covers[ index ];
     if ( fader->size() != r.size() )
         fader->setSize( r.size() );
-
     const QPixmap cover = fader->currentPixmap();
-    painter->drawPixmap( r, cover );
 
-    qreal opacity = -1.;
+    qreal opacity = -1.0;
+    qreal pct = -1.0;
     if ( m_hoverFaders.contains( index ) )
     {
-        const qreal pct = ( m_hoverFaders[ index ]->currentFrame() / 100.0 );
-        opacity = 0.15 - pct * 0.15;
+        pct = ( m_hoverFaders[ index ]->currentFrame() / 100.0 );
+        opacity = 1.0 - pct * 0.70;
     }
     else if ( m_hoverIndex == index )
     {
-        opacity = 0.15;
+        opacity = 0.3;
+        pct = 1.0;
     }
-
     if ( opacity > -1.0 )
     {
         painter->save();
 
-        painter->setPen( TomahawkStyle::HOVER_GLOW );
-        painter->setBrush( TomahawkStyle::HOVER_GLOW );
-        painter->setOpacity( opacity );
+        const int cropIn = pct * ( (qreal)cover.width() * 0.10 );
+        const QRect crop = cover.rect().adjusted( cropIn, cropIn, -cropIn, -cropIn );
+
+        painter->drawPixmap( r, cover.copy( crop ).scaled( r.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
+
+        painter->setOpacity( 1.0 - opacity );
+        painter->setPen( Qt::transparent );
+        painter->setBrush( Qt::black );
         painter->drawRect( r );
 
         painter->restore();
     }
+    else
+    {
+        painter->drawPixmap( r, cover );
+    }
 
     QTextOption to;
     to.setWrapMode( QTextOption::NoWrap );
 
     QString text;
     QFont font = opt.font;
-    font.setPointSize( TomahawkUtils::defaultFontSize() );
-    QFont boldFont = font;
-    boldFont.setBold( true );
-    boldFont.setPointSize( TomahawkUtils::defaultFontSize() + 1 );
+    font.setPointSize( TomahawkUtils::defaultFontSize() + 2 );
+    QFont smallFont = font;
+    smallFont.setBold( true );
+    smallFont.setPointSize( TomahawkUtils::defaultFontSize() );
 
-    int bottomHeight = QFontMetrics( font ).boundingRect( bottom ).height();
-    int topHeight = QFontMetrics( boldFont ).boundingRect( top ).height();
-    int frameHeight = bottomHeight + topHeight + 10;
-
-    QRect gradientRect = r.adjusted( 0, r.height() - frameHeight * 1.2, 0, 0 );
-    QColor gradientColor = opt.palette.background().color();
-    gradientColor.setAlphaF( 0.66 );
-
-    painter->save();
-    painter->setPen( Qt::transparent );
-    painter->setBrush( gradientColor );
-    painter->drawRect( gradientRect );
-    painter->restore();
+    int bottomHeight = QFontMetrics( smallFont ).boundingRect( bottom ).height();
+    int topHeight = QFontMetrics( font ).boundingRect( top ).height();
 
     painter->setPen( TomahawkStyle::SELECTION_FOREGROUND );
 
-    QRect textRect = option.rect.adjusted( 6, option.rect.height() - frameHeight, -6, -6 );
+    QRect textRect = option.rect.adjusted( 0, r.height() + 8, 0, 0 );
     bool oneLiner = false;
     if ( bottom.isEmpty() )
         oneLiner = true;
 
-    painter->setFont( boldFont );
+    painter->setFont( font );
+    painter->setPen( Qt::black );
     if ( oneLiner )
     {
-        to.setAlignment( Qt::AlignHCenter | Qt::AlignVCenter );
+        to.setAlignment( Qt::AlignLeft | Qt::AlignTop );
         text = painter->fontMetrics().elidedText( top, Qt::ElideRight, textRect.width() - 3 );
         painter->drawText( textRect, text, to );
     }
     else
     {
-        to.setAlignment( Qt::AlignHCenter | Qt::AlignTop );
+        to.setAlignment( Qt::AlignLeft | Qt::AlignTop );
         text = painter->fontMetrics().elidedText( top, Qt::ElideRight, textRect.width() - 3 );
         painter->drawText( textRect, text, to );
 
-        painter->setFont( font );
-        // If the user is hovering over an artist rect, draw a background so she knows it's clickable
-        QRect r = textRect;
+        painter->setOpacity( 0.5 );
+        painter->setFont( smallFont );
+        // If the user is hovering over an artist rect, draw a background so they knows it's clickable
+/*        QRect r = textRect;
         r.setTop( r.bottom() - painter->fontMetrics().height() );
         r.adjust( 4, 0, -4, -1 );
         if ( m_hoveringOver == index )
         {
             TomahawkUtils::drawQueryBackground( painter, r );
             painter->setPen( TomahawkStyle::SELECTION_FOREGROUND );
-        }
+        }*/
 
-        to.setAlignment( Qt::AlignHCenter | Qt::AlignBottom );
+        to.setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
         text = painter->fontMetrics().elidedText( bottom, Qt::ElideRight, textRect.width() - 10 );
-        painter->drawText( textRect.adjusted( 5, -1, -5, -1 ), text, to );
+        painter->drawText( textRect.adjusted( 0, 2, 0, 2 ), text, to );
 
         // Calculate rect of artist on-hover button click area
-        m_artistNameRects[ index ] = r;
+//        m_artistNameRects[ index ] = r;
     }
 
     painter->restore();
@@ -241,8 +243,10 @@ GridItemDelegate::onPlayClicked( const QPersistentModelIndex& index )
     spinner->setAutoCenter( false );
     spinner->fadeIn();
 
-    QPoint pos = m_view->visualRect( index ).center() - QPoint( ( spinner->width() ) / 2 - 1,
-                                                                ( spinner->height() ) / 2 - 1 );
+    QRect r = m_view->visualRect( index );
+    r.setHeight( r.width() );
+    QPoint pos = r.center() - QPoint( ( spinner->width() ) / 2 - 1,
+                                      ( spinner->height() ) / 2 - 1 );
 
     spinner->move( pos );
     spinner->setFocusPolicy( Qt::NoFocus );
@@ -294,32 +298,33 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
         else
             m_view->setCursor( Qt::ArrowCursor );
 
-        foreach ( const QModelIndex& idx, m_playButton.keys() )
+        foreach ( const QModelIndex& idx, m_hoverControls.keys() )
         {
             if ( index != idx )
-                m_playButton.take( idx )->deleteLater();
+                m_hoverControls.take( idx )->deleteLater();
         }
 
-        if ( !m_playButton.contains( index ) && !m_spinner.contains( index ) && !m_pauseButton.contains( index ) )
+        if ( !m_hoverControls.contains( index ) && !m_spinner.contains( index ) )
         {
-            foreach ( ImageButton* button, m_playButton )
-                button->deleteLater();
-            m_playButton.clear();
+            foreach ( HoverControls* control, m_hoverControls )
+                control->deleteLater();
+            m_hoverControls.clear();
 
-            ImageButton* button = new ImageButton( m_view );
-            button->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::PlayButton, TomahawkUtils::Original, QSize( 48, 48 ) ) );
-            button->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::PlayButtonPressed, TomahawkUtils::Original, QSize( 48, 48 ) ), QIcon::Off, QIcon::Active );
-            button->setFixedSize( 48, 48 );
-            button->move( option.rect.center() - QPoint( 23, 23 ) );
-            button->setContentsMargins( 0, 0, 0, 0 );
-            button->setFocusPolicy( Qt::NoFocus );
-            button->installEventFilter( this );
-            button->show();
+            QRect cRect = option.rect;
+            cRect.setHeight( cRect.width() );
 
-            NewClosure( button, SIGNAL( clicked( bool ) ),
+            HoverControls* controls = new HoverControls( m_view );
+            controls->setFixedSize( 64, 40 );
+            controls->move( cRect.center() - QPoint( controls->width() / 2 -1, controls->height() / 2 -1 ) );
+            controls->setContentsMargins( 0, 0, 0, 0 );
+            controls->setFocusPolicy( Qt::NoFocus );
+            controls->installEventFilter( this );
+            controls->show();
+
+            NewClosure( controls, SIGNAL( play() ),
                         const_cast<GridItemDelegate*>(this), SLOT( onPlayClicked( QPersistentModelIndex ) ), QPersistentModelIndex( index ) );
 
-            m_playButton[ index ] = button;
+            m_hoverControls[ index ] = controls;
         }
 
         if ( m_hoveringOver != index || ( !hoveringArtist && m_hoveringOver.isValid() ) )
@@ -338,19 +343,35 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
         {
             if ( m_hoverIndex.isValid() )
             {
-                QTimeLine* fadeOut = createTimeline( QTimeLine::Forward );
+                int startFrame = 100;
+                if ( m_hoverFaders.contains( m_hoverIndex ) )
+                {
+                    QTimeLine* oldFader = m_hoverFaders.take( m_hoverIndex );
+                    startFrame = oldFader->currentFrame();
+                    oldFader->deleteLater();
+                }
+
+                QTimeLine* fadeOut = createTimeline( QTimeLine::Backward, startFrame );
                 _detail::Closure* c = NewClosure( fadeOut, SIGNAL( frameChanged( int ) ), this, SLOT( fadingFrameChanged( QPersistentModelIndex ) ), QPersistentModelIndex( m_hoverIndex ) );
                 c->setAutoDelete( false );
                 c = NewClosure( fadeOut, SIGNAL( finished() ), this, SLOT( fadingFrameFinished( QPersistentModelIndex ) ), QPersistentModelIndex( m_hoverIndex ) );
                 c->setAutoDelete( false );
+
                 m_hoverFaders[ m_hoverIndex ] = fadeOut;
                 fadeOut->start();
             }
-
             emit updateIndex( m_hoverIndex );
-            m_hoverIndex = index;
 
-            QTimeLine* fadeIn = createTimeline( QTimeLine::Backward );
+            m_hoverIndex = index;
+            int startFrame = 0;
+            if ( m_hoverFaders.contains( index ) )
+            {
+                QTimeLine* oldFader = m_hoverFaders.take( index );
+                startFrame = oldFader->currentFrame();
+                oldFader->deleteLater();
+            }
+
+            QTimeLine* fadeIn = createTimeline( QTimeLine::Forward, startFrame );
             _detail::Closure* c = NewClosure( fadeIn, SIGNAL( frameChanged( int ) ), this, SLOT( fadingFrameChanged( QPersistentModelIndex ) ), QPersistentModelIndex( index ) );
             c->setAutoDelete( false );
             c = NewClosure( fadeIn, SIGNAL( finished() ), this, SLOT( fadingFrameFinished( QPersistentModelIndex ) ), QPersistentModelIndex( index ) );
@@ -428,17 +449,14 @@ GridItemDelegate::onViewChanged()
     foreach ( const QPersistentModelIndex& index, m_spinner.keys() )
     {
         QRect rect = m_view->visualRect( index );
-        m_spinner.value( index )->move( rect.center() - QPoint( 23, 23 ) );
+        rect.setHeight( rect.width() );
+        m_spinner.value( index )->move( rect.center() - QPoint( 15, 15 ) );
     }
-    foreach ( const QPersistentModelIndex& index, m_playButton.keys() )
+    foreach ( const QPersistentModelIndex& index, m_hoverControls.keys() )
     {
         QRect rect = m_view->visualRect( index );
-        m_playButton.value( index )->move( rect.center() - QPoint( 23, 23 ) );
-    }
-    foreach ( const QPersistentModelIndex& index, m_pauseButton.keys() )
-    {
-        QRect rect = m_view->visualRect( index );
-        m_pauseButton.value( index )->move( rect.center() - QPoint( 23, 23 ) );
+        rect.setHeight( rect.width() );
+        m_hoverControls.value( index )->move( rect.center() - QPoint( m_hoverControls[ index ]->width() / 2 -1, m_hoverControls[ index ]->height() / 2 -1 ) );
     }
 }
 
@@ -452,57 +470,16 @@ GridItemDelegate::onPlaybackFinished()
 }
 
 
-void
-GridItemDelegate::onPlayPauseHover( const QPersistentModelIndex& index )
-{
-    if( m_pauseButton.contains( index ) )
-    {
-        updatePlayPauseButton( m_pauseButton[ index ] );
-    }
-}
-
-
-void
-GridItemDelegate::updatePlayPauseButton( ImageButton* button, bool setState )
-{
-    if ( button )
-    {
-        if ( button->property( "paused" ).toBool() )
-        {
-            button->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::PauseButton, TomahawkUtils::Original, QSize( 48, 48 ) ) );
-            button->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::PauseButtonPressed, TomahawkUtils::Original, QSize( 48, 48 ) ), QIcon::Off, QIcon::Active );
-        }
-        else
-        {
-            button->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::PlayButton, TomahawkUtils::Original, QSize( 48, 48 ) ) );
-            button->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::PlayButtonPressed, TomahawkUtils::Original, QSize( 48, 48 ) ), QIcon::Off, QIcon::Active );
-        }
-
-        if ( setState )
-            button->setProperty( "paused", !button->property( "paused" ).toBool() );
-    }
-}
-
-
-void
-GridItemDelegate::onPlayPausedClicked()
-{
-    ImageButton* button = qobject_cast< ImageButton* >( QObject::sender() );
-    updatePlayPauseButton( button, true );
-}
-
-
 void
 GridItemDelegate::onPlaybackStarted( const QPersistentModelIndex& index )
 {
-    if( m_spinner.contains( index ) )
+    if ( m_spinner.contains( index ) )
     {
         LoadingSpinner* spinner = static_cast<LoadingSpinner*>(m_spinner[ index ]);
         spinner->fadeOut();
     }
 
     clearButtons();
-    createPauseButton( index );
 
     emit startedPlaying( index );
 }
@@ -511,12 +488,10 @@ GridItemDelegate::onPlaybackStarted( const QPersistentModelIndex& index )
 void
 GridItemDelegate::clearButtons()
 {
-    foreach ( ImageButton* button, m_playButton )
-        button->deleteLater();
-    m_playButton.clear();
-    foreach ( ImageButton* button, m_pauseButton )
-        button->deleteLater();
-    m_pauseButton.clear();
+    foreach ( HoverControls* control, m_hoverControls )
+        control->deleteLater();
+    m_hoverControls.clear();
+
     foreach ( QWidget* widget, m_spinner )
         widget->deleteLater();
     m_spinner.clear();
@@ -539,9 +514,30 @@ GridItemDelegate::onCurrentIndexChanged()
 void
 GridItemDelegate::resetHoverIndex()
 {
-    foreach ( ImageButton* button, m_playButton )
-        button->deleteLater();
-    m_playButton.clear();
+    foreach ( HoverControls* controls, m_hoverControls )
+        controls->deleteLater();
+    m_hoverControls.clear();
+
+    if ( m_hoverIndex.isValid() )
+    {
+        int startFrame = 100;
+        if ( m_hoverFaders.contains( m_hoverIndex ) )
+        {
+            QTimeLine* oldFader = m_hoverFaders.take( m_hoverIndex );
+            startFrame = oldFader->currentFrame();
+            oldFader->deleteLater();
+        }
+
+        QTimeLine* fadeOut = createTimeline( QTimeLine::Backward, startFrame );
+        _detail::Closure* c = NewClosure( fadeOut, SIGNAL( frameChanged( int ) ), this, SLOT( fadingFrameChanged( QPersistentModelIndex ) ), QPersistentModelIndex( m_hoverIndex ) );
+        c->setAutoDelete( false );
+        c = NewClosure( fadeOut, SIGNAL( finished() ), this, SLOT( fadingFrameFinished( QPersistentModelIndex ) ), QPersistentModelIndex( m_hoverIndex ) );
+        c->setAutoDelete( false );
+
+        m_hoverFaders[ m_hoverIndex ] = fadeOut;
+        fadeOut->start();
+    }
+    emit updateIndex( m_hoverIndex );
 
     QModelIndex idx = m_hoveringOver;
     m_hoveringOver = QPersistentModelIndex();
@@ -550,27 +546,6 @@ GridItemDelegate::resetHoverIndex()
 }
 
 
-void
-GridItemDelegate::createPauseButton( const QPersistentModelIndex& index )
-{
-    ImageButton* button = new ImageButton( m_view );
-    button->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::PauseButton, TomahawkUtils::Original, QSize( 48, 48 ) ) );
-    button->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::PauseButtonPressed, TomahawkUtils::Original, QSize( 48, 48 ) ), QIcon::Off, QIcon::Active );
-    button->setFixedSize( 48, 48 );
-    button->move( m_view->visualRect( index ).center() - QPoint( 23, 23 ) );
-    button->setContentsMargins( 0, 0, 0, 0 );
-    button->setFocusPolicy( Qt::NoFocus );
-    button->installEventFilter( this );
-    button->setProperty( "paused", false );
-    button->show();
-
-    connect( button, SIGNAL( clicked( bool ) ), AudioEngine::instance(), SLOT( playPause() ) );
-    connect( button, SIGNAL( clicked( bool ) ), this, SLOT( onPlayPausedClicked() ) );
-
-    m_pauseButton[ index ] = button;
-}
-
-
 void
 GridItemDelegate::fadingFrameChanged( const QPersistentModelIndex& idx )
 {
@@ -590,14 +565,29 @@ GridItemDelegate::fadingFrameFinished( const QPersistentModelIndex& idx )
 
 
 QTimeLine*
-GridItemDelegate::createTimeline( QTimeLine::Direction direction )
+GridItemDelegate::createTimeline( QTimeLine::Direction direction, int startFrame )
 {
-    QTimeLine* timeline = new QTimeLine( FADE_DURATION, this );
+    qreal dur = (qreal)FADE_DURATION * ( 1.0 - (qreal)startFrame / 100.0 );
+    if ( direction == QTimeLine::Backward )
+    {
+        dur = (qreal)FADE_DURATION * ( (qreal)startFrame / 100.0 );
+    }
+
+    QTimeLine* timeline = new QTimeLine( dur, this );
     timeline->setDirection( direction );
     timeline->setCurveShape( QTimeLine::LinearCurve );
-    timeline->setUpdateInterval( 30 );
-    timeline->setStartFrame( 0 );
-    timeline->setEndFrame( 100 );
+    timeline->setUpdateInterval( 20 );
+
+    if ( direction == QTimeLine::Forward )
+    {
+        timeline->setStartFrame( startFrame );
+        timeline->setEndFrame( 100 );
+    }
+    else
+    {
+        timeline->setStartFrame( 0 );
+        timeline->setEndFrame( startFrame );
+    }
 
     return timeline;
 }
diff --git a/src/libtomahawk/playlist/GridItemDelegate.h b/src/libtomahawk/playlist/GridItemDelegate.h
index 002daa589..741e928f1 100644
--- a/src/libtomahawk/playlist/GridItemDelegate.h
+++ b/src/libtomahawk/playlist/GridItemDelegate.h
@@ -1,6 +1,6 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
- *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
+ *   Copyright 2010-2014, Christian Muehlhaeuser <muesli@tomahawk-player.org>
  *   Copyright 2011-2012, Leo Franchi            <lfranchi@kde.org>
  *
  *   Tomahawk is free software: you can redistribute it and/or modify
@@ -37,6 +37,7 @@ class QEvent;
 class QTimeLine;
 class PlayableProxyModel;
 class ImageButton;
+class HoverControls;
 
 class DLLEXPORT GridItemDelegate : public QStyledItemDelegate
 {
@@ -79,13 +80,8 @@ private slots:
     void fadingFrameChanged( const QPersistentModelIndex& );
     void fadingFrameFinished( const QPersistentModelIndex& );
 
-    void updatePlayPauseButton(ImageButton* button , bool setState = false );
-    void onPlayPauseHover( const QPersistentModelIndex& index );
-    void onPlayPausedClicked();
-
 private:
-    QTimeLine* createTimeline( QTimeLine::Direction direction );
-    void createPauseButton( const QPersistentModelIndex& index );
+    QTimeLine* createTimeline( QTimeLine::Direction direction, int startFrame = 0 );
     void clearButtons();
 
     QAbstractItemView* m_view;
@@ -101,8 +97,7 @@ private:
 
     QPixmap m_shadowPixmap;
     mutable QHash< QPersistentModelIndex, QWidget* > m_spinner;
-    mutable QHash< QPersistentModelIndex, ImageButton* > m_playButton;
-    mutable QHash< QPersistentModelIndex, ImageButton* > m_pauseButton;
+    mutable QHash< QPersistentModelIndex, HoverControls* > m_hoverControls;
     mutable QHash< QPersistentModelIndex, QTimeLine* > m_hoverFaders;
 };