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;