diff --git a/data/images/loading-animation.gif b/data/images/loading-animation.gif new file mode 100644 index 000000000..0be11d9d2 Binary files /dev/null and b/data/images/loading-animation.gif differ diff --git a/resources.qrc b/resources.qrc index ea7fff596..e1bc45cf5 100644 --- a/resources.qrc +++ b/resources.qrc @@ -69,6 +69,7 @@ ./data/images/volume-slider-bkg.png ./data/images/volume-slider-level.png ./data/images/echonest_logo.png +./data/images/loading-animation.gif ./data/topbar-radiobuttons.css ./data/icons/tomahawk-icon-16x16.png ./data/icons/tomahawk-icon-32x32.png diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 3c452790f..265a2417b 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -115,6 +115,7 @@ set( libSources playlist/dynamic/widgets/MiscControlWidgets.cpp playlist/dynamic/widgets/CollapsibleControls.cpp playlist/dynamic/widgets/DynamicSetupWidget.cpp + playlist/dynamic/widgets/LoadingSpinner.cpp network/bufferiodevice.cpp network/msgprocessor.cpp @@ -262,6 +263,7 @@ set( libHeaders playlist/dynamic/widgets/MiscControlWidgets.h playlist/dynamic/widgets/CollapsibleControls.h playlist/dynamic/widgets/DynamicSetupWidget.h + playlist/dynamic/widgets/LoadingSpinner.h utils/tomahawkutils.h utils/querylabel.h diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp index 5b3182525..2796f3275 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_firstTrackGenerated( false ) , m_currentAttempts( 0 ) , m_lastResolvedRow( 0 ) { @@ -69,6 +70,10 @@ void DynamicModel::newTrackGenerated( const Tomahawk::query_ptr& query ) { if( m_onDemandRunning ) { + if( !m_firstTrackGenerated ) { + emit firstTrackGenerated(); + m_firstTrackGenerated = false; + } connect( query.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( trackResolveFinished( bool ) ) ); connect( query.data(), SIGNAL( resultsAdded( QList ) ), this, SLOT( trackResolved() ) ); @@ -80,6 +85,7 @@ void DynamicModel::stopOnDemand( bool stopPlaying ) { m_onDemandRunning = false; + m_firstTrackGenerated = false; if( stopPlaying ) AudioEngine::instance()->stop(); diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.h b/src/libtomahawk/playlist/dynamic/DynamicModel.h index 53c9828ce..1ff9d9a87 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.h +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.h @@ -49,6 +49,7 @@ signals: void collapseFromTo( int startRow, int num ); void checkForOverflow(); + void firstTrackGenerated(); void trackGenerationFailure( const QString& msg ); private slots: @@ -64,6 +65,7 @@ private: bool m_onDemandRunning; bool m_changeOnNext; bool m_searchingForNext; + bool m_firstTrackGenerated; int m_currentAttempts; int m_lastResolvedRow; }; diff --git a/src/libtomahawk/playlist/dynamic/DynamicView.cpp b/src/libtomahawk/playlist/dynamic/DynamicView.cpp index f9db9f6cd..f345f7279 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicView.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicView.cpp @@ -40,6 +40,7 @@ DynamicView::DynamicView( QWidget* parent ) : PlaylistView( parent ) , m_onDemand( false ) , m_checkOnCollapse( false ) + , m_working( false ) , m_fadebg( false ) { setContentsMargins( 0, 0, 0, 0 ); @@ -110,11 +111,21 @@ DynamicView::showMessage(const QString& message) overlay()->show(); } +void +DynamicView::setDynamicWorking(bool working) +{ + m_working = working; + if( working ) + overlay()->hide(); + else + onTrackCountChanged( proxyModel()->rowCount() ); +} + void DynamicView::onTrackCountChanged( unsigned int tracks ) { - if ( tracks == 0 ) + if ( tracks == 0 && !m_working ) { if( m_onDemand ) { if( m_readOnly ) @@ -129,8 +140,10 @@ DynamicView::onTrackCountChanged( unsigned int tracks ) if( !overlay()->shown() ) overlay()->show(); } - else + else { + m_working = false; overlay()->hide(); + } } void diff --git a/src/libtomahawk/playlist/dynamic/DynamicView.h b/src/libtomahawk/playlist/dynamic/DynamicView.h index f3dd452fc..235117c37 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicView.h +++ b/src/libtomahawk/playlist/dynamic/DynamicView.h @@ -43,6 +43,8 @@ public: void setOnDemand( bool onDemand ); void setReadOnly( bool readOnly ); + void setDynamicWorking( bool working ); + virtual void paintEvent(QPaintEvent* event); public slots: @@ -68,6 +70,7 @@ private: bool m_onDemand; bool m_readOnly; bool m_checkOnCollapse; + bool m_working; // for collapsing animation QPoint m_fadingPointAnchor; diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp index 5f74ca866..813762ac7 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp @@ -39,6 +39,7 @@ #include #include "audiocontrols.h" +#include "LoadingSpinner.h" using namespace Tomahawk; @@ -58,7 +59,7 @@ DynamicWidget::DynamicWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget m_controls = new CollapsibleControls( this ); m_layout->addWidget( m_controls ); setContentsMargins( 0, 0, 0, 1 ); // to align the bottom with the bottom of the sourcelist - + m_model = new DynamicModel( this ); m_view = new DynamicView( this ); m_view->setModel( m_model ); @@ -67,7 +68,9 @@ DynamicWidget::DynamicWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget connect( m_model, SIGNAL( collapseFromTo( int, int ) ), m_view, SLOT( collapseEntries( int, int ) ) ); connect( m_model, SIGNAL( trackGenerationFailure( QString ) ), this, SLOT( stationFailed( QString ) ) ); + connect( m_model, SIGNAL( firstTrackGenerated() ), this, SLOT( firstStationTrackGenerated() ) ); + m_loading = new LoadingSpinner( m_view ); m_setup = new DynamicSetupWidget( playlist, this ); m_setup->fadeIn(); @@ -140,7 +143,7 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist ) m_model->loadPlaylist( m_playlist ); m_controlsChanged = false; m_setup->setPlaylist( m_playlist ); - + if( !m_playlist.isNull() ) m_controls->setControls( m_playlist, m_playlist->author()->isLocal() ); @@ -222,6 +225,8 @@ DynamicWidget::generate( int num ) if( m_playlist->mode() == Static ) { // get the items from the generator, and put them in the playlist + m_view->setDynamicWorking( true ); + m_loading->fadeIn(); m_playlist->generator()->generate( num ); } else if( m_playlist->mode() == OnDemand ) { @@ -232,6 +237,8 @@ void DynamicWidget::stationFailed( const QString& msg ) { m_view->showMessage( msg ); + m_view->setDynamicWorking( false ); + m_loading->fadeOut(); stopStation( false ); } @@ -252,11 +259,19 @@ DynamicWidget::playPressed() if( isVisible() && !m_playlist.isNull() && m_playlist->mode() == OnDemand && !m_runningOnDemand ) { + m_view->setDynamicWorking( true ); startStation(); } } +void +DynamicWidget::firstStationTrackGenerated() +{ + m_view->setDynamicWorking( false ); + m_loading->fadeOut(); +} + void DynamicWidget::stopStation( bool stopPlaying ) @@ -304,6 +319,8 @@ DynamicWidget::playlistTypeChanged( QString ) void DynamicWidget::tracksGenerated( const QList< query_ptr >& queries ) { + m_loading->fadeOut(); + if( m_playlist->author()->isLocal() ) { m_playlist->addEntries( queries, m_playlist->currentrevision() ); m_resolveOnNextLoad = true; @@ -343,6 +360,11 @@ DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control ) void DynamicWidget::generatorError( const QString& title, const QString& content ) { + if( m_runningOnDemand ) { + stopStation( false ); + } + m_view->setDynamicWorking( false ); + m_loading->fadeOut(); m_view->showMessageTimeout( title, content ); } diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h index 7db1a75c2..17d31d882 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h @@ -23,6 +23,7 @@ #include "dynamic/DynamicPlaylist.h" #include "dynamic/DynamicControl.h" +class LoadingSpinner; class QShowEvent; class QHideEvent; class QSpinBox; @@ -87,6 +88,7 @@ private slots: void generate( int = -1 ); void tracksGenerated( const QList< Tomahawk::query_ptr>& queries ); void generatorError( const QString& title, const QString& content ); + void firstStationTrackGenerated(); void controlsChanged(); void controlChanged( const Tomahawk::dyncontrol_ptr& control ); @@ -97,6 +99,9 @@ private: QVBoxLayout* m_layout; bool m_resolveOnNextLoad; int m_seqRevLaunched; // if we shoot off multiple createRevision calls, we don'y want to set one of the middle ones + + // loading animation + LoadingSpinner* m_loading; // setup controls DynamicSetupWidget* m_setup;