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_startOnResolved( false )
|
||||||
, m_onDemandRunning( false )
|
, m_onDemandRunning( false )
|
||||||
, m_currentAttempts( 0 )
|
, m_currentAttempts( 0 )
|
||||||
|
, m_lastResolvedRow( 0 )
|
||||||
{
|
{
|
||||||
|
|
||||||
|
connect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicModel::~DynamicModel()
|
DynamicModel::~DynamicModel()
|
||||||
@@ -38,6 +40,9 @@ DynamicModel::~DynamicModel()
|
|||||||
void
|
void
|
||||||
DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
|
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;
|
m_playlist = playlist;
|
||||||
|
|
||||||
|
|
||||||
@@ -50,7 +55,6 @@ DynamicModel::startOnDemand()
|
|||||||
{
|
{
|
||||||
m_playlist->generator()->startOnDemand();
|
m_playlist->generator()->startOnDemand();
|
||||||
|
|
||||||
connect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
|
|
||||||
m_onDemandRunning = true;
|
m_onDemandRunning = true;
|
||||||
m_startOnResolved = true;
|
m_startOnResolved = true;
|
||||||
}
|
}
|
||||||
@@ -79,21 +83,31 @@ DynamicModel::stopOnDemand()
|
|||||||
void
|
void
|
||||||
DynamicModel::trackResolved()
|
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
|
if( m_startOnResolved ) { // on first start
|
||||||
m_startOnResolved = false;
|
m_startOnResolved = false;
|
||||||
AudioEngine::instance()->play();
|
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
|
void
|
||||||
DynamicModel::trackResolveFinished( bool success )
|
DynamicModel::trackResolveFinished( bool success )
|
||||||
{
|
{
|
||||||
if( !success ) { // if it was successful, we've already gotten a trackResolved() signal
|
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++;
|
m_currentAttempts++;
|
||||||
if( m_currentAttempts < 100 ) {
|
if( m_currentAttempts < 30 ) {
|
||||||
m_playlist->generator()->fetchNext();
|
m_playlist->generator()->fetchNext();
|
||||||
|
} else {
|
||||||
|
// TODO handle failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,6 +117,21 @@ void
|
|||||||
DynamicModel::newTrackLoading()
|
DynamicModel::newTrackLoading()
|
||||||
{
|
{
|
||||||
if( m_onDemandRunning && m_currentAttempts == 0 ) { // if we're in dynamic mode and we're also currently idle
|
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();
|
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() );
|
||||||
|
}
|
||||||
|
@@ -41,6 +41,10 @@ public:
|
|||||||
|
|
||||||
void loadPlaylist( const dynplaylist_ptr& playlist );
|
void loadPlaylist( const dynplaylist_ptr& playlist );
|
||||||
|
|
||||||
|
virtual void removeIndex( const QModelIndex& index, bool moreToCome = false );
|
||||||
|
signals:
|
||||||
|
void collapseFromTo( int startRow, int num );
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void newTrackGenerated( const Tomahawk::query_ptr& query );
|
void newTrackGenerated( const Tomahawk::query_ptr& query );
|
||||||
|
|
||||||
@@ -53,6 +57,7 @@ private:
|
|||||||
bool m_startOnResolved;
|
bool m_startOnResolved;
|
||||||
bool m_onDemandRunning;
|
bool m_onDemandRunning;
|
||||||
int m_currentAttempts;
|
int m_currentAttempts;
|
||||||
|
int m_lastResolvedRow;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@@ -18,16 +18,35 @@
|
|||||||
|
|
||||||
#include "widgets/overlaywidget.h"
|
#include "widgets/overlaywidget.h"
|
||||||
#include "playlistmodel.h"
|
#include "playlistmodel.h"
|
||||||
|
#include "trackproxymodel.h"
|
||||||
|
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QtGui/qpaintengine.h>
|
||||||
using namespace Tomahawk;
|
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 )
|
DynamicView::DynamicView( QWidget* parent )
|
||||||
: PlaylistView( parent )
|
: PlaylistView( parent )
|
||||||
, m_onDemand( false )
|
, 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()
|
DynamicView::~DynamicView()
|
||||||
@@ -74,3 +93,77 @@ DynamicView::onTrackCountChanged( unsigned int tracks )
|
|||||||
else
|
else
|
||||||
overlay()->hide();
|
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 "playlist/playlistview.h"
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QPropertyAnimation>
|
#include <QPropertyAnimation>
|
||||||
|
#include <QTimeLine>
|
||||||
|
|
||||||
class PlaylistModel;
|
class PlaylistModel;
|
||||||
class TrackModel;
|
class TrackModel;
|
||||||
@@ -36,20 +37,33 @@ public:
|
|||||||
virtual void setModel( PlaylistModel* model );
|
virtual void setModel( PlaylistModel* model );
|
||||||
|
|
||||||
void setOnDemand( bool onDemand );
|
void setOnDemand( bool onDemand );
|
||||||
|
|
||||||
|
virtual void paintEvent(QPaintEvent* event);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void showMessageTimeout( const QString& title, const QString& body );
|
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:
|
private slots:
|
||||||
void onTrackCountChanged( unsigned int );
|
void onTrackCountChanged( unsigned int );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTimer m_showTimer;
|
|
||||||
QPropertyAnimation* m_fadeOut;
|
|
||||||
|
|
||||||
QString m_title;
|
QString m_title;
|
||||||
QString m_body;
|
QString m_body;
|
||||||
|
|
||||||
bool m_onDemand;
|
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->setModel( m_model );
|
||||||
m_view->setContentsMargins( 0, 0, 0, 0 );
|
m_view->setContentsMargins( 0, 0, 0, 0 );
|
||||||
m_layout->addWidget( m_view, 1 );
|
m_layout->addWidget( m_view, 1 );
|
||||||
|
connect( m_model, SIGNAL( collapseFromTo( int, int ) ), m_view, SLOT( collapseEntries( int, int ) ), Qt::QueuedConnection );
|
||||||
|
|
||||||
loadDynamicPlaylist( playlist );
|
loadDynamicPlaylist( playlist );
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user