From 70429217c61881bf812eaf1e3c5c45d728a46307 Mon Sep 17 00:00:00 2001 From: Arnaud Bienner Date: Thu, 28 Jun 2012 23:57:51 +0200 Subject: [PATCH] Search on Spotify on the left/Internet tab directly, instead of Spotify magic playlist (like for Grooveshark) - Next step: fix the "did you mean" widget which is visible to user when displayed --- src/internet/searchboxwidget.cpp | 8 +++ src/internet/searchboxwidget.h | 4 ++ src/internet/spotifysearchplaylisttype.cpp | 4 +- src/internet/spotifyservice.cpp | 59 +++++++++++++++------- src/internet/spotifyservice.h | 10 ++-- 5 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/internet/searchboxwidget.cpp b/src/internet/searchboxwidget.cpp index 5e39cde8d..9a79932d2 100644 --- a/src/internet/searchboxwidget.cpp +++ b/src/internet/searchboxwidget.cpp @@ -19,6 +19,7 @@ #include "searchboxwidget.h" #include "ui_searchboxwidget.h" #include "ui/iconloader.h" +#include "widgets/didyoumean.h" #include #include @@ -43,6 +44,13 @@ SearchBoxWidget::SearchBoxWidget(InternetService* service) ui_->filter->setPlaceholderText(QString("Search on %1").arg(service_->name())); connect(ui_->filter, SIGNAL(textChanged(QString)), SIGNAL(TextChanged(QString))); + + // FIXME: the "Did you mean" suggestion is displayed above the search box, + // but below the internet services tree, which makes it fairly unusuable for + // now :( + did_you_mean_ = new DidYouMean(ui_->filter, this); + connect(did_you_mean_, SIGNAL(Accepted(QString)), + ui_->filter, SLOT(setText(QString))); } SearchBoxWidget::~SearchBoxWidget() { diff --git a/src/internet/searchboxwidget.h b/src/internet/searchboxwidget.h index fc60d17fa..9ba3c6b3b 100644 --- a/src/internet/searchboxwidget.h +++ b/src/internet/searchboxwidget.h @@ -21,6 +21,7 @@ #include class InternetService; +class DidYouMean; class Ui_SearchBoxWidget; class QActionGroup; @@ -33,6 +34,8 @@ public: SearchBoxWidget(InternetService* service); ~SearchBoxWidget(); + DidYouMean* did_you_mean() { return did_you_mean_; } + signals: void TextChanged(const QString& text); @@ -46,6 +49,7 @@ private: InternetService* service_; Ui_SearchBoxWidget* ui_; QMenu* menu_; + DidYouMean* did_you_mean_; }; #endif // SEARCHBOXWIDGET_H diff --git a/src/internet/spotifysearchplaylisttype.cpp b/src/internet/spotifysearchplaylisttype.cpp index a1568e969..f290340f4 100644 --- a/src/internet/spotifysearchplaylisttype.cpp +++ b/src/internet/spotifysearchplaylisttype.cpp @@ -45,5 +45,7 @@ void SpotifySearchPlaylistType::Search(const QString& text, Playlist* playlist) } void SpotifySearchPlaylistType::DidYouMeanClicked(const QString& text, Playlist* playlist) { - service_->Search(text, playlist, true); + // TODO Dead-code now: we will probably remove the entire class later, if the + // new search looks pretty enough for everyone + //service_->Search(text, playlist, true); } diff --git a/src/internet/spotifyservice.cpp b/src/internet/spotifyservice.cpp index ec4a38f7e..0c1eb2345 100644 --- a/src/internet/spotifyservice.cpp +++ b/src/internet/spotifyservice.cpp @@ -8,6 +8,7 @@ #include "core/application.h" #include "core/database.h" #include "core/logging.h" +#include "core/mergedproxymodel.h" #include "core/player.h" #include "core/taskmanager.h" #include "core/timeconstants.h" @@ -17,8 +18,10 @@ #include "playlist/playlist.h" #include "playlist/playlistcontainer.h" #include "playlist/playlistmanager.h" +#include "searchboxwidget.h" #include "widgets/didyoumean.h" #include "ui/iconloader.h" +#include "widgets/didyoumean.h" #include #include @@ -47,8 +50,8 @@ SpotifyService::SpotifyService(Application* app, InternetModel* parent) inbox_(NULL), toplist_(NULL), login_task_id_(0), - pending_search_playlist_(NULL), context_menu_(NULL), + search_box_(new SearchBoxWidget(this)), search_delay_(new QTimer(this)), login_state_(LoginState_OtherError), bitrate_(pb::spotify::Bitrate320k), @@ -80,6 +83,7 @@ SpotifyService::SpotifyService(Application* app, InternetModel* parent) search_delay_->setInterval(kSearchDelayMsec); search_delay_->setSingleShot(true); connect(search_delay_, SIGNAL(timeout()), SLOT(DoSearch())); + connect(search_box_, SIGNAL(TextChanged(QString)), SLOT(Search(QString))); } SpotifyService::~SpotifyService() { @@ -340,7 +344,9 @@ void SpotifyService::PlaylistsUpdated(const pb::spotify::Playlists& response) { // Create starred and inbox playlists if they're not here already if (!search_) { search_ = new QStandardItem(IconLoader::Load("edit-find"), - tr("Search Spotify (opens a new tab)")); + tr("Search results")); + search_->setToolTip(tr("Start typing something on the search box above to " + "fill this search results list")); search_->setData(Type_SearchResults, InternetModel::Role_Type); search_->setData(InternetModel::PlayBehaviour_DoubleClickAction, InternetModel::Role_PlayBehaviour); @@ -494,6 +500,10 @@ PlaylistItem::Options SpotifyService::playlistitem_options() const { return PlaylistItem::PauseDisabled | PlaylistItem::SeekDisabled; } +QWidget* SpotifyService::HeaderWidget() const { + return search_box_; +} + void SpotifyService::EnsureMenuCreated() { if (context_menu_) return; @@ -502,8 +512,6 @@ void SpotifyService::EnsureMenuCreated() { context_menu_->addActions(GetPlaylistActions()); context_menu_->addSeparator(); - context_menu_->addAction(IconLoader::Load("edit-find"), tr("Search Spotify (opens a new tab)..."), this, SLOT(OpenSearchTab())); - context_menu_->addSeparator(); context_menu_->addAction(IconLoader::Load("configure"), tr("Configure Spotify..."), this, SLOT(ShowConfig())); playlist_context_menu_ = new QMenu; @@ -514,6 +522,11 @@ void SpotifyService::EnsureMenuCreated() { SLOT(SyncPlaylist())); } +void SpotifyService::ClearSearchResults() { + if (search_) + search_->removeRows(0, search_->rowCount()); +} + void SpotifyService::SyncPlaylist() { QStandardItem* item = playlist_sync_action_->data().value(); Q_ASSERT(item); @@ -539,11 +552,18 @@ void SpotifyService::SyncPlaylist() { } } -void SpotifyService::Search(const QString& text, Playlist* playlist, bool now) { +void SpotifyService::Search(const QString& text, bool now) { EnsureServerCreated(); pending_search_ = text; - pending_search_playlist_ = playlist; + + // If there is no text (e.g. user cleared search box), we don't need to do a + // real query that will return nothing: we can clear the playlist now + if (text.isEmpty()) { + search_delay_->stop(); + ClearSearchResults(); + return; + } if (now) { search_delay_->stop(); @@ -577,13 +597,22 @@ void SpotifyService::SearchResults(const pb::spotify::SearchResponse& response) qLog(Debug) << "Got" << songs.count() << "results"; - pending_search_playlist_->Clear(); - pending_search_playlist_->InsertSongs(songs); + ClearSearchResults(); - const QString did_you_mean = QStringFromStdString(response.did_you_mean()); - if (!did_you_mean.isEmpty()) { - app_->playlist_manager()->playlist_container()->did_you_mean()->Show(did_you_mean); + // Fill results list + foreach(const Song& song, songs) { + QStandardItem* child = CreateSongItem(song); + search_->appendRow(child); } + + const QString did_you_mean_suggestion = QStringFromStdString(response.did_you_mean()); + qLog(Debug) << "Did you mean suggestion: " << did_you_mean_suggestion; + if (!did_you_mean_suggestion.isEmpty()) { + search_box_->did_you_mean()->Show(did_you_mean_suggestion); + } + + QModelIndex index = model()->merged_model()->mapFromSource(search_->index()); + ScrollToIndex(index); } SpotifyServer* SpotifyService::server() const { @@ -616,15 +645,7 @@ void SpotifyService::ShowContextMenu(const QPoint& global_pos) { context_menu_->popup(global_pos); } -void SpotifyService::OpenSearchTab() { - app_->playlist_manager()->New(tr("Search Spotify"), SongList(), - SpotifySearchPlaylistType::kName); -} - void SpotifyService::ItemDoubleClicked(QStandardItem* item) { - if (item == search_) { - OpenSearchTab(); - } } void SpotifyService::DropMimeData(const QMimeData* data, const QModelIndex& index) { diff --git a/src/internet/spotifyservice.h b/src/internet/spotifyservice.h index 521c0e602..d40520b9a 100644 --- a/src/internet/spotifyservice.h +++ b/src/internet/spotifyservice.h @@ -10,7 +10,9 @@ #include +class DidYouMean; class Playlist; +class SearchBoxWidget; class SpotifyServer; class QMenu; @@ -57,10 +59,10 @@ public: void ItemDoubleClicked(QStandardItem* item); void DropMimeData(const QMimeData* data, const QModelIndex& index); PlaylistItem::Options playlistitem_options() const; + QWidget* HeaderWidget() const; void Logout(); void Login(const QString& username, const QString& password); - void Search(const QString& text, Playlist* playlist, bool now = false); Q_INVOKABLE void LoadImage(const QString& id); SpotifyServer* server() const; @@ -80,6 +82,7 @@ signals: void ImageLoaded(const QString& id, const QImage& image); public slots: + void Search(const QString& text, bool now = false); void ShowConfig(); private: @@ -89,6 +92,7 @@ private: const google::protobuf::RepeatedPtrField& tracks); void FillPlaylist(QStandardItem* item, const pb::spotify::LoadPlaylistResponse& response); void EnsureMenuCreated(); + void ClearSearchResults(); QStandardItem* PlaylistBySpotifyIndex(int index) const; bool DoPlaylistsDiffer(const pb::spotify::Playlists& response) const; @@ -107,7 +111,6 @@ private slots: void SyncPlaylistProgress(const pb::spotify::SyncPlaylistProgress& progress); void ToplistLoaded(const pb::spotify::BrowseToplistResponse& response); - void OpenSearchTab(); void DoSearch(); void SyncPlaylist(); @@ -130,12 +133,13 @@ private: int login_task_id_; QString pending_search_; - Playlist* pending_search_playlist_; QMenu* context_menu_; QMenu* playlist_context_menu_; QAction* playlist_sync_action_; + SearchBoxWidget* search_box_; + QTimer* search_delay_; int inbox_sync_id_;