From 0cb2a1cf91158566ecce395c9a82a116c16452ff Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 11 Feb 2011 18:59:45 -0500 Subject: [PATCH 1/2] tentative support for removing old station tracks and fading-only when lookups exceed size of playlist --- .../playlist/dynamic/DynamicModel.cpp | 5 ++ .../playlist/dynamic/DynamicModel.h | 3 + .../playlist/dynamic/DynamicView.cpp | 89 +++++++++++++------ .../playlist/dynamic/DynamicView.h | 4 +- 4 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp index 9822ff89a..edd519fe2 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp @@ -25,6 +25,7 @@ DynamicModel::DynamicModel( QObject* parent ) , m_startOnResolved( false ) , m_onDemandRunning( false ) , m_changeOnNext( false ) + , m_searchingForNext( false ) , m_currentAttempts( 0 ) , m_lastResolvedRow( 0 ) { @@ -61,6 +62,7 @@ DynamicModel::startOnDemand() m_onDemandRunning = true; m_startOnResolved = true; + m_searchingForNext = false; m_currentAttempts = 0; m_lastResolvedRow = 0; } @@ -73,6 +75,7 @@ DynamicModel::newTrackGenerated( const Tomahawk::query_ptr& query ) connect( query.data(), SIGNAL( resultsAdded( QList ) ), this, SLOT( trackResolved() ) ); append( query ); + m_searchingForNext = true; } } @@ -80,6 +83,7 @@ void DynamicModel::stopOnDemand() { m_onDemandRunning = false; + m_searchingForNext = false; AudioEngine::instance()->stop(); disconnect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) ); @@ -100,6 +104,7 @@ DynamicModel::trackResolved() { Query* q = qobject_cast(sender()); qDebug() << "Got successful resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts; + m_searchingForNext = false; if( m_startOnResolved ) { // on first start m_startOnResolved = false; AudioEngine::instance()->play(); diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.h b/src/libtomahawk/playlist/dynamic/DynamicModel.h index 639190624..5eb19e88a 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.h +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.h @@ -43,6 +43,8 @@ public: void loadPlaylist( const dynplaylist_ptr& playlist ); virtual void removeIndex( const QModelIndex& index, bool moreToCome = false ); + + bool searchingForNext() const { return m_searchingForNext; } signals: void collapseFromTo( int startRow, int num ); @@ -60,6 +62,7 @@ private: bool m_startOnResolved; bool m_onDemandRunning; bool m_changeOnNext; + bool m_searchingForNext; int m_currentAttempts; int m_lastResolvedRow; }; diff --git a/src/libtomahawk/playlist/dynamic/DynamicView.cpp b/src/libtomahawk/playlist/dynamic/DynamicView.cpp index a7de13825..37c73eda3 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicView.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicView.cpp @@ -61,6 +61,7 @@ DynamicView::~DynamicView() void DynamicView::setModel( DynamicModel* model) { + m_model = model; PlaylistView::setModel( model ); connect( model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); @@ -70,6 +71,11 @@ void DynamicView::setOnDemand( bool onDemand ) { m_onDemand = onDemand; + + if( m_onDemand ) + setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + else + setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded ); } void @@ -117,53 +123,82 @@ DynamicView::onTrackCountChanged( unsigned int tracks ) else overlay()->hide(); - // make sure we can see all our tracks - scrollTo( proxyModel()->index( proxyModel()->rowCount() - 1, 0, QModelIndex() ), EnsureVisible ); + if( !m_onDemand || m_model->searchingForNext() || proxyModel()->rowCount( QModelIndex() ) == 0 ) + return; + + /// We don't want stations to grow forever, because we don't want the view to have to scroll + /// So if there are too many tracks, we remove some that have already been played + /// Our threshold is 4 rows to the end. That's when we collapse. + QModelIndex last = proxyModel()->index( proxyModel()->rowCount( QModelIndex() ) - 1, 0, QModelIndex() ); + QRect lastRect = visualRect( last ); + qDebug() << "Checking viewport height of" << viewport()->height() << "and last track bottom:" << lastRect.bottomLeft().y() << "under threshold" << 4 * lastRect.height(); + if( viewport()->height() - lastRect.bottomLeft().y() <= ( 4 * lastRect.height() ) ) { + qDebug() << "Deciding to remove some tracks from this station"; + + // figure out how many to remove. lets get rid of 1/3rd of the backlog, visually. + int toRemove = ( viewport()->height() / 3 ) / lastRect.height(); + qDebug() << "Decided to remove" << toRemove << "rows!"; + collapseEntries( 0, toRemove, proxyModel()->rowCount( QModelIndex() ) - toRemove ); + } } void -DynamicView::collapseEntries( int startRow, int num ) +DynamicView::collapseEntries( int startRow, int num, int numToKeep ) { if( m_fadeOutAnim.state() == QTimeLine::Running ) qDebug() << "COLLAPSING TWICE!"; - // TODO if we are scrolled, we can't animate this way. - // we have to animate the top coming down, which i haven't implemented yet.. - if( verticalScrollBar()->sliderPosition() == 0 ) { - // 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(); - QRect fadingRectViewport = fadingRect; // all values that we use in paintEvent() have to be in viewport coords - fadingRect.moveTo( viewport()->mapTo( this, fadingRect.topLeft() ) ); - - m_fadingIndexes = QPixmap::grabWidget( this, fadingRect ); // but all values we use to grab the widgetr have to be in scrollarea coords :( - m_fadingPointAnchor = QPoint( 0, fadingRectViewport.topLeft().y() ); - - 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() ); + + /// Two options: Either we are overflowing our view, or we're not. If we are, it's because the search for a playable track + /// went past the limit of the view. Just fade out from the beginning to the end in that case. otherwise, animate a slide + bool justFade = false; + int realNum = num; + QModelIndex last = indexAt( QPoint( 3, viewport()->height() - 3 ) ); + if( last.isValid() && last.row() < startRow + num ) { + justFade = true; + realNum = last.row(); + } + // 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 + realNum - 1, proxyModel()->columnCount( QModelIndex() ) - 1, QModelIndex() ); + QItemSelection sel( topLeft, bottomRight ); + QRect fadingRect = visualRegionForSelection( sel ).boundingRect(); + QRect fadingRectViewport = fadingRect; // all values that we use in paintEvent() have to be in viewport coords + fadingRect.moveTo( viewport()->mapTo( this, fadingRect.topLeft() ) ); + + m_fadingIndexes = QPixmap::grabWidget( this, fadingRect ); // but all values we use to grab the widgetr have to be in scrollarea coords :( + m_fadingPointAnchor = QPoint( 0, fadingRectViewport.topLeft().y() ); + + m_fadeOutAnim.start(); + + qDebug() << "Grabbed fading indexes from rect:" << fadingRect << m_fadingIndexes.size(); + + if( !justFade ) { + /// sanity checks. make sure we have all the rows we need + int firstSlider = startRow + realNum; + Q_ASSERT( firstSlider + numToKeep - 1 <= proxyModel()->rowCount() ); + + topLeft = proxyModel()->index( startRow + realNum, 0, QModelIndex() ); + bottomRight = proxyModel()->index( startRow + realNum + numToKeep - 1, proxyModel()->columnCount( QModelIndex() ) - 1, QModelIndex() ); QRect slidingRect = visualRegionForSelection( QItemSelection( topLeft, bottomRight ) ).boundingRect(); QRect slidingRectViewport = slidingRect; - // map internal view cord to external qscrollarea + // map internal view coord to external qscrollarea slidingRect.moveTo( viewport()->mapTo( this, slidingRect.topLeft() ) ); - + m_slidingIndex = QPixmap::grabWidget( this, slidingRect ); m_bottomAnchor = QPoint( 0, slidingRectViewport.topLeft().y() ); m_bottomOfAnim = QPoint( 0, slidingRectViewport.bottomLeft().y() ); 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( slidingRectViewport.topLeft().y(), fadingRectViewport.topLeft().y() ); - m_fadeOutAnim.start(); QTimer::singleShot( SLIDE_OFFSET, &m_slideAnim, SLOT( start() ) ); } + // delete the actual indices QModelIndexList todel; for( int i = 0; i < num; i++ ) { diff --git a/src/libtomahawk/playlist/dynamic/DynamicView.h b/src/libtomahawk/playlist/dynamic/DynamicView.h index 147675b44..6dbb17255 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicView.h +++ b/src/libtomahawk/playlist/dynamic/DynamicView.h @@ -51,12 +51,14 @@ public slots: // 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 ); + // optionally you can specify how many rows are past the block of collapsed rows + void collapseEntries( int startRow, int num, int numToKeep = 1 ); private slots: void onTrackCountChanged( unsigned int ); private: + DynamicModel* m_model; QString m_title; QString m_body; From 60fb37f740b5ef741599addb99ff9f8e8c7bfa37 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 11 Feb 2011 23:59:30 -0500 Subject: [PATCH 2/2] fix backlog removing --- .../playlist/dynamic/DynamicModel.cpp | 9 +++---- .../playlist/dynamic/DynamicModel.h | 3 ++- .../playlist/dynamic/DynamicView.cpp | 27 ++++++++++++++++--- .../playlist/dynamic/DynamicView.h | 8 ++++-- .../dynamic/widgets/DynamicWidget.cpp | 2 +- 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp index edd519fe2..ac64b2848 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp @@ -25,7 +25,6 @@ DynamicModel::DynamicModel( QObject* parent ) , m_startOnResolved( false ) , m_onDemandRunning( false ) , m_changeOnNext( false ) - , m_searchingForNext( false ) , m_currentAttempts( 0 ) , m_lastResolvedRow( 0 ) { @@ -62,7 +61,6 @@ DynamicModel::startOnDemand() m_onDemandRunning = true; m_startOnResolved = true; - m_searchingForNext = false; m_currentAttempts = 0; m_lastResolvedRow = 0; } @@ -75,7 +73,6 @@ DynamicModel::newTrackGenerated( const Tomahawk::query_ptr& query ) connect( query.data(), SIGNAL( resultsAdded( QList ) ), this, SLOT( trackResolved() ) ); append( query ); - m_searchingForNext = true; } } @@ -83,7 +80,6 @@ void DynamicModel::stopOnDemand() { m_onDemandRunning = false; - m_searchingForNext = false; AudioEngine::instance()->stop(); disconnect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) ); @@ -104,7 +100,6 @@ DynamicModel::trackResolved() { Query* q = qobject_cast(sender()); qDebug() << "Got successful resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts; - m_searchingForNext = false; if( m_startOnResolved ) { // on first start m_startOnResolved = false; AudioEngine::instance()->play(); @@ -115,6 +110,8 @@ DynamicModel::trackResolved() emit collapseFromTo( m_lastResolvedRow, m_currentAttempts ); } m_currentAttempts = 0; + + emit checkForOverflow(); } void @@ -142,7 +139,7 @@ DynamicModel::newTrackLoading() } else 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 diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.h b/src/libtomahawk/playlist/dynamic/DynamicModel.h index 5eb19e88a..2386d3fd2 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.h +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.h @@ -47,7 +47,8 @@ public: bool searchingForNext() const { return m_searchingForNext; } signals: void collapseFromTo( int startRow, int num ); - + void checkForOverflow(); + void trackGenerationFailure( const QString& msg ); private slots: diff --git a/src/libtomahawk/playlist/dynamic/DynamicView.cpp b/src/libtomahawk/playlist/dynamic/DynamicView.cpp index 37c73eda3..8e76f3163 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicView.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicView.cpp @@ -37,6 +37,7 @@ using namespace Tomahawk; DynamicView::DynamicView( QWidget* parent ) : PlaylistView( parent ) , m_onDemand( false ) + , m_checkOnCollapse( false ) { m_fadeOutAnim.setDuration( FADE_LENGTH ); m_fadeOutAnim.setCurveShape( QTimeLine::LinearCurve ); @@ -51,6 +52,7 @@ DynamicView::DynamicView( QWidget* parent ) connect( &m_fadeOutAnim, SIGNAL( frameChanged( int ) ), viewport(), SLOT( update() ) ); + connect( &m_fadeOutAnim, SIGNAL( finished() ), this, SLOT( animFinished() ) ); } DynamicView::~DynamicView() @@ -65,6 +67,7 @@ DynamicView::setModel( DynamicModel* model) PlaylistView::setModel( model ); connect( model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); + connect( model, SIGNAL( checkForOverflow() ), this, SLOT( checkForOverflow() ) ); } void @@ -122,10 +125,17 @@ DynamicView::onTrackCountChanged( unsigned int tracks ) } else overlay()->hide(); - - if( !m_onDemand || m_model->searchingForNext() || proxyModel()->rowCount( QModelIndex() ) == 0 ) +} + +void +DynamicView::checkForOverflow() +{ + if( !m_onDemand || proxyModel()->rowCount( QModelIndex() ) == 0 ) return; - + + if( m_fadeOutAnim.state() == QTimeLine::Running ) + m_checkOnCollapse = true; + /// We don't want stations to grow forever, because we don't want the view to have to scroll /// So if there are too many tracks, we remove some that have already been played /// Our threshold is 4 rows to the end. That's when we collapse. @@ -134,7 +144,7 @@ DynamicView::onTrackCountChanged( unsigned int tracks ) qDebug() << "Checking viewport height of" << viewport()->height() << "and last track bottom:" << lastRect.bottomLeft().y() << "under threshold" << 4 * lastRect.height(); if( viewport()->height() - lastRect.bottomLeft().y() <= ( 4 * lastRect.height() ) ) { qDebug() << "Deciding to remove some tracks from this station"; - + // figure out how many to remove. lets get rid of 1/3rd of the backlog, visually. int toRemove = ( viewport()->height() / 3 ) / lastRect.height(); qDebug() << "Decided to remove" << toRemove << "rows!"; @@ -145,6 +155,7 @@ DynamicView::onTrackCountChanged( unsigned int tracks ) void DynamicView::collapseEntries( int startRow, int num, int numToKeep ) { + qDebug() << "BEGINNING TO COLLAPSE FROM" << startRow << num << numToKeep; if( m_fadeOutAnim.state() == QTimeLine::Running ) qDebug() << "COLLAPSING TWICE!"; @@ -209,6 +220,14 @@ DynamicView::collapseEntries( int startRow, int num, int numToKeep ) proxyModel()->removeIndexes( todel ); } +void +DynamicView::animFinished() +{ + if( m_checkOnCollapse ) + checkForOverflow(); + m_checkOnCollapse = false; +} + void DynamicView::paintEvent( QPaintEvent* event ) { diff --git a/src/libtomahawk/playlist/dynamic/DynamicView.h b/src/libtomahawk/playlist/dynamic/DynamicView.h index 6dbb17255..d1024b93d 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicView.h +++ b/src/libtomahawk/playlist/dynamic/DynamicView.h @@ -21,6 +21,7 @@ #include #include #include +#include class PlaylistModel; class TrackModel; @@ -56,7 +57,9 @@ public slots: private slots: void onTrackCountChanged( unsigned int ); - + void checkForOverflow(); + void animFinished(); + private: DynamicModel* m_model; QString m_title; @@ -64,7 +67,8 @@ private: bool m_onDemand; bool m_readOnly; - + bool m_checkOnCollapse; + // for collapsing animation QPoint m_fadingPointAnchor; QPoint m_bottomAnchor; diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp index e1bc73831..98a802bae 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp @@ -64,7 +64,7 @@ DynamicWidget::DynamicWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget 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 ); + connect( m_model, SIGNAL( collapseFromTo( int, int ) ), m_view, SLOT( collapseEntries( int, int ) ) ); connect( m_model, SIGNAL( trackGenerationFailure( QString ) ), m_view, SLOT( showMessage( QString ) ) );