1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-15 10:33:59 +02:00

* Imported QML specific widgets / views / providers.

This commit is contained in:
Christian Muehlhaeuser
2013-05-18 22:53:56 +02:00
committed by Michael Zanetti
parent 17eeae9a8f
commit d03cb674c9
6 changed files with 659 additions and 0 deletions

View File

@@ -0,0 +1,266 @@
#include "DynamicQmlWidget.h"
#include "playlist/dynamic/DynamicModel.h"
#include "playlist/PlayableProxyModel.h"
#include "playlist/dynamic/DynamicModel.h"
#include "playlist/dynamic/echonest/EchonestControl.h"
#include "playlist/dynamic/GeneratorInterface.h"
#include "playlist/PlayableItem.h"
#include "Source.h"
#include "SourceList.h"
#include "audio/AudioEngine.h"
#include "database/Database.h"
#include "database/DatabaseCommand_PlaybackCharts.h"
#include "widgets/DeclarativeCoverArtProvider.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/Logger.h"
#include <QUrl>
#include <QSize>
#include <qdeclarative.h>
#include <QDeclarativeContext>
namespace Tomahawk
{
DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* parent )
: DeclarativeView( parent )
, m_playlist( playlist )
, m_runningOnDemand( false )
, m_activePlaylist( false )
, m_playNextResolved( false )
{
m_model = new DynamicModel( this );
m_proxyModel = new PlayableProxyModel( this );
m_proxyModel->setSourcePlayableModel( m_model );
m_proxyModel->setShowOfflineResults( false );
m_model->loadPlaylist( m_playlist );
m_artistChartsModel = new PlayableModel( this );
qmlRegisterUncreatableType<GeneratorInterface>("tomahawk", 1, 0, "Generator", "you cannot create it on your own - should be set in context");
rootContext()->setContextProperty( "dynamicModel", m_proxyModel );
rootContext()->setContextProperty( "artistChartsModel", m_artistChartsModel );
rootContext()->setContextProperty( "generator", m_playlist->generator().data() );
rootContext()->setContextProperty( "currentlyPlayedIndex", QVariant::fromValue( 0 ) );
setSource( QUrl( "qrc" RESPATH "qml/StationView.qml" ) );
connect( m_model, SIGNAL( currentIndexChanged()), SLOT( currentIndexChanged() ) );
connect( m_model, SIGNAL( loadingStarted() ), SIGNAL(loadingChanged() ) );
connect( m_model, SIGNAL( loadingFinished() ), SIGNAL(loadingChanged() ) );
connect( m_model, SIGNAL( changed() ), SIGNAL( titleChanged() ) );
connect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( nextTrackGenerated( Tomahawk::query_ptr ) ) );
connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) );
connect( m_playlist->generator().data(), SIGNAL( error( QString, QString )), SLOT( error(QString,QString) ) );
connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), this, SLOT( trackStarted() ) );
connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistChanged( Tomahawk::playlistinterface_ptr ) ) );
// m_playlist->generator()->generate( 20 );
loadArtistCharts();
}
DynamicQmlWidget::~DynamicQmlWidget()
{
}
Tomahawk::playlistinterface_ptr
DynamicQmlWidget::playlistInterface() const
{
return m_proxyModel->playlistInterface();
}
QString
DynamicQmlWidget::title() const
{
return m_model->title();
}
QString
DynamicQmlWidget::description() const
{
return m_model->description();
}
QString
DynamicQmlWidget::iconSource() const
{
return QLatin1String( RESPATH "images/station.png" );
}
bool
DynamicQmlWidget::jumpToCurrentTrack()
{
return true;
}
playlist_ptr DynamicQmlWidget::playlist() const
{
return m_model->playlist();
}
bool DynamicQmlWidget::loading()
{
// Why does isLoading() not reset to true when cleared and station started again?
// return m_model->isLoading();
return m_playNextResolved && m_proxyModel->rowCount() == 0;
}
bool DynamicQmlWidget::configured()
{
return !m_playlist->generator()->controls().isEmpty();
}
void DynamicQmlWidget::playItem(int index)
{
tDebug() << "playItem called for cover" << index;
AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), m_proxyModel->itemFromIndex( index )->result() );
}
void DynamicQmlWidget::pause()
{
AudioEngine::instance()->pause();
}
void DynamicQmlWidget::startStationFromArtist(const QString &artist)
{
m_model->clear();
m_playNextResolved = true;
m_playlist->generator()->startFromArtist(Artist::get(artist));
emit loadingChanged();
emit configuredChanged();
}
void DynamicQmlWidget::startStationFromGenre(const QString &genre)
{
tDebug() << "should start startion from genre" << genre;
m_model->clear();
m_playNextResolved = true;
m_playlist->generator()->startFromGenre( genre );
emit loadingChanged();
emit configuredChanged();
}
void DynamicQmlWidget::currentIndexChanged()
{
tDebug() << "current index is" << m_model->currentItem().row();
rootContext()->setContextProperty( "currentlyPlayedIndex", m_proxyModel->mapFromSource( m_model->currentItem() ).row() );
}
void
DynamicQmlWidget::tracksGenerated( const QList< query_ptr >& queries )
{
m_model->tracksGenerated( queries, queries.count() );
m_playlist->resolve();
}
void DynamicQmlWidget::nextTrackGenerated(const query_ptr &track)
{
m_model->tracksGenerated( QList<query_ptr>() << track );
m_playlist->resolve();
connect( track.data(), SIGNAL( resolvingFinished( bool )), SLOT( resolvingFinished( bool ) ) );
}
void DynamicQmlWidget::error(const QString &title, const QString &body)
{
qDebug() << "got a generator error:" << title << body;
// m_playlist->generator()->fetchNext();
}
void DynamicQmlWidget::onRevisionLoaded(DynamicPlaylistRevision)
{
m_playlist->resolve();
}
void DynamicQmlWidget::resolvingFinished(bool hasResults)
{
Q_UNUSED(hasResults)
qDebug() << "next track generated" << m_proxyModel->rowCount() << m_proxyModel->currentIndex().row();
if( m_proxyModel->rowCount() <= m_proxyModel->currentIndex().row() + 8 ) {
qDebug() << "fetching next one";
m_playlist->generator()->fetchNext();
}
if( m_playNextResolved && m_proxyModel->rowCount() > 0 ) {
playItem( 0 );
m_playNextResolved = false;
}
}
void DynamicQmlWidget::trackStarted()
{
if ( m_activePlaylist && !m_playlist.isNull() &&
m_playlist->mode() == OnDemand && !m_runningOnDemand )
{
startStation();
}
}
void
DynamicQmlWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl )
{
if ( pl == m_proxyModel->playlistInterface() ) // same playlist
m_activePlaylist = true;
else
{
m_activePlaylist = false;
// user started playing something somewhere else, so give it a rest
if ( m_runningOnDemand )
{
stopStation( false );
}
}
}
void
DynamicQmlWidget::stopStation( bool stopPlaying )
{
m_model->stopOnDemand( stopPlaying );
m_runningOnDemand = false;
}
void
DynamicQmlWidget::startStation()
{
m_runningOnDemand = true;
m_model->startOnDemand();
}
void
DynamicQmlWidget::loadArtistCharts()
{
DatabaseCommand_PlaybackCharts* cmd = new DatabaseCommand_PlaybackCharts( SourceList::instance()->getLocal(), this );
cmd->setLimit( 15 );
connect( cmd, SIGNAL( artists( QList<Tomahawk::artist_ptr> ) ), SLOT( onArtistCharts( QList< Tomahawk::artist_ptr > ) ), Qt::UniqueConnection );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
void
DynamicQmlWidget::onArtistCharts( const QList< Tomahawk::artist_ptr >& artists )
{
m_artistChartsModel->clear();
m_artistChartsModel->appendArtists( artists );
}
}

