mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-09 07:36:48 +02:00
Add a summary generator to the dynamic playlists. Now when you collapse the controls a sentence representing the controls is shown.
This will need some work to make the english flow better, but this is a good start.
This commit is contained in:
@@ -61,6 +61,7 @@
|
|||||||
<file>./data/images/list-add.png</file>
|
<file>./data/images/list-add.png</file>
|
||||||
<file>./data/images/list-remove.png</file>
|
<file>./data/images/list-remove.png</file>
|
||||||
<file>./data/images/arrow-up-double.png</file>
|
<file>./data/images/arrow-up-double.png</file>
|
||||||
|
<file>./data/images/arrow-down-double.png</file>
|
||||||
<file>./data/images/volume-icon-full.png</file>
|
<file>./data/images/volume-icon-full.png</file>
|
||||||
<file>./data/images/volume-icon-muted.png</file>
|
<file>./data/images/volume-icon-muted.png</file>
|
||||||
<file>./data/images/volume-slider-bkg.png</file>
|
<file>./data/images/volume-slider-bkg.png</file>
|
||||||
|
@@ -44,6 +44,7 @@ class DynamicControl : public QObject
|
|||||||
Q_PROPERTY( QString selectedType READ selectedType WRITE setSelectedType )
|
Q_PROPERTY( QString selectedType READ selectedType WRITE setSelectedType )
|
||||||
Q_PROPERTY( QString match READ match WRITE setMatch )
|
Q_PROPERTY( QString match READ match WRITE setMatch )
|
||||||
Q_PROPERTY( QString input READ input WRITE setInput )
|
Q_PROPERTY( QString input READ input WRITE setInput )
|
||||||
|
Q_PROPERTY( QString summary READ summary ) // a summary of the control in phrase form
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DynamicControl( const QStringList& typeSelectors = QStringList() );
|
DynamicControl( const QStringList& typeSelectors = QStringList() );
|
||||||
@@ -66,17 +67,18 @@ public:
|
|||||||
virtual QWidget* inputField() { Q_ASSERT( false ); return 0; }
|
virtual QWidget* inputField() { Q_ASSERT( false ); return 0; }
|
||||||
|
|
||||||
/// The user-readable match value, for showing in read-only playlists
|
/// The user-readable match value, for showing in read-only playlists
|
||||||
virtual QString matchString() { Q_ASSERT( false ); return QString(); }
|
virtual QString matchString() const { Q_ASSERT( false ); return QString(); }
|
||||||
|
|
||||||
/// the serializable value of the match
|
/// the serializable value of the match
|
||||||
virtual QString match() const { Q_ASSERT( false ); return QString(); }
|
virtual QString match() const { Q_ASSERT( false ); return QString(); }
|
||||||
/// the serializable value of the input
|
/// the serializable value of the input
|
||||||
virtual QString input() const { Q_ASSERT( false ); return QString(); }
|
virtual QString input() const { Q_ASSERT( false ); return QString(); }
|
||||||
|
/// the user-readable summary phrase
|
||||||
|
virtual QString summary() const { Q_ASSERT( false ); return QString(); }
|
||||||
|
|
||||||
// used by JSON serialization
|
// used by JSON serialization
|
||||||
virtual void setMatch( const QString& match ) { Q_ASSERT( false ); }
|
virtual void setMatch( const QString& match ) { Q_ASSERT( false ); }
|
||||||
virtual void setInput( const QString& input ) { Q_ASSERT( false ); }
|
virtual void setInput( const QString& input ) { Q_ASSERT( false ); }
|
||||||
|
|
||||||
/// All the potential type selectors for this control
|
/// All the potential type selectors for this control
|
||||||
QStringList typeSelectors() const { return m_typeSelectors; }
|
QStringList typeSelectors() const { return m_typeSelectors; }
|
||||||
|
|
||||||
@@ -104,9 +106,6 @@ protected:
|
|||||||
// Private constructor, you can't make one. Get it from your Generator.
|
// Private constructor, you can't make one. Get it from your Generator.
|
||||||
explicit DynamicControl( const QString& selectedType, const QStringList& typeSelectors, QObject* parent = 0 );
|
explicit DynamicControl( const QString& selectedType, const QStringList& typeSelectors, QObject* parent = 0 );
|
||||||
|
|
||||||
QString m_match;
|
|
||||||
QString m_input;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_type;
|
QString m_type;
|
||||||
QString m_selectedType;
|
QString m_selectedType;
|
||||||
|
@@ -80,6 +80,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual void fetchNext( int rating = -1 ) {}
|
virtual void fetchNext( int rating = -1 ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a sentence that describes this generator's controls. TODO english only ATM
|
||||||
|
*/
|
||||||
|
virtual QString sentenceSummary() {}
|
||||||
|
|
||||||
/// The type of this generator
|
/// The type of this generator
|
||||||
QString type() const { return m_type; }
|
QString type() const { return m_type; }
|
||||||
|
|
||||||
|
@@ -76,30 +76,40 @@ Tomahawk::EchonestControl::toENParam() const
|
|||||||
return m_data;
|
return m_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Tomahawk::EchonestControl::input() const
|
QString
|
||||||
|
Tomahawk::EchonestControl::input() const
|
||||||
{
|
{
|
||||||
return m_data.second.toString();
|
return m_data.second.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Tomahawk::EchonestControl::match() const
|
QString
|
||||||
|
Tomahawk::EchonestControl::match() const
|
||||||
{
|
{
|
||||||
return m_matchData;
|
return m_matchData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Tomahawk::EchonestControl::matchString()
|
QString
|
||||||
|
Tomahawk::EchonestControl::matchString() const
|
||||||
{
|
{
|
||||||
return m_matchString;
|
return m_matchString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
Tomahawk::EchonestControl::summary() const
|
||||||
|
{
|
||||||
|
return m_summary;
|
||||||
|
}
|
||||||
|
|
||||||
void Tomahawk::EchonestControl::setInput(const QString& input)
|
void
|
||||||
|
Tomahawk::EchonestControl::setInput(const QString& input)
|
||||||
{
|
{
|
||||||
// TODO generate widgets
|
// TODO generate widgets
|
||||||
m_data.second = input;
|
m_data.second = input;
|
||||||
updateWidgetsFromData();
|
updateWidgetsFromData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tomahawk::EchonestControl::setMatch(const QString& match)
|
void
|
||||||
|
Tomahawk::EchonestControl::setMatch(const QString& match)
|
||||||
{
|
{
|
||||||
// TODO generate widgets
|
// TODO generate widgets
|
||||||
m_matchData = match;
|
m_matchData = match;
|
||||||
@@ -295,6 +305,8 @@ Tomahawk::EchonestControl::updateWidgets()
|
|||||||
m_match = QWeakPointer<QWidget>( new QWidget );
|
m_match = QWeakPointer<QWidget>( new QWidget );
|
||||||
m_input = QWeakPointer<QWidget>( new QWidget );
|
m_input = QWeakPointer<QWidget>( new QWidget );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calculateSummary();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -368,6 +380,8 @@ Tomahawk::EchonestControl::updateData()
|
|||||||
m_data.second = enumVal;
|
m_data.second = enumVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calculateSummary();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -428,6 +442,7 @@ Tomahawk::EchonestControl::updateWidgetsFromData()
|
|||||||
input->setCurrentIndex( val );
|
input->setCurrentIndex( val );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
calculateSummary();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -449,10 +464,69 @@ void Tomahawk::EchonestControl::updateToLabelAndCombo()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Tomahawk::EchonestControl::editingFinished()
|
Tomahawk::EchonestControl::editingFinished()
|
||||||
{
|
{
|
||||||
qDebug() << Q_FUNC_INFO;
|
qDebug() << Q_FUNC_INFO;
|
||||||
m_editingTimer.start();
|
m_editingTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Tomahawk::EchonestControl::calculateSummary()
|
||||||
|
{
|
||||||
|
// turns the current control into an english phrase suitable for embedding into a sentence summary
|
||||||
|
QString summary;
|
||||||
|
if( selectedType() == "Artist" ) {
|
||||||
|
// magic char is used by EchonestGenerator to split the prefix from the artist name
|
||||||
|
if( static_cast< Echonest::DynamicPlaylist::ArtistTypeEnum >( m_matchData.toInt() ) == Echonest::DynamicPlaylist::ArtistType )
|
||||||
|
summary = QString( "only by ~%1" ).arg( m_data.second.toString() );
|
||||||
|
else if( static_cast< Echonest::DynamicPlaylist::ArtistTypeEnum >( m_matchData.toInt() ) == Echonest::DynamicPlaylist::ArtistRadioType )
|
||||||
|
summary = QString( "similar to ~%1" ).arg( m_data.second.toString() );
|
||||||
|
else if( static_cast< Echonest::DynamicPlaylist::ArtistTypeEnum >( m_matchData.toInt() ) == Echonest::DynamicPlaylist::ArtistDescriptionType )
|
||||||
|
summary = QString( "like ~%1" ).arg( m_data.second.toString() );
|
||||||
|
} else if( selectedType() == "Variety" || selectedType() == "Danceability" || selectedType() == "Artist Hotttnesss" || selectedType() == "Energy" || selectedType() == "Artist Familiarity" || selectedType() == "Song Hotttnesss" ) {
|
||||||
|
QString modifier;
|
||||||
|
qreal sliderVal = m_data.second.toReal();
|
||||||
|
// divide into avpproximate chunks
|
||||||
|
if( 0.0 <= sliderVal && sliderVal < 0.2 )
|
||||||
|
modifier = "very low";
|
||||||
|
else if( 0.2 <= sliderVal && sliderVal < 0.4 )
|
||||||
|
modifier = "low";
|
||||||
|
else if( 0.4 <= sliderVal && sliderVal < 0.6 )
|
||||||
|
modifier = "moderate";
|
||||||
|
else if( 0.6 <= sliderVal && sliderVal < 0.8 )
|
||||||
|
modifier = "high";
|
||||||
|
else if( 0.8 <= sliderVal && sliderVal <= 1 )
|
||||||
|
modifier = "very high";
|
||||||
|
summary = QString( "with %1 %2" ).arg( modifier ).arg( selectedType().toLower() );
|
||||||
|
} else if( selectedType() == "Tempo" ) {
|
||||||
|
summary = QString( "about %1 BPM" ).arg( m_data.second.toString() );
|
||||||
|
} else if( selectedType() == "Duration" ) {
|
||||||
|
summary = QString( "about %1 minutes long" ).arg( m_data.second.toString() );
|
||||||
|
} else if( selectedType() == "Loudness" ) {
|
||||||
|
summary = QString( "about %1 dB" ).arg( m_data.second.toString() );
|
||||||
|
} else if( selectedType() == "Latitude" || selectedType() == "Longitude" ) {
|
||||||
|
summary = QString( "at around %1%2 %3" ).arg( m_data.second.toString() ).arg( QString( QChar( 0x00B0 ) ) ).arg( selectedType().toLower() );
|
||||||
|
} else if( selectedType() == "Key" ) {
|
||||||
|
Q_ASSERT( !m_input.isNull() );
|
||||||
|
Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
|
||||||
|
QString keyName = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
|
||||||
|
summary = QString( "in %1" ).arg( keyName );
|
||||||
|
} else if( selectedType() == "Mode" ) {
|
||||||
|
Q_ASSERT( !m_input.isNull() );
|
||||||
|
Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
|
||||||
|
QString modeName = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
|
||||||
|
summary = QString( "in a %1 key" ).arg( modeName );
|
||||||
|
} else if( selectedType() == "Sorting" ) {
|
||||||
|
Q_ASSERT( !m_input.isNull() );
|
||||||
|
Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
|
||||||
|
QString sortType = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
|
||||||
|
|
||||||
|
Q_ASSERT( !m_match.isNull() );
|
||||||
|
Q_ASSERT( qobject_cast< QComboBox* >( m_match.data() ) );
|
||||||
|
QString ascdesc = qobject_cast< QComboBox* >( m_match.data() )->currentText().toLower();
|
||||||
|
|
||||||
|
summary = QString( "sorted in %1 %2 order" ).arg( ascdesc ).arg( sortType );
|
||||||
|
}
|
||||||
|
m_summary = summary;
|
||||||
|
}
|
||||||
|
@@ -37,11 +37,12 @@ public:
|
|||||||
|
|
||||||
virtual QString input() const;
|
virtual QString input() const;
|
||||||
virtual QString match() const;
|
virtual QString match() const;
|
||||||
virtual QString matchString();
|
virtual QString matchString() const;
|
||||||
|
virtual QString summary() const;
|
||||||
|
|
||||||
virtual void setInput(const QString& input);
|
virtual void setInput(const QString& input);
|
||||||
virtual void setMatch(const QString& match);
|
virtual void setMatch(const QString& match);
|
||||||
|
|
||||||
/// DO NOT USE IF YOU ARE NOT A DBCMD
|
/// DO NOT USE IF YOU ARE NOT A DBCMD
|
||||||
explicit EchonestControl( const QString& type, const QStringList& typeSelectors, QObject* parent = 0 );
|
explicit EchonestControl( const QString& type, const QStringList& typeSelectors, QObject* parent = 0 );
|
||||||
|
|
||||||
@@ -64,6 +65,8 @@ private:
|
|||||||
void updateToComboAndSlider( bool smooth = false );
|
void updateToComboAndSlider( bool smooth = false );
|
||||||
void updateToLabelAndCombo();
|
void updateToLabelAndCombo();
|
||||||
|
|
||||||
|
void calculateSummary();
|
||||||
|
|
||||||
Echonest::DynamicPlaylist::PlaylistParam m_currentType;
|
Echonest::DynamicPlaylist::PlaylistParam m_currentType;
|
||||||
int m_overrideType;
|
int m_overrideType;
|
||||||
|
|
||||||
@@ -71,6 +74,7 @@ private:
|
|||||||
QWeakPointer< QWidget > m_match;
|
QWeakPointer< QWidget > m_match;
|
||||||
QString m_matchData;
|
QString m_matchData;
|
||||||
QString m_matchString;
|
QString m_matchString;
|
||||||
|
QString m_summary;
|
||||||
|
|
||||||
QTimer m_editingTimer;
|
QTimer m_editingTimer;
|
||||||
|
|
||||||
|
@@ -224,14 +224,16 @@ void
|
|||||||
EchonestGenerator::appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& params ) const throw( std::runtime_error )
|
EchonestGenerator::appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& params ) const throw( std::runtime_error )
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* so we try to match the best type of echonest playlist, based on the controls
|
* So we try to match the best type of echonest playlist, based on the controls
|
||||||
* the types are artist, artist-radio, artist-description, catalog, catalog-radio, song-radio. we don't care about the catalog ones.
|
* the types are artist, artist-radio, artist-description, catalog, catalog-radio, song-radio. we don't care about the catalog ones, and
|
||||||
|
* we can't use the song ones since for the moment EN only accepts Song IDs, not names, and we don't want to insert an extra song.search
|
||||||
|
* call first.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// 1. artist: If all the artist controls are Limit-To. If some were but not all, error out.
|
/// 1. artist: If all the artist controls are Limit-To. If some were but not all, error out.
|
||||||
/// 2. artist-description: If all the artist entries are Description. If some were but not all, error out.
|
/// 2. artist-description: If all the artist entries are Description. If some were but not all, error out.
|
||||||
/// 3. Artist-Radio: If all the artist entries are Similar To. If some were but not all, error out.
|
/// 3. artist-radio: If all the artist entries are Similar To. If some were but not all, error out.
|
||||||
if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistType ) )
|
if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistType ) )
|
||||||
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistType ) );
|
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistType ) );
|
||||||
else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistDescriptionType ) )
|
else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistDescriptionType ) )
|
||||||
@@ -249,3 +251,80 @@ EchonestGenerator::queryFromSong(const Echonest::Song& song)
|
|||||||
track[ "track" ] = song.title();
|
track[ "track" ] = song.title();
|
||||||
return query_ptr( new Query( track ) );
|
return query_ptr( new Query( track ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
EchonestGenerator::sentenceSummary()
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The idea is we generate an english sentence from the individual phrases of the controls. We have to follow a few rules, but othewise it's quite straightforward.
|
||||||
|
*
|
||||||
|
* Rules:
|
||||||
|
* - Sentence starts with "Songs "
|
||||||
|
* - Artists always go first
|
||||||
|
* - Separate phrases by comma, and before last phrase
|
||||||
|
* - sorting always at end
|
||||||
|
* - collapse artists. "Like X, like Y, like Z, ..." -> "Like X, Y, and Z"
|
||||||
|
*
|
||||||
|
* NOTE / TODO: In order for the sentence to be grammatically correct, we must follow the EN API rules. That means we can't have multiple of some types of filters,
|
||||||
|
* and all Artist types must be the same. The filters aren't checked at the moment until Generate / Play is pressed. Consider doing a check on hide as well.
|
||||||
|
*/
|
||||||
|
QList< dyncontrol_ptr > allcontrols = m_controls;
|
||||||
|
QString sentence = "Songs ";
|
||||||
|
|
||||||
|
/// 1. Collect all artist filters
|
||||||
|
/// 2. Get the sorted by filter if it exists.
|
||||||
|
QList< dyncontrol_ptr > artists;
|
||||||
|
dyncontrol_ptr sorting;
|
||||||
|
foreach( const dyncontrol_ptr& control, allcontrols ) {
|
||||||
|
if( control->selectedType() == "Artist" )
|
||||||
|
artists << control;
|
||||||
|
else if( control->selectedType() == "Sorting" )
|
||||||
|
sorting = control;
|
||||||
|
}
|
||||||
|
if( !sorting.isNull() )
|
||||||
|
allcontrols.removeAll( sorting );
|
||||||
|
|
||||||
|
/// Do the assembling. Start with the artists if there are any, then do all the rest.
|
||||||
|
for( int i = 0; i < artists.size(); i++ ) {
|
||||||
|
dyncontrol_ptr artist = artists.value( i );
|
||||||
|
allcontrols.removeAll( artist ); // remove from pool while we're here
|
||||||
|
|
||||||
|
/// Collapse artist lists
|
||||||
|
QString center, suffix;
|
||||||
|
QString summary = artist.dynamicCast< EchonestControl >()->summary();
|
||||||
|
if( i == 0 ) { // if it's the first and only one
|
||||||
|
center = summary.remove( "~" );
|
||||||
|
if( artists.size() == 2 ) // special case for 2, no comma. ( X and Y )
|
||||||
|
suffix = " and ";
|
||||||
|
else
|
||||||
|
suffix = ", ";
|
||||||
|
} else {
|
||||||
|
center = summary.mid( summary.indexOf( "~" ) + 1 );
|
||||||
|
suffix = ", ";
|
||||||
|
if( i != artists.size() - 1 ) // if there are more, add an " and "
|
||||||
|
suffix += "and ";
|
||||||
|
}
|
||||||
|
sentence += center + suffix;
|
||||||
|
}
|
||||||
|
qDebug() << "Got artists:" << sentence;
|
||||||
|
for( int i = 0; i < allcontrols.size(); i++ ) {
|
||||||
|
// end case: if this is the last AND there is not a sorting filter (so this is the real last one)
|
||||||
|
const bool last = ( i == allcontrols.size() - 1 && sorting.isNull() );
|
||||||
|
QString prefix, suffix;
|
||||||
|
if( last ) {
|
||||||
|
prefix = "and ";
|
||||||
|
suffix = ".";
|
||||||
|
} else
|
||||||
|
suffix = ", ";
|
||||||
|
sentence += prefix + allcontrols.value( i ).dynamicCast< EchonestControl >()->summary() + suffix;
|
||||||
|
}
|
||||||
|
qDebug() << "Got artists and contents:" << sentence;
|
||||||
|
|
||||||
|
if( !sorting.isNull() ) {
|
||||||
|
sentence += "and " + sorting.dynamicCast< EchonestControl >()->summary() + ".";
|
||||||
|
}
|
||||||
|
qDebug() << "Got full summary:" << sentence;
|
||||||
|
|
||||||
|
return sentence;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -49,6 +49,7 @@ public:
|
|||||||
virtual void generate ( int number = -1 );
|
virtual void generate ( int number = -1 );
|
||||||
virtual void startOnDemand();
|
virtual void startOnDemand();
|
||||||
virtual void fetchNext( int rating = -1 );
|
virtual void fetchNext( int rating = -1 );
|
||||||
|
virtual QString sentenceSummary();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void staticFinished();
|
void staticFinished();
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "CollapsibleControls.h"
|
#include "CollapsibleControls.h"
|
||||||
|
|
||||||
|
#include "tomahawk/tomahawkapp.h"
|
||||||
#include "DynamicControlList.h"
|
#include "DynamicControlList.h"
|
||||||
#include "DynamicControlWrapper.h"
|
#include "DynamicControlWrapper.h"
|
||||||
#include "dynamic/GeneratorInterface.h"
|
#include "dynamic/GeneratorInterface.h"
|
||||||
@@ -23,6 +24,8 @@
|
|||||||
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QStackedLayout>
|
#include <QStackedLayout>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
using namespace Tomahawk;
|
using namespace Tomahawk;
|
||||||
|
|
||||||
@@ -32,11 +35,12 @@ CollapsibleControls::CollapsibleControls( QWidget* parent )
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
CollapsibleControls::CollapsibleControls( const geninterface_ptr& generator, const QList< dyncontrol_ptr >& controls, bool isLocal, QWidget* parent )
|
CollapsibleControls::CollapsibleControls( const dynplaylist_ptr& playlist, bool isLocal, QWidget* parent )
|
||||||
: QWidget( parent )
|
: QWidget( parent )
|
||||||
|
, m_dynplaylist( playlist )
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
setControls( generator, controls, isLocal );
|
setControls( m_dynplaylist, isLocal );
|
||||||
}
|
}
|
||||||
|
|
||||||
Tomahawk::CollapsibleControls::~CollapsibleControls()
|
Tomahawk::CollapsibleControls::~CollapsibleControls()
|
||||||
@@ -54,16 +58,28 @@ CollapsibleControls::init()
|
|||||||
|
|
||||||
m_controls = new Tomahawk::DynamicControlList( this );
|
m_controls = new Tomahawk::DynamicControlList( this );
|
||||||
m_layout->addWidget( m_controls );
|
m_layout->addWidget( m_controls );
|
||||||
|
connect( m_controls, SIGNAL( toggleCollapse() ), this, SLOT( toggleCollapse() ) );
|
||||||
|
|
||||||
m_summaryWidget = new QWidget( this );
|
m_summaryWidget = new QWidget( this );
|
||||||
// TODO replace
|
// TODO replace
|
||||||
// m_summaryWidget->setMinimumHeight( 24 );
|
// m_summaryWidget->setMinimumHeight( 24 );
|
||||||
// m_summaryWidget->setMaximumHeight( 24 );
|
// m_summaryWidget->setMaximumHeight( 24 );
|
||||||
m_summaryWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
|
m_summaryWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
|
||||||
m_summaryWidget->setLayout( new QVBoxLayout );
|
QHBoxLayout* summaryLayout = new QHBoxLayout;
|
||||||
|
m_summaryWidget->setLayout( summaryLayout );
|
||||||
m_summaryWidget->layout()->setMargin( 0 );
|
m_summaryWidget->layout()->setMargin( 0 );
|
||||||
m_summaryWidget->layout()->addWidget( new QLabel( "replace me plz", m_summaryWidget ) );
|
|
||||||
|
m_summary = new QLabel( m_summaryWidget );
|
||||||
|
summaryLayout->addWidget( m_summary, 1 );
|
||||||
|
m_summaryExpand = new QToolButton( m_summary );
|
||||||
|
m_summaryExpand->setIconSize( QSize( 16, 16 ) );
|
||||||
|
m_summaryExpand->setIcon( QIcon( RESPATH "images/arrow-down-double.png" ) );
|
||||||
|
m_summaryExpand->setToolButtonStyle( Qt::ToolButtonIconOnly );
|
||||||
|
m_summaryExpand->setAutoRaise( true );
|
||||||
|
m_summaryExpand->setContentsMargins( 0, 0, 0, 0 );
|
||||||
|
summaryLayout->addWidget( m_summaryExpand );
|
||||||
m_layout->addWidget( m_summaryWidget );
|
m_layout->addWidget( m_summaryWidget );
|
||||||
|
connect( m_summaryExpand, SIGNAL( clicked( bool ) ), this, SLOT( toggleCollapse() ) );
|
||||||
|
|
||||||
m_layout->setCurrentIndex( 0 );
|
m_layout->setCurrentIndex( 0 );
|
||||||
connect( m_controls, SIGNAL( controlChanged( Tomahawk::dyncontrol_ptr ) ), SIGNAL( controlChanged( Tomahawk::dyncontrol_ptr ) ) );
|
connect( m_controls, SIGNAL( controlChanged( Tomahawk::dyncontrol_ptr ) ), SIGNAL( controlChanged( Tomahawk::dyncontrol_ptr ) ) );
|
||||||
@@ -82,13 +98,19 @@ Tomahawk::CollapsibleControls::controls() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CollapsibleControls::setControls( const geninterface_ptr& generator, const QList< dyncontrol_ptr >& controls, bool isLocal )
|
CollapsibleControls::setControls( const dynplaylist_ptr& playlist, bool isLocal )
|
||||||
{
|
{
|
||||||
m_controls->setControls( generator, controls, isLocal );
|
m_dynplaylist = playlist;
|
||||||
|
m_controls->setControls( m_dynplaylist->generator(), m_dynplaylist->generator()->controls(), isLocal );
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CollapsibleControls::toggleCollapse()
|
CollapsibleControls::toggleCollapse()
|
||||||
{
|
{
|
||||||
|
if( m_layout->currentWidget() == m_controls ) {
|
||||||
|
m_summary->setText( m_dynplaylist->generator()->sentenceSummary() );
|
||||||
|
m_layout->setCurrentWidget( m_summaryWidget );
|
||||||
|
} else {
|
||||||
|
m_layout->setCurrentWidget( m_controls );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QToolButton;
|
||||||
|
class QLabel;
|
||||||
class QStackedLayout;
|
class QStackedLayout;
|
||||||
namespace Tomahawk
|
namespace Tomahawk
|
||||||
{
|
{
|
||||||
@@ -33,10 +35,10 @@ class CollapsibleControls : public QWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CollapsibleControls( QWidget* parent );
|
CollapsibleControls( QWidget* parent );
|
||||||
CollapsibleControls( const geninterface_ptr& generator, const QList< dyncontrol_ptr >& controls, bool isLocal, QWidget* parent = 0 );
|
CollapsibleControls( const dynplaylist_ptr& playlist, bool isLocal, QWidget* parent = 0 );
|
||||||
virtual ~CollapsibleControls();
|
virtual ~CollapsibleControls();
|
||||||
|
|
||||||
void setControls( const geninterface_ptr& generator, const QList< dyncontrol_ptr >& controls, bool isLocal );
|
void setControls( const dynplaylist_ptr& playlist, bool isLocal );
|
||||||
QList< DynamicControlWrapper* > controls() const;
|
QList< DynamicControlWrapper* > controls() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -49,10 +51,13 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
|
dynplaylist_ptr m_dynplaylist;
|
||||||
QStackedLayout* m_layout;
|
QStackedLayout* m_layout;
|
||||||
DynamicControlList* m_controls;
|
DynamicControlList* m_controls;
|
||||||
QWidget* m_summaryWidget;
|
|
||||||
|
|
||||||
|
QWidget* m_summaryWidget;
|
||||||
|
QLabel* m_summary;
|
||||||
|
QToolButton* m_summaryExpand;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -118,7 +118,7 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
|
|||||||
foreach( const dyncontrol_ptr& control, playlist->generator()->controls() ) {
|
foreach( const dyncontrol_ptr& control, playlist->generator()->controls() ) {
|
||||||
qDebug() << "CONTROL:" << control->selectedType() << control->match() << control->input();
|
qDebug() << "CONTROL:" << control->selectedType() << control->match() << control->input();
|
||||||
}
|
}
|
||||||
m_controls->setControls( m_playlist->generator(), m_playlist->generator()->controls(), m_playlist->author()->isLocal() );
|
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
|
||||||
|
|
||||||
m_playlist = playlist;
|
m_playlist = playlist;
|
||||||
m_view->setOnDemand( m_playlist->mode() == OnDemand );
|
m_view->setOnDemand( m_playlist->mode() == OnDemand );
|
||||||
@@ -139,7 +139,7 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
|
|||||||
m_model->loadPlaylist( m_playlist );
|
m_model->loadPlaylist( m_playlist );
|
||||||
|
|
||||||
if( !m_playlist.isNull() )
|
if( !m_playlist.isNull() )
|
||||||
m_controls->setControls( m_playlist->generator(), m_playlist->generator()->controls(), m_playlist->author()->isLocal() );
|
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
|
||||||
|
|
||||||
m_generatorCombo->setWritable( playlist->author()->isLocal() );
|
m_generatorCombo->setWritable( playlist->author()->isLocal() );
|
||||||
m_generatorCombo->setLabel( qobject_cast< QComboBox* >( m_generatorCombo->writableWidget() )->currentText() );
|
m_generatorCombo->setLabel( qobject_cast< QComboBox* >( m_generatorCombo->writableWidget() )->currentText() );
|
||||||
|
Reference in New Issue
Block a user