Allow toggling of individual global search providers.

This commit is contained in:
David Sansome 2011-10-01 21:22:01 +01:00
parent 3e189f53bc
commit 292610c20b
12 changed files with 268 additions and 62 deletions

View File

@ -9,24 +9,24 @@
}
#search {
border: 1px groove rgba(128, 128, 128, 60%);
border: 1px groove rgb(100, 100, 100);
border-radius: 4px 4px;
background-color: rgba(0, 0, 0, 20%)
background-color: rgba(0, 0, 0, 20%);
}
QToolButton {
font-weight: normal;
font-weight: bold;
color: rgba(255, 255, 255, 50%);
}
QToolButton:hover {
background-color: black;
border: 2px solid rgba(255, 255, 255, 20%);
border-radius: 3px;
}
QToolButton:pressed {
border: 2px solid rgba(255, 255, 255, 20%);
background-color: rgba(255, 255, 255, 10%);
QToolButton:hover {
background-color: rgb(64, 64, 64);
border: 1px solid rgba(255, 255, 255, 20%);
}
QToolButton:pressed {
background-color: rgb(64, 64, 64);
border: 1px solid rgba(255, 255, 255, 50%);
}

View File

@ -19,10 +19,12 @@
#include "globalsearch.h"
#include "core/logging.h"
#include <QSettings>
#include <QStringBuilder>
#include <QUrl>
const int GlobalSearch::kDelayedSearchTimeoutMs = 200;
const char* GlobalSearch::kSettingsGroup = "GlobalSearch";
GlobalSearch::GlobalSearch(QObject* parent)
@ -45,7 +47,11 @@ void GlobalSearch::AddProvider(SearchProvider* provider) {
connect(provider, SIGNAL(destroyed(QObject*)),
SLOT(ProviderDestroyedSlot(QObject*)));
providers_ << provider;
ProviderData data;
data.enabled_ = !disabled_provider_ids_.contains(provider->id());
providers_[provider] = data;
emit ProviderAdded(provider);
}
int GlobalSearch::SearchAsync(const QString& query) {
@ -54,7 +60,10 @@ int GlobalSearch::SearchAsync(const QString& query) {
int timer_id = -1;
pending_search_providers_[id] = providers_.count();
foreach (SearchProvider* provider, providers_) {
foreach (SearchProvider* provider, providers_.keys()) {
if (!providers_[provider].enabled_)
continue;
if (provider->wants_delayed_queries()) {
if (timer_id == -1) {
timer_id = startTimer(kDelayedSearchTimeoutMs);
@ -131,8 +140,8 @@ void GlobalSearch::ProviderDestroyedSlot(QObject* object) {
if (!providers_.contains(provider))
return;
providers_.removeAll(provider);
emit ProviderDestroyed(provider);
providers_.remove(provider);
emit ProviderRemoved(provider);
// We have to abort any pending searches since we can't tell whether they
// were on this provider.
@ -142,19 +151,31 @@ void GlobalSearch::ProviderDestroyedSlot(QObject* object) {
pending_search_providers_.clear();
}
QList<SearchProvider*> GlobalSearch::providers() const {
return providers_.keys();
}
int GlobalSearch::LoadArtAsync(const SearchProvider::Result& result) {
const int id = next_id_ ++;
pending_art_searches_[id] = result.pixmap_cache_key_;
if (!providers_.contains(result.provider_) ||
!providers_[result.provider_].enabled_) {
emit ArtLoaded(id, QPixmap());
return id;
}
if (result.provider_->wants_serialised_art()) {
QueuedArt request;
request.id_ = id;
request.result_ = result;
queued_art_[result.provider_].append(request);
QList<QueuedArt>* queued_art = &providers_[result.provider_].queued_art_;
if (queued_art_[result.provider_].count() == 1) {
queued_art->append(request);
if (queued_art->count() == 1) {
TakeNextQueuedArt(result.provider_);
}
} else {
@ -165,10 +186,11 @@ int GlobalSearch::LoadArtAsync(const SearchProvider::Result& result) {
}
void GlobalSearch::TakeNextQueuedArt(SearchProvider* provider) {
if (queued_art_[provider].isEmpty())
if (!providers_.contains(provider) ||
providers_[provider].queued_art_.isEmpty())
return;
const QueuedArt& data = queued_art_[provider].first();
const QueuedArt& data = providers_[provider].queued_art_.first();
provider->LoadArtAsync(data.id_, data.result_);
}
@ -181,8 +203,9 @@ void GlobalSearch::ArtLoadedSlot(int id, const QImage& image) {
emit ArtLoaded(id, pixmap);
if (!queued_art_[provider].isEmpty()) {
queued_art_[provider].removeFirst();
if (providers_.contains(provider) &&
!providers_[provider].queued_art_.isEmpty()) {
providers_[provider].queued_art_.removeFirst();
TakeNextQueuedArt(provider);
}
}
@ -200,3 +223,33 @@ int GlobalSearch::LoadTracksAsync(const SearchProvider::Result& result) {
return id;
}
void GlobalSearch::SetProviderEnabled(const SearchProvider* const_provider,
bool enabled) {
SearchProvider* provider = const_cast<SearchProvider*>(const_provider);
if (!providers_.contains(provider))
return;
if (providers_[provider].enabled_ != enabled) {
providers_[provider].enabled_ = enabled;
emit ProviderToggled(provider, enabled);
}
}
bool GlobalSearch::is_provider_enabled(const SearchProvider* const_provider) const {
SearchProvider* provider = const_cast<SearchProvider*>(const_provider);
if (!providers_.contains(provider))
return false;
return providers_[provider].enabled_;
}
void GlobalSearch::ReloadSettings() {
QSettings s;
s.beginGroup(kSettingsGroup);
disabled_provider_ids_ = s.value("disabled_providers").toStringList();
foreach (SearchProvider* provider, providers_.keys()) {
SetProviderEnabled(provider, !disabled_provider_ids_.contains(provider->id()));
}
}

View File

@ -31,8 +31,10 @@ public:
GlobalSearch(QObject* parent = 0);
static const int kDelayedSearchTimeoutMs;
static const char* kSettingsGroup;
void AddProvider(SearchProvider* provider);
void SetProviderEnabled(const SearchProvider* provider, bool enabled);
int SearchAsync(const QString& query);
int LoadArtAsync(const SearchProvider::Result& result);
@ -43,6 +45,12 @@ public:
bool FindCachedPixmap(const SearchProvider::Result& result, QPixmap* pixmap) const;
QList<SearchProvider*> providers() const;
bool is_provider_enabled(const SearchProvider* provider) const;
public slots:
void ReloadSettings();
signals:
void ResultsAvailable(int id, const SearchProvider::ResultList& results);
void ProviderSearchFinished(int id, const SearchProvider* provider);
@ -52,7 +60,9 @@ signals:
void TracksLoaded(int id, MimeData* mime_data);
void ProviderDestroyed(SearchProvider* provider);
void ProviderAdded(const SearchProvider* provider);
void ProviderRemoved(const SearchProvider* provider);
void ProviderToggled(const SearchProvider* provider, bool enabled);
protected:
void timerEvent(QTimerEvent* e);
@ -81,16 +91,22 @@ private:
SearchProvider::Result result_;
};
QList<SearchProvider*> providers_;
struct ProviderData {
QList<QueuedArt> queued_art_;
bool enabled_;
};
QMap<SearchProvider*, ProviderData> providers_;
QMap<int, DelayedSearch> delayed_searches_;
QMap<SearchProvider*, QList<QueuedArt> > queued_art_;
int next_id_;
QMap<int, int> pending_search_providers_;
QPixmapCache pixmap_cache_;
QMap<int, QString> pending_art_searches_;
QStringList disabled_provider_ids_;
};
#endif // GLOBALSEARCH_H

View File

@ -27,6 +27,7 @@
#include "core/utilities.h"
#include "playlist/playlistview.h"
#include "playlist/songmimedata.h"
#include "ui/qt_blurimage.h"
#include "widgets/stylehelper.h"
#include <QDesktopWidget>
@ -36,12 +37,13 @@
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QTimer>
#include <QToolButton>
#include <QToolTip>
const int GlobalSearchWidget::kMinVisibleItems = 3;
const int GlobalSearchWidget::kMaxVisibleItems = 25;
const int GlobalSearchWidget::kSwapModelsTimeoutMsec = 250;
const char* GlobalSearchWidget::kSettingsGroup = "GlobalSearch";
GlobalSearchWidget::GlobalSearchWidget(QWidget* parent)
@ -142,6 +144,16 @@ void GlobalSearchWidget::Init(GlobalSearch* engine) {
connect(engine_, SIGNAL(TracksLoaded(int,MimeData*)), SLOT(TracksLoaded(int,MimeData*)),
Qt::QueuedConnection);
connect(engine_, SIGNAL(ProviderAdded(const SearchProvider*)),
SLOT(ProviderAdded(const SearchProvider*)));
connect(engine_, SIGNAL(ProviderRemoved(const SearchProvider*)),
SLOT(ProviderRemoved(const SearchProvider*)));
// Take all the ProviderAdded signals we missed.
foreach (const SearchProvider* provider, engine_->providers()) {
ProviderAdded(provider);
}
view_->setStyle(new PlaylistProxyStyle(style()));
// The style helper's base color doesn't get initialised until after the
@ -309,6 +321,10 @@ bool GlobalSearchWidget::eventFilter(QObject* o, QEvent* e) {
if (o == view_)
return EventFilterPopup(o, e);
QToolButton* button = qobject_cast<QToolButton*>(o);
if (button && provider_buttons_.values().contains(button))
return EventFilterProviderButton(button, e);
return QWidget::eventFilter(o, e);
}
@ -457,6 +473,28 @@ bool GlobalSearchWidget::EventFilterPopup(QObject*, QEvent* e) {
return false;
}
bool GlobalSearchWidget::EventFilterProviderButton(QToolButton* button, QEvent* e) {
switch (e->type()) {
case QEvent::Enter:
QToolTip::showText(button->mapToGlobal(button->rect().bottomLeft()),
button->toolTip(), button);
break;
case QEvent::Leave:
QToolTip::hideText();
break;
case QEvent::ToolTip:
// Ignore normal tooltip events.
return true;
default:
break;
}
return false;
}
void GlobalSearchWidget::LazyLoadArt(const QModelIndex& proxy_index) {
if (!proxy_index.isValid() || proxy_index.data(Role_LazyLoadingArt).isValid()) {
return;
@ -561,7 +599,7 @@ void GlobalSearchWidget::TracksLoaded(int id, MimeData* mime_data) {
void GlobalSearchWidget::ReloadSettings() {
QSettings s;
s.beginGroup(kSettingsGroup);
s.beginGroup(GlobalSearch::kSettingsGroup);
combine_identical_results_ = s.value("combine_identical_results", true).toBool();
provider_order_ = s.value("provider_order", QStringList() << "library").toStringList();
@ -654,3 +692,63 @@ void GlobalSearchWidget::UpdateTooltip() {
tooltip_->SetResults(results);
tooltip_->ShowAt(view_->mapToGlobal(popup_pos));
}
void GlobalSearchWidget::ProviderAdded(const SearchProvider* provider) {
if (provider_buttons_.contains(provider)) {
qLog(Error) << "Tried to add the same provider twice:"
<< provider->name() << provider->id();
return;
}
// Create a button for the provider.
QToolButton* button = new QToolButton(this);
button->setToolTip(tr("Show results from %1").arg(provider->name()));
button->setCheckable(true);
button->setChecked(engine_->is_provider_enabled(provider));
button->installEventFilter(this);
// Make the "Off" icon state greyed out, semi transparent and blurred.
QImage disabled_image = provider->icon().pixmap(
button->iconSize(), QIcon::Disabled).toImage();
QImage off_image = QImage(disabled_image.size(), QImage::Format_ARGB32);
off_image.fill(Qt::transparent);
QPainter p(&off_image);
p.setOpacity(0.5);
qt_blurImage(&p, disabled_image, 3.0, true, false);
p.end();
QIcon icon;
icon.addPixmap(provider->icon().pixmap(button->iconSize(), QIcon::Normal),
QIcon::Normal, QIcon::On);
icon.addPixmap(QPixmap::fromImage(off_image), QIcon::Normal, QIcon::Off);
button->setIcon(icon);
connect(button, SIGNAL(toggled(bool)), SLOT(ProviderButtonToggled(bool)));
ui_->provider_layout->insertWidget(0, button);
provider_buttons_[provider] = button;
}
void GlobalSearchWidget::ProviderRemoved(const SearchProvider* provider) {
if (!provider_buttons_.contains(provider)) {
qLog(Error) << "Tried to remove a provider that hadn't been added yet:"
<< provider->name() << provider->id();
return;
}
delete provider_buttons_.take(provider);
}
void GlobalSearchWidget::ProviderButtonToggled(bool on) {
QToolButton* button = qobject_cast<QToolButton*>(sender());
if (!button)
return;
const SearchProvider* provider = provider_buttons_.key(button);
if (!provider)
return;
engine_->SetProviderEnabled(provider, on);
}

View File

@ -34,6 +34,7 @@ class QMimeData;
class QModelIndex;
class QSortFilterProxyModel;
class QStandardItemModel;
class QToolButton;
class GlobalSearchWidget : public QWidget {
@ -46,7 +47,6 @@ public:
static const int kMinVisibleItems;
static const int kMaxVisibleItems;
static const int kSwapModelsTimeoutMsec;
static const char* kSettingsGroup;
enum Role {
Role_PrimaryResult = Qt::UserRole + 1,
@ -93,6 +93,10 @@ private slots:
void SwapModels();
void ProviderAdded(const SearchProvider* provider);
void ProviderRemoved(const SearchProvider* provider);
void ProviderButtonToggled(bool on);
private:
// Return values from CanCombineResults
enum CombineAction {
@ -107,6 +111,7 @@ private:
bool EventFilterSearchWidget(QObject* o, QEvent* e);
bool EventFilterPopup(QObject* o, QEvent* e);
bool EventFilterProviderButton(QToolButton* button, QEvent* e);
void LoadTracks(QAction* trigger);
@ -153,6 +158,8 @@ private:
QAction* replace_;
QAction* replace_and_play_;
QList<QAction*> actions_;
QMap<const SearchProvider*, QToolButton*> provider_buttons_;
};
#endif // GLOBALSEARCHWIDGET_H

View File

@ -22,7 +22,10 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="provider_layout">
<property name="spacing">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
@ -36,34 +39,6 @@
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Include:</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="toolButton">
<property name="text">
<string>Library</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="toolButton_2">
<property name="text">
<string>Spotify</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="toolButton_3">
<property name="text">
<string>Magnatune</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>

View File

@ -807,6 +807,7 @@ void MainWindow::ReloadAllSettings() {
ReloadSettings();
// Other settings
global_search_->ReloadSettings();
library_->ReloadSettings();
player_->ReloadSettings();
osd_->ReloadSettings();

25
src/ui/qt_blurimage.h Normal file
View File

@ -0,0 +1,25 @@
/* This file is part of Clementine.
Copyright 2011, David Sansome <me@davidsansome.com>
Clementine 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.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QT_BLURIMAGE_H
#define QT_BLURIMAGE_H
// Exported by QtGui
void qt_blurImage(QPainter* p, QImage& blurImage, qreal radius, bool quality,
bool alphaOnly, int transposed = 0);
#endif // QT_BLURIMAGE_H

View File

@ -33,13 +33,12 @@
#include <QtGui/QColor>
#include <QtGui/QStyle>
#include "ui/qt_blurimage.h"
QT_BEGIN_NAMESPACE
class QPalette;
class QPainter;
class QRect;
// Note, this is exported but in a private header as qtopengl depends on it.
// We should consider adding this as a public helper function.
void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
QT_END_NAMESPACE
// Helper class holding all custom color values

View File

@ -1,4 +1,22 @@
/* This file is part of Clementine.
Copyright 2011, David Sansome <me@davidsansome.com>
Clementine 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.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tracksliderpopup.h"
#include "ui/qt_blurimage.h"
#include <QBitmap>
#include <QCoreApplication>
@ -14,7 +32,6 @@ const int TrackSliderPopup::kPointWidth = 4;
const int TrackSliderPopup::kBorderRadius = 4;
const qreal TrackSliderPopup::kBlurRadius = 20.0;
void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
TrackSliderPopup::TrackSliderPopup(QWidget* parent)
: QWidget(parent),

View File

@ -1,3 +1,20 @@
/* This file is part of Clementine.
Copyright 2011, David Sansome <me@davidsansome.com>
Clementine 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.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRACKSLIDERPOPUP_H
#define TRACKSLIDERPOPUP_H

View File

@ -16,6 +16,7 @@
*/
#include "widgetfadehelper.h"
#include "ui/qt_blurimage.h"
#include <QResizeEvent>
#include <QPainter>
@ -25,9 +26,6 @@
const int WidgetFadeHelper::kLoadingPadding = 9;
const int WidgetFadeHelper::kLoadingBorderRadius = 10;
// Exported by QtGui
void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
WidgetFadeHelper::WidgetFadeHelper(QWidget* parent, int msec)
: QWidget(parent),
parent_(parent),