1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-02 02:12:47 +02:00

some more work on stations

This commit is contained in:
Michael Zanetti
2012-11-24 23:38:15 +01:00
parent 4b23fe2fe0
commit 8462a47985
9 changed files with 207 additions and 141 deletions

82
data/qml/CoverFlip.qml Normal file
View File

@@ -0,0 +1,82 @@
import QtQuick 1.1
import tomahawk 1.0
import "tomahawkimports"
PathView {
id: coverView
// The start coordinates for the covers
// Default is left, centered in height
property int pathStartX: coverSize
property int pathStartY: height / 2
// The size of the covers in the path
property int coverSize: 100
// emitted when a cover is clicked
signal itemClicked(int index)
// emitted when a cover is clicked
signal itemPlayPauseClicked(int index)
preferredHighlightBegin: 0.2 // scene.width / 11000
preferredHighlightEnd: preferredHighlightBegin
pathItemCount: 5
//highlightMoveDuration: 500
delegate: CoverImage {
height: root.coverSize
width: root.coverSize
showLabels: true
showMirror: true
artistName: model.artistName
trackName: model.trackName
artworkId: model.coverID
showPlayButton: true
currentyPlayed: mode.itemFromIndex(index).isPlaying
scale: PathView.itemScale
itemBrightness: PathView.itemBrightness
opacity: PathView.itemOpacity
z: -x
onPlayClicked: {
coverView.itemPlayPauseClicked(index)
}
onClicked: {
coverView.itemClicked(index)
}
}
path: Path {
startX: coverView.pathStartX
startY: coverView.pathStartY
PathAttribute { name: "itemOpacity"; value: 1 }
PathAttribute { name: "itemBrightness"; value: 1 }
PathAttribute { name: "itemScale"; value: 1 }
// PathLine { x: coverView.pathStartX * 0.9 ; y: coverView.pathStartY * 0.9 }
// PathPercent { value: .2 }
// PathAttribute { name: "itemOpacity"; value: 1 }
// PathAttribute { name: "itemBrightness"; value: 1 }
// PathAttribute { name: "itemScale"; value: 1 }
// PathLine { x: coverView.pathStartX * .5; y: coverView.pathStartY * .5}
// PathPercent { value: .3 }
// PathAttribute { name: "itemOpacity"; value: 1 }
// PathAttribute { name: "itemBrightness"; value: 1 }
// PathAttribute { name: "itemScale"; value: 0.5 }
// // PathLine { x: coverView.pathStartX * .25 ; y: coverView.pathStartY * .25 }
// // PathPercent { value: .75 }
// // PathAttribute { name: "itemOpacity"; value: 1 }
// // PathAttribute { name: "itemBrightness"; value: .5 }
// // PathAttribute { name: "itemScale"; value: 0.4 }
PathLine { x: root.width; y: 0 }
PathPercent { value: 1 }
PathAttribute { name: "itemOpacity"; value: 1 }
PathAttribute { name: "itemBrightness"; value: 0 }
PathAttribute { name: "itemScale"; value: 0.1 }
}
}

View File