View File

@@ -0,0 +1,109 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Michael Zanetti <mzanetti@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DYNAMIC_QML_WIDGET_H
#define DYNAMIC_QML_WIDGET_H
#include "ViewPage.h"
#include "Typedefs.h"
#include "widgets/DeclarativeView.h"
#include <QDeclarativeImageProvider>
class PlayableModel;
class PlayableProxyModel;
namespace Tomahawk
{
class DynamicModel;
class DynamicQmlWidget : public DeclarativeView, public Tomahawk::ViewPage
{
Q_OBJECT
Q_PROPERTY(QString title READ title NOTIFY titleChanged)
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
Q_PROPERTY(bool configured READ configured NOTIFY configuredChanged)
public:
explicit DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* parent = 0 );
virtual ~DynamicQmlWidget();
virtual QWidget* widget() { return this; }
virtual Tomahawk::playlistinterface_ptr playlistInterface() const;
virtual QString title() const;
virtual QString description() const;
virtual QString iconSource() const;
virtual bool showInfoBar() const { return false; }
virtual bool showModes() const { return false; }
virtual bool showFilter() const { return false; }
virtual bool jumpToCurrentTrack();
playlist_ptr playlist() const;
bool loading();
bool configured();
signals:
void loadingChanged();
void configuredChanged();
void titleChanged();
public slots:
void playItem(int index);
void pause();
void startStationFromArtist(const QString &artist);
void startStationFromGenre(const QString &genre);
private slots:
void currentIndexChanged();
void tracksGenerated( const QList< Tomahawk::query_ptr>& queries );
void nextTrackGenerated( const Tomahawk::query_ptr& track );
void error( const QString& title, const QString& body);
void onRevisionLoaded( Tomahawk::DynamicPlaylistRevision );
void playlistChanged( Tomahawk::playlistinterface_ptr pl );
void resolvingFinished( bool hasResults );
void trackStarted();
void startStation();
void stopStation( bool stopPlaying );
void loadArtistCharts();
void onArtistCharts( const QList< Tomahawk::artist_ptr >& artists );
private:
DynamicModel* m_model;
PlayableProxyModel* m_proxyModel;
dynplaylist_ptr m_playlist;
PlayableModel* m_artistChartsModel;
bool m_runningOnDemand;
bool m_activePlaylist;
bool m_playNextResolved;
};
}
#endif // DYNAMIC_QML_WIDGET_H

View File

@@ -0,0 +1,142 @@
#include "DeclarativeCoverArtProvider.h"
#include "playlist/PlayableItem.h"
#include "playlist/PlayableProxyModel.h"
#include "Query.h"
#include "Album.h"
#include "Artist.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/Logger.h"
#include <QDeclarativeImageProvider>
#include <QModelIndex>
#include <QDebug>
#include <QPainter>
namespace Tomahawk
{
DeclarativeCoverArtProvider::DeclarativeCoverArtProvider( )
: QDeclarativeImageProvider( QDeclarativeImageProvider::Pixmap )
{
}
DeclarativeCoverArtProvider::~DeclarativeCoverArtProvider()
{
}
QPixmap DeclarativeCoverArtProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
{
// We always can generate it in the requested size
int width = requestedSize.width() > 0 ? requestedSize.width() : 230;
int height = requestedSize.height() > 0 ? requestedSize.height() : 230;
if( size )
*size = QSize( width, height );
QPixmap cover;
tDebug() << "DeclarativeCoverArtprovider: Getting album art by id:" << id << requestedSize;
bool mirrored = false;
bool labeled = false;
QString coverId = id;
if(coverId.contains("-mirror")) {
coverId.remove("-mirror");
mirrored = true;
}
if(coverId.contains("-labels")) {
coverId.remove("-labels");
labeled = true;
}
artist_ptr artist = Artist::getByCoverId( coverId );
if ( !artist.isNull() )
{
tDebug() << "Returning artist cover:" << artist->cover( *size ).isNull();
cover = artist->cover( *size );
if ( cover.isNull() )
{
tDebug() << Q_FUNC_INFO << "Returning default artist image";
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::Original, *size );
}
}
if ( cover.isNull() )
{
album_ptr album = Album::getByCoverId( coverId );
if ( !album.isNull() )
{
tDebug() << "Returning album cover:" << album->cover( *size ).isNull();
cover = album->cover( *size );
if ( cover.isNull() )
{
tDebug() << Q_FUNC_INFO << "Returning default album image";
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::Original, *size );
}
}
}
if ( cover.isNull() )
{
tDebug() << Q_FUNC_INFO << "Returning default track image";
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Original, *size );
}
QImage image(*size, QImage::Format_ARGB32);
if(labeled) {
QImage coverImage(*size, QImage::Format_RGB32);
QPainter bgPainter(&coverImage);
bgPainter.drawPixmap(0, 0, size->width(), size->height(), cover);
QColor c1;
c1.setRgb( 0, 0, 0 );
c1.setAlphaF( 0.00 );
QColor c2;
c2.setRgb( 0, 0, 0 );
c2.setAlphaF( 0.88 );
QLinearGradient gradient( QPointF( 0, 0 ), QPointF( 0, 1 ) );
gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
gradient.setColorAt( 0.0, c1 );
gradient.setColorAt( 0.6, c2 );
gradient.setColorAt( 1.0, c2 );
bgPainter.setPen( Qt::transparent );
bgPainter.setBrush(QBrush(gradient));
bgPainter.drawRect(0, size->height() * 0.7, size->width(), size->height() * 0.3);
cover = QPixmap::fromImage(coverImage);
}
QPainter painter(&image);
if(!mirrored) {
image.fill(Qt::white);
painter.drawPixmap(0, 0, size->width(), size->height(), cover);
} else {
image.fill(QColor(0, 0, 0, 0));
// Lets paint half of the image in a fragment per line
int mirrorHeight = size->height() / 2;
int fragmentCount = mirrorHeight;
int fragmentHeight = mirrorHeight / fragmentCount;
QPainter::PixmapFragment fragments[fragmentCount];
qreal fragmentOpacity = 0;
int fragmentStartY = size->height() - mirrorHeight;
for(int i = 0; i < fragmentCount; ++i) {
QPointF point = QPointF(size->width() / 2, fragmentStartY + (fragmentHeight / 2));
QRectF sourceRect = QRectF(0, fragmentStartY, size->width(), fragmentHeight);
fragments[i] = QPainter::PixmapFragment::create(point, sourceRect, 1, 1, 0, fragmentOpacity);
fragmentOpacity += 0.5 / fragmentCount;
fragmentStartY += fragmentHeight;
}
painter.drawPixmapFragments(fragments, fragmentCount, cover);
}
return QPixmap::fromImage(image);
}
}

View File

@@ -0,0 +1,23 @@
#ifndef DECLARATIVECOVERARTPROVIDER_H
#define DECLARATIVECOVERARTPROVIDER_H
#include "playlist/PlayableProxyModel.h"
#include <QDeclarativeImageProvider>
namespace Tomahawk
{
class DeclarativeCoverArtProvider: public QDeclarativeImageProvider
{
public:
DeclarativeCoverArtProvider();
~DeclarativeCoverArtProvider();
QPixmap requestPixmap( const QString &id, QSize *size, const QSize &requestedSize );
};
}
#endif // DECLARATIVECOVERARTPROVIDER_H

View File

@@ -0,0 +1,58 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Michael Zanetti <mzanetti@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "DeclarativeView.h"
#include "playlist/PlayableItem.h"
#include "DeclarativeCoverArtProvider.h"
#include "utils/TomahawkUtilsGui.h"
#include <qdeclarative.h>
#include <QDeclarativeEngine>
#include <QDeclarativeContext>
namespace Tomahawk
{
DeclarativeView::DeclarativeView( QWidget *parent ):
QDeclarativeView( parent )
{
// Needed to make the QML contents scale with tomahawk
setResizeMode( QDeclarativeView::SizeRootObjectToView );
// This types seem to be needed everywhere anyways, lets the register here
qmlRegisterType<PlayableItem>( "tomahawk", 1, 0, "PlayableItem");
// qmlRegisterType<SearchFieldQmlProxy>("tomahawk", 1, 0, "SearchField");
// QML image providers will be deleted by the view
engine()->addImageProvider( "albumart", new DeclarativeCoverArtProvider() );
// Register the view itself to make it easy to invoke the view's slots from QML
rootContext()->setContextProperty( "mainView", this );
rootContext()->setContextProperty( "defaultFontSize", TomahawkUtils::defaultFontSize() );
rootContext()->setContextProperty( "defaultFontHeight", TomahawkUtils::defaultFontHeight() );
}
DeclarativeView::~DeclarativeView()
{
}
}

View File

@@ -0,0 +1,61 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Michael Zanetti <mzanetti@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DECLARATIVEVIEW_H
#define DECLARATIVEVIEW_H
#include <QDeclarativeView>
class QAbstractItemModel;
/**
* @class This is the main class for Tomahawk's declarative views
*
* DeclarativeView inherits from QDeclarativeView and registers some
* common types, properties and functions used by all of Tomhawk's
* declarative views:
*
* Registered Types:
* - PlayableItem
*
* Set context properties:
* - mainView: This view, so you can invoke this view's slots from QML
* - defaultFontSize: system default font point size
* - defaultFontHeight: system default font pixel height
*
* It also registers an albumart image provider. You can access album art
* in QML with the source url "image://albumart/<coverid>".
* The cover id can be obtained by the CoverIdRole in PlayableModels
*
* After subclassing this, all you have to do is call setSource() to
* load the QML file and optionally setModel().
*/
namespace Tomahawk
{
class DeclarativeView: public QDeclarativeView
{
Q_OBJECT
public:
DeclarativeView(QWidget *parent = 0);
~DeclarativeView();
};
}
#endif