mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-11 08:34:34 +02:00
add some animation
This commit is contained in:
@@ -26,8 +26,10 @@ DynamicModel::DynamicModel( QObject* parent )
|
||||
, m_startOnResolved( false )
|
||||
, m_onDemandRunning( false )
|
||||
, m_currentAttempts( 0 )
|
||||
, m_lastResolvedRow( 0 )
|
||||
{
|
||||
|
||||
|
||||
connect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
|
||||
}
|
||||
|
||||
DynamicModel::~DynamicModel()
|
||||
@@ -38,6 +40,9 @@ DynamicModel::~DynamicModel()
|
||||
void
|
||||
DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
|
||||
{
|
||||
if( !m_playlist.isNull() ) {
|
||||
disconnect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
|
||||
}
|
||||
m_playlist = playlist;
|
||||
|
||||
|
||||
@@ -50,7 +55,6 @@ DynamicModel::startOnDemand()
|
||||
{
|
||||
m_playlist->generator()->startOnDemand();
|
||||
|
||||
connect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
|
||||
m_onDemandRunning = true;
|
||||
m_startOnResolved = true;
|
||||
}
|
||||
@@ -79,21 +83,31 @@ DynamicModel::stopOnDemand()
|
||||
void
|
||||
DynamicModel::trackResolved()
|
||||
{
|
||||
m_currentAttempts = 0;
|
||||
|
||||
Query* q = qobject_cast<Query*>(sender());
|
||||
qDebug() << "Got successful resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts;
|
||||
if( m_startOnResolved ) { // on first start
|
||||
m_startOnResolved = false;
|
||||
AudioEngine::instance()->play();
|
||||
}
|
||||
|
||||
if( m_currentAttempts > 0 ) {
|
||||
qDebug() << "EMITTING AN ASK FOR COLLAPSE:" << m_lastResolvedRow << m_currentAttempts;
|
||||
emit collapseFromTo( m_lastResolvedRow, m_currentAttempts );
|
||||
}
|
||||
m_currentAttempts = 0;
|
||||
}
|
||||
|
||||
void
|
||||
DynamicModel::trackResolveFinished( bool success )
|
||||
{
|
||||
if( !success ) { // if it was successful, we've already gotten a trackResolved() signal
|
||||
Query* q = qobject_cast<Query*>(sender());
|
||||
qDebug() << "Got not resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts;
|
||||
m_currentAttempts++;
|
||||
if( m_currentAttempts < 100 ) {
|
||||
if( m_currentAttempts < 30 ) {
|
||||
m_playlist->generator()->fetchNext();
|
||||
} else {
|
||||
// TODO handle failure
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,6 +117,21 @@ void
|
||||
DynamicModel::newTrackLoading()
|
||||
{
|
||||
if( m_onDemandRunning && m_currentAttempts == 0 ) { // if we're in dynamic mode and we're also currently idle
|
||||
m_lastResolvedRow = rowCount( QModelIndex() );
|
||||
m_playlist->generator()->fetchNext();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DynamicModel::removeIndex(const QModelIndex& index, bool moreToCome)
|
||||
{
|
||||
if ( isReadOnly() )
|
||||
return;
|
||||
|
||||
if( m_playlist->mode() == OnDemand )
|
||||
TrackModel::removeIndex( index );
|
||||
// don't call onPlaylistChanged.
|
||||
|
||||
if( !moreToCome )
|
||||
m_lastResolvedRow = rowCount( QModelIndex() );
|
||||
}
|
||||
|
@@ -40,7 +40,11 @@ public:
|
||||
void stopOnDemand();
|
||||
|
||||
void loadPlaylist( const dynplaylist_ptr& playlist );
|
||||
|
||||
|
||||
virtual void removeIndex( const QModelIndex& index, bool moreToCome = false );
|
||||
signals:
|
||||
void collapseFromTo( int startRow, int num );
|
||||
|
||||
private slots:
|
||||
void newTrackGenerated( const Tomahawk::query_ptr& query );
|
||||
|
||||
@@ -53,6 +57,7 @@ private:
|
||||
bool m_startOnResolved;
|
||||
bool m_onDemandRunning;
|
||||
int m_currentAttempts;
|
||||
int m_lastResolvedRow;
|
||||
};
|
||||
|
||||
};
|
||||
|
@@ -18,16 +18,35 @@
|
||||
|
||||
#include "widgets/overlaywidget.h"
|
||||
#include "playlistmodel.h"
|
||||
#include "trackproxymodel.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QPaintEvent>
|
||||
#include <QtGui/qpaintengine.h>
|
||||
using namespace Tomahawk;
|
||||
|
||||
#define FADE_LENGTH 800
|
||||
#define SLIDE_LENGTH 300
|
||||
#define SLIDE_OFFSET 500
|
||||
#define LONG_MULT 0.4 // to avoid superfast slides when the length is long, make it longer incrementally
|
||||
|
||||
DynamicView::DynamicView( QWidget* parent )
|
||||
: PlaylistView( parent )
|
||||
, m_onDemand( false )
|
||||
{
|
||||
m_fadeOutAnim.setDuration( FADE_LENGTH );
|
||||
m_fadeOutAnim.setCurveShape( QTimeLine::LinearCurve );
|
||||
m_fadeOutAnim.setFrameRange( 100, 0 );
|
||||
m_fadeOutAnim.setUpdateInterval( 10 );
|
||||
|
||||
QEasingCurve curve( QEasingCurve::OutBounce );
|
||||
curve.setAmplitude( .2 );
|
||||
m_slideAnim.setEasingCurve( curve );
|
||||
m_slideAnim.setDirection( QTimeLine::Forward );
|
||||
m_fadeOutAnim.setUpdateInterval( 10 );
|
||||
|
||||
|
||||
connect( &m_fadeOutAnim, SIGNAL( frameChanged( int ) ), viewport(), SLOT( update() ) );
|
||||
}
|
||||
|
||||
DynamicView::~DynamicView()
|
||||
@@ -74,3 +93,77 @@ DynamicView::onTrackCountChanged( unsigned int tracks )
|
||||
else
|
||||
overlay()->hide();
|
||||
}
|
||||
|
||||
void
|
||||
DynamicView::collapseEntries( int startRow, int num )
|
||||
{
|
||||
if( m_fadeOutAnim.state() == QTimeLine::Running )
|
||||
qDebug() << "COLLAPSING TWICE!";
|
||||
// we capture the image of the rows we're going to collapse
|
||||
// then we capture the image of the target row we're going to animate downwards
|
||||
// then we fade the first image out while sliding the second image up.
|
||||
QModelIndex topLeft = proxyModel()->index( startRow, 0, QModelIndex() );
|
||||
QModelIndex bottomRight = proxyModel()->index( startRow + num - 1, proxyModel()->columnCount( QModelIndex() ) - 1, QModelIndex() );
|
||||
QItemSelection sel( topLeft, bottomRight );
|
||||
QRect fadingRect = visualRegionForSelection( sel ).boundingRect();
|
||||
|
||||
m_fadingIndexes = QPixmap::grabWidget( viewport(), fadingRect );
|
||||
m_fadingPointAnchor = fadingRect.topLeft();
|
||||
|
||||
qDebug() << "Grabbed fading indexes from rect:" << fadingRect << m_fadingIndexes.size();
|
||||
|
||||
topLeft = proxyModel()->index( startRow + num, 0, QModelIndex() );
|
||||
bottomRight = proxyModel()->index( startRow + num, proxyModel()->columnCount( QModelIndex() ) - 1, QModelIndex() );
|
||||
QRect slidingRect = visualRegionForSelection( QItemSelection( topLeft, bottomRight ) ).boundingRect();
|
||||
|
||||
m_slidingIndex = QPixmap::grabWidget( viewport(), slidingRect );
|
||||
m_bottomAnchor = slidingRect.topLeft();
|
||||
qDebug() << "Grabbed sliding index from rect:" << slidingRect << m_slidingIndex.size();
|
||||
|
||||
// slide from the current position to the new one
|
||||
int frameRange = fadingRect.topLeft().y() - slidingRect.topLeft().y();
|
||||
m_slideAnim.setDuration( SLIDE_LENGTH + frameRange * LONG_MULT );
|
||||
m_slideAnim.setFrameRange( slidingRect.topLeft().y(), fadingRect.topLeft().y() );
|
||||
|
||||
m_fadeOutAnim.start();
|
||||
QTimer::singleShot( SLIDE_OFFSET, &m_slideAnim, SLOT( start() ) );
|
||||
|
||||
QModelIndexList todel;
|
||||
for( int i = 0; i < num; i++ ) {
|
||||
for( int k = 0; k < proxyModel()->columnCount( QModelIndex() ); k++ ) {
|
||||
todel << proxyModel()->index( startRow + i, k );
|
||||
}
|
||||
}
|
||||
proxyModel()->removeIndexes( todel );
|
||||
}
|
||||
|
||||
void
|
||||
DynamicView::paintEvent( QPaintEvent* event )
|
||||
{
|
||||
TrackView::paintEvent(event);
|
||||
|
||||
QPainter p( viewport() );
|
||||
if( m_fadeOutAnim.state() == QTimeLine::Running ) { // both run together
|
||||
p.save();
|
||||
QRect bg = m_fadingIndexes.rect();
|
||||
bg.moveTo( m_fadingPointAnchor ); // cover up the background
|
||||
p.fillRect( bg, Qt::white );
|
||||
|
||||
// qDebug() << "FAST SETOPACITY:" << p.paintEngine()->hasFeature(QPaintEngine::ConstantOpacity);
|
||||
p.setOpacity( m_fadeOutAnim.currentFrame() );
|
||||
p.drawPixmap( m_fadingPointAnchor, m_fadingIndexes );
|
||||
|
||||
p.restore();
|
||||
|
||||
if( m_slideAnim.state() == QTimeLine::Running ) {
|
||||
// draw the collapsing entry
|
||||
QRect bg = m_slidingIndex.rect();
|
||||
bg.moveTo( m_bottomAnchor );
|
||||
p.fillRect( bg, Qt::white );
|
||||
p.drawPixmap( 0, m_slideAnim.currentFrame(), m_slidingIndex );
|
||||
} else if( m_fadeOutAnim.state() == QTimeLine::Running ) {
|
||||
p.drawPixmap( m_bottomAnchor, m_slidingIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include "playlist/playlistview.h"
|
||||
#include <QTimer>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QTimeLine>
|
||||
|
||||
class PlaylistModel;
|
||||
class TrackModel;
|
||||
@@ -36,20 +37,33 @@ public:
|
||||
virtual void setModel( PlaylistModel* model );
|
||||
|
||||
void setOnDemand( bool onDemand );
|
||||
|
||||
virtual void paintEvent(QPaintEvent* event);
|
||||
|
||||
public slots:
|
||||
void showMessageTimeout( const QString& title, const QString& body );
|
||||
|
||||
// collapse and animate the transition
|
||||
// there MUST be a row *after* startRow + num. that is, you can't collapse
|
||||
// entries unless there is at least one entry after the last collapsed row
|
||||
void collapseEntries( int startRow, int num );
|
||||
|
||||
private slots:
|
||||
void onTrackCountChanged( unsigned int );
|
||||
|
||||
private:
|
||||
QTimer m_showTimer;
|
||||
QPropertyAnimation* m_fadeOut;
|
||||
|
||||
QString m_title;
|
||||
QString m_body;
|
||||
|
||||
bool m_onDemand;
|
||||
|
||||
// for collapsing animation
|
||||
QPoint m_fadingPointAnchor;
|
||||
QPoint m_bottomAnchor;
|
||||
QPixmap m_fadingIndexes;
|
||||
QPixmap m_slidingIndex;
|
||||
QTimeLine m_fadeOutAnim;
|
||||
QTimeLine m_slideAnim;
|
||||
};
|
||||
|
||||
};
|
||||
|
@@ -90,7 +90,7 @@ DynamicWidget::DynamicWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget
|
||||
m_view->setModel( m_model );
|
||||
m_view->setContentsMargins( 0, 0, 0, 0 );
|
||||
m_layout->addWidget( m_view, 1 );
|
||||
|
||||
connect( m_model, SIGNAL( collapseFromTo( int, int ) ), m_view, SLOT( collapseEntries( int, int ) ), Qt::QueuedConnection );
|
||||
|
||||
loadDynamicPlaylist( playlist );
|
||||
|
||||
|
Reference in New Issue
Block a user