@@ -5,8 +5,13 @@ Item {
// Should the artist + track labels be painted // Should the artist + track labels be painted
property bool showLabels: true property bool showLabels: true
// Should the play button be painted on mouse hover? // Should the play button be painted on mouse hover?
property bool showPlayButton: false property bool showPlayButton: false
// if this is true, the play button will be swapped by a pause button
property bool currentyPlayed: false
// Should the mirror be painted? // Should the mirror be painted?
property bool showMirror: false property bool showMirror: false
@@ -57,31 +62,35 @@ Item {
source: "image://albumart/" + artworkId source: "image://albumart/" + artworkId
} }
Rectangle { Rectangle {
id: textBackground id: textBackground
anchors { left: parent.left; right: parent.right; bottom: parent.bottom } anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
height: 32 height: (artistText.height + trackText.height) * 2
anchors.margins: 5 opacity: 1// showLabels ? 1 : 0
color: "black"
opacity: showLabels ? 0.5 : 0
radius: 3 radius: 3
gradient: Gradient {
GradientStop { position: 0.0; color: "#00000000" }
GradientStop { position: 0.5; color: "black" }
GradientStop { position: 1.0; color: "black" }
}
} }
Text { Text {
id: trackText
color: "white" color: "white"
font.bold: true font.bold: true
text: trackName text: trackName
anchors { left: textBackground.left; right: textBackground.right; top: textBackground.top } anchors { left: parent.left; right: parent.right; bottom: artistText.top }
anchors.margins: 2 anchors.margins: 2
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight elide: Text.ElideRight
opacity: showLabels ? 1 : 0 opacity: showLabels ? 1 : 0
} }
Text { Text {
id: artistText
color: "white" color: "white"
text: artistName text: artistName
anchors { left: textBackground.left; right: textBackground.right; bottom: textBackground.bottom } anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
anchors.margins: 2 anchors.margins: 2
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight elide: Text.ElideRight
@@ -144,7 +153,7 @@ Item {
Image { Image {
id: playButton id: playButton
visible: showPlayButton ? mouseArea.containsMouse : false visible: showPlayButton ? mouseArea.containsMouse : false
source: "../images/play-rest.png" source: currentyPlayed ? "../images/pause-rest.png" : "../images/play-rest.png"
anchors.centerIn: parent anchors.centerIn: parent
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent

View File

@@ -6,7 +6,7 @@ Rectangle {
id: scene id: scene
color: "black" color: "black"
anchors.fill: parent anchors.fill: parent
state: echonestStation.configured ? "list" : "configure" state: "list"
ListModel { ListModel {
id: styleModel id: styleModel
@@ -48,79 +48,79 @@ Rectangle {
id: stationVisualModel id: stationVisualModel
Column { // Column {
height: scene.height // height: scene.height
width: scene.width // width: scene.width
Row { // Row {
height: scene.height / 2 // height: scene.height / 2
width: scene.width // width: scene.width
spacing: width * .1 // spacing: width * .1
Item { // Item {
height: parent.height // height: parent.height
width: (parent.width - orText.width - parent.spacing * 2 ) * 2 / 3 // width: (parent.width - orText.width - parent.spacing * 2 ) * 2 / 3
GridView { // GridView {
id: gridView // id: gridView
anchors.fill: parent // anchors.fill: parent
anchors.margins: cellWidth / 2 // anchors.margins: cellWidth / 2
model: dummyArtistModel // model: dummyArtistModel
cellWidth: gridView.width / 4 - 1 // -1 to make sure there is space for 4 items even with rounding error // cellWidth: gridView.width / 4 - 1 // -1 to make sure there is space for 4 items even with rounding error
cellHeight: cellWidth // cellHeight: cellWidth
delegate: Item { // delegate: Item {
height: gridView.cellHeight * .9 // height: gridView.cellHeight * .9
width: height // width: height
CoverImage { // CoverImage {
artistName: modelData // artistName: modelData
anchors.fill: parent // anchors.fill: parent
onClicked: { // onClicked: {
echonestStation.setMainControl( EchonestStation.StationTypeArtist, modelData ); // echonestStation.setMainControl( EchonestStation.StationTypeArtist, modelData );
stationListView.incrementCurrentIndex(); // stationListView.incrementCurrentIndex();
} // }
} // }
} // }
} // }
} // }
} // }
Row { // Row {
height: scene.height / 2 // height: scene.height / 2
width: scene.width * .9 // width: scene.width * .9
anchors.horizontalCenter: parent.horizontalCenter // anchors.horizontalCenter: parent.horizontalCenter
spacing: width * .1 // spacing: width * .1
TagCloud { // TagCloud {
height: parent.height // height: parent.height
width: (parent.width - orText.width - parent.spacing * 2 ) * 2 / 3 // width: (parent.width - orText.width - parent.spacing * 2 ) * 2 / 3
model: styleModel//generator.styles() // model: styleModel//generator.styles()
opacity: echonestStation.configured ? 0 : 1 // opacity: echonestStation.configured ? 0 : 1
onTagClicked: { // onTagClicked: {
echonestStation.setMainControl( EchonestStation.StationTypeStyle, item ); // echonestStation.setMainControl( EchonestStation.StationTypeStyle, item );
stationListView.incrementCurrentIndex(); // stationListView.incrementCurrentIndex();
} // }
Behavior on opacity { // Behavior on opacity {
NumberAnimation { duration: 300 } // NumberAnimation { duration: 300 }
} // }
} // }
Text { // Text {
id: orText // id: orText
text: "or" // text: "or"
color: "white" // color: "white"
anchors.verticalCenter: parent.verticalCenter // anchors.verticalCenter: parent.verticalCenter
} // }
InputField { // InputField {
anchors.verticalCenter: parent.verticalCenter // anchors.verticalCenter: parent.verticalCenter
width: (parent.width - orText.width - parent.spacing * 2 ) * 1 / 3 // width: (parent.width - orText.width - parent.spacing * 2 ) * 1 / 3
} // }
} // }
} // }
StationView { StationView {
coverSize: Math.min(scene.height, scene.width) / 2 coverSize: Math.min(scene.height, scene.width) / 2
@@ -147,12 +147,12 @@ Rectangle {
orientation: ListView.Horizontal orientation: ListView.Horizontal
model: stationVisualModel model: stationVisualModel
interactive: false interactive: false
highlightMoveDuration: 400 //highlightMoveDuration: 400
Component.onCompleted: { // currentIndex: 1
if ( echonestStation.configured ) {
currentIndex = 1 // Component.onCompleted: {
} // currentIndex = 1
} // }
} }
} }

View File

@@ -8,79 +8,24 @@ Item {
signal configure() signal configure()
PathView {
CoverFlip {
id: coverView id: coverView
anchors.fill: parent anchors.fill: parent
anchors.rightMargin: parent.width / 3 anchors.leftMargin: parent.width / 2
preferredHighlightBegin: 0.2 // scene.width / 11000
preferredHighlightEnd: preferredHighlightBegin
pathItemCount: 5
//highlightMoveDuration: 500
model: dynamicModel model: dynamicModel
currentIndex: currentlyPlayedIndex
property int pathStartX: width / 2 onItemPlayPauseClicked: {
property int pathStartY: height / 2 rootView.playItem(index)
delegate: CoverImage {
height: root.coverSize
width: root.coverSize
showLabels: false
showMirror: true
//artistName: model.artistName
//trackName: model.trackName
artworkId: model.coverID
scale: PathView.itemScale
itemBrightness: PathView.itemBrightness
opacity: PathView.itemOpacity
z: x
onClicked: {
if ( currentlyPlayedIndex !==-1 ) {
echonestStation.playItem( index )
}
}
}
path: Path {
startX: coverView.pathStartX
startY: coverView.pathStartY
PathAttribute { name: "itemOpacity"; value: 0 }
PathAttribute { name: "itemBrightness"; value: 0 }
PathAttribute { name: "itemScale"; value: 1.5 }
PathLine { x: coverView.pathStartX * 0.9 ; y: coverView.pathStartY * 0.9 }
PathPercent { value: .2 }
PathAttribute { name: "itemOpacity"; value: 1 }
PathAttribute { name: "itemBrightness"; value: 1 }
PathAttribute { name: "itemScale"; value: 1 }
PathLine { x: coverView.pathStartX * .5; y: coverView.pathStartY * .5}
PathPercent { value: .3 }
PathAttribute { name: "itemOpacity"; value: 1 }
PathAttribute { name: "itemBrightness"; value: 1 }
PathAttribute { name: "itemScale"; value: 0.5 }
// PathLine { x: coverView.pathStartX * .25 ; y: coverView.pathStartY * .25 }
// PathPercent { value: .75 }
// PathAttribute { name: "itemOpacity"; value: 1 }
// PathAttribute { name: "itemBrightness"; value: .5 }
// PathAttribute { name: "itemScale"; value: 0.4 }
PathLine { x: 0; y: 0 }
PathPercent { value: 1 }
PathAttribute { name: "itemOpacity"; value: 1 }
PathAttribute { name: "itemBrightness"; value: 0 }
PathAttribute { name: "itemScale"; value: 0.1 }
} }
} }
Item { Item {
anchors { top: parent.top; right: parent.right; bottom: parent.bottom } anchors { top: parent.top; left: parent.left; bottom: parent.bottom }
anchors.margins: 50 anchors.margins: 50
width: scene.width / 3 width: scene.width / 2
Column { Column {
anchors { left: parent.left; top: parent.top; right: parent.right } anchors { left: parent.left; top: parent.top; right: parent.right }

View File

@@ -171,5 +171,6 @@
<file>data/qml/tomahawkimports/InputField.qml</file> <file>data/qml/tomahawkimports/InputField.qml</file>
<file>data/qml/tomahawkimports/Button.qml</file> <file>data/qml/tomahawkimports/Button.qml</file>
<file>data/qml/tomahawkimports/DoubleSlider.qml</file> <file>data/qml/tomahawkimports/DoubleSlider.qml</file>
<file>data/qml/CoverFlip.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -33,6 +33,7 @@ Q_OBJECT
Q_PROPERTY(QString name READ name NOTIFY dataChanged) Q_PROPERTY(QString name READ name NOTIFY dataChanged)
Q_PROPERTY(QString artistName READ artistName NOTIFY dataChanged) Q_PROPERTY(QString artistName READ artistName NOTIFY dataChanged)
Q_PROPERTY(QString albumName READ albumName NOTIFY dataChanged) Q_PROPERTY(QString albumName READ albumName NOTIFY dataChanged)
Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY dataChanged)
public: public:
~PlayableItem(); ~PlayableItem();

View File

@@ -167,6 +167,17 @@ EchonestGenerator::generate( int number )
// qWarning() << "Got invalid controls!" << e.what(); // qWarning() << "Got invalid controls!" << e.what();
// emit error( "Filters are not valid", e.what() ); // emit error( "Filters are not valid", e.what() );
// } // }
QList< query_ptr > queries;
queries << Query::get("Colour Haze", "All", QString(), uuid(), true);
queries << Query::get("Colour Haze", "Sun", QString(), uuid(), true);
queries << Query::get("Colour Haze", "Zen", QString(), uuid(), true);
queries << Query::get("Colour Haze", "Outside", QString(), uuid(), true);
queries << Query::get("Colour Haze", "Dirt", QString(), uuid(), true);
emit generated( queries );
} }

View File

@@ -42,8 +42,6 @@ DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* pa
m_model->loadPlaylist( m_playlist ); m_model->loadPlaylist( m_playlist );
// Initially seed the playlist
m_playlist->generator()->generate( 20 );
qDebug() << "###got" << m_playlist->generator()->controls().size() << "controls"; qDebug() << "###got" << m_playlist->generator()->controls().size() << "controls";
@@ -55,6 +53,7 @@ DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* pa
rootContext()->setContextProperty( "dynamicModel", m_proxyModel ); rootContext()->setContextProperty( "dynamicModel", m_proxyModel );
rootContext()->setContextProperty( "generator", m_playlist->generator().data() ); rootContext()->setContextProperty( "generator", m_playlist->generator().data() );
rootContext()->setContextProperty( "rootView", this );
setSource( QUrl( "qrc" RESPATH "qml/StationScene.qml" ) ); setSource( QUrl( "qrc" RESPATH "qml/StationScene.qml" ) );
@@ -67,6 +66,9 @@ DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* pa
connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), this, SLOT( trackStarted() ) ); 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 ) ) ); connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistChanged( Tomahawk::playlistinterface_ptr ) ) );
// Initially seed the playlist
m_playlist->generator()->generate( 20 );
} }
@@ -114,6 +116,17 @@ playlist_ptr DynamicQmlWidget::playlist() const
return m_model->playlist(); return m_model->playlist();
} }
void DynamicQmlWidget::playItem(int index)
{
qDebug() << "playItem called for cover" << index;
AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), m_proxyModel->itemFromIndex( index )->result() );
}
void DynamicQmlWidget::pause()
{
AudioEngine::instance()->pause();
}
void DynamicQmlWidget::currentItemChanged( const QPersistentModelIndex &currentIndex ) void DynamicQmlWidget::currentItemChanged( const QPersistentModelIndex &currentIndex )
{ {
rootContext()->setContextProperty( "currentlyPlayedIndex", m_proxyModel->mapFromSource( currentIndex ).row() ); rootContext()->setContextProperty( "currentlyPlayedIndex", m_proxyModel->mapFromSource( currentIndex ).row() );

View File

@@ -54,6 +54,10 @@ public:
playlist_ptr playlist() const; playlist_ptr playlist() const;
public slots:
void playItem(int index);
void pause();
private slots: private slots:
void currentItemChanged( const QPersistentModelIndex &currentIndex ); void currentItemChanged( const QPersistentModelIndex &currentIndex );
void tracksGenerated( const QList< Tomahawk::query_ptr>& queries ); void tracksGenerated( const QList< Tomahawk::query_ptr>& queries );