Sort the results in the popup list, prevent the user from searching for short strings
This commit is contained in:
parent
0f9d2d29bb
commit
cd44c47f7b
|
@ -116,6 +116,8 @@ set(SOURCES
|
||||||
engines/gstelementdeleter.cpp
|
engines/gstelementdeleter.cpp
|
||||||
|
|
||||||
globalsearch/globalsearch.cpp
|
globalsearch/globalsearch.cpp
|
||||||
|
globalsearch/globalsearchitemdelegate.cpp
|
||||||
|
globalsearch/globalsearchsortmodel.cpp
|
||||||
globalsearch/globalsearchwidget.cpp
|
globalsearch/globalsearchwidget.cpp
|
||||||
globalsearch/librarysearchprovider.cpp
|
globalsearch/librarysearchprovider.cpp
|
||||||
globalsearch/searchprovider.cpp
|
globalsearch/searchprovider.cpp
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, 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 "globalsearchitemdelegate.h"
|
||||||
|
#include "globalsearchwidget.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
|
||||||
|
const int GlobalSearchItemDelegate::kHeight = SearchProvider::kArtHeight;
|
||||||
|
const int GlobalSearchItemDelegate::kMargin = 1;
|
||||||
|
const int GlobalSearchItemDelegate::kArtMargin = 6;
|
||||||
|
const int GlobalSearchItemDelegate::kWordPadding = 6;
|
||||||
|
|
||||||
|
GlobalSearchItemDelegate::GlobalSearchItemDelegate(GlobalSearchWidget* widget)
|
||||||
|
: QStyledItemDelegate(widget),
|
||||||
|
widget_(widget)
|
||||||
|
{
|
||||||
|
no_cover_ = ScaleAndPad(QImage(":nocover.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap GlobalSearchItemDelegate::ScaleAndPad(const QImage& image) {
|
||||||
|
if (image.isNull())
|
||||||
|
return QPixmap();
|
||||||
|
|
||||||
|
if (image.size() == QSize(kHeight, kHeight))
|
||||||
|
return QPixmap::fromImage(image);
|
||||||
|
|
||||||
|
// Scale the image down
|
||||||
|
QImage copy;
|
||||||
|
copy = image.scaled(QSize(kHeight, kHeight),
|
||||||
|
Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
|
||||||
|
// Pad the image to kHeight x kHeight
|
||||||
|
QImage padded_image(kHeight, kHeight, QImage::Format_ARGB32);
|
||||||
|
padded_image.fill(0);
|
||||||
|
|
||||||
|
QPainter p(&padded_image);
|
||||||
|
p.drawImage((kHeight - copy.width()) / 2, (kHeight - copy.height()) / 2,
|
||||||
|
copy);
|
||||||
|
p.end();
|
||||||
|
|
||||||
|
return QPixmap::fromImage(padded_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize GlobalSearchItemDelegate::sizeHint(const QStyleOptionViewItem& option,
|
||||||
|
const QModelIndex& index) const {
|
||||||
|
QSize size = QStyledItemDelegate::sizeHint(option, index);
|
||||||
|
size.setHeight(kHeight + kMargin);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalSearchItemDelegate::DrawAndShrink(QPainter* p, QRect* rect,
|
||||||
|
const QString& text) const {
|
||||||
|
QRect br;
|
||||||
|
p->drawText(*rect, Qt::TextSingleLine | Qt::AlignVCenter, text, &br);
|
||||||
|
rect->setLeft(br.right() + kWordPadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalSearchItemDelegate::paint(QPainter* p,
|
||||||
|
const QStyleOptionViewItem& option,
|
||||||
|
const QModelIndex& index) const {
|
||||||
|
const SearchProvider::Result result =
|
||||||
|
index.data(GlobalSearchWidget::Role_Result).value<SearchProvider::Result>();
|
||||||
|
const Song& m = result.metadata_;
|
||||||
|
|
||||||
|
widget_->LazyLoadArt(index);
|
||||||
|
|
||||||
|
QFont bold_font = option.font;
|
||||||
|
bold_font.setBold(true);
|
||||||
|
|
||||||
|
QColor pen = option.palette.color(QPalette::Text);
|
||||||
|
QColor light_pen = pen;
|
||||||
|
pen.setAlpha(200);
|
||||||
|
light_pen.setAlpha(128);
|
||||||
|
|
||||||
|
// Draw the background
|
||||||
|
const QStyleOptionViewItemV3* vopt = qstyleoption_cast<const QStyleOptionViewItemV3*>(&option);
|
||||||
|
const QWidget* widget = vopt->widget;
|
||||||
|
QStyle* style = widget->style() ? widget->style() : QApplication::style();
|
||||||
|
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, widget);
|
||||||
|
|
||||||
|
// Draw the album art. This will already be the correct size.
|
||||||
|
const QRect rect = option.rect;
|
||||||
|
const QRect art_rect(rect.left() + kMargin, rect.top(), kHeight, kHeight);
|
||||||
|
|
||||||
|
QPixmap art = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||||
|
if (art.isNull())
|
||||||
|
art = no_cover_;
|
||||||
|
|
||||||
|
p->drawPixmap(art_rect, art);
|
||||||
|
|
||||||
|
// Position text
|
||||||
|
QRect text_rect(art_rect.right() + kArtMargin, art_rect.top(),
|
||||||
|
rect.right() - art_rect.right() - kArtMargin, kHeight);
|
||||||
|
QRect text_rect_1(text_rect.adjusted(0, 0, 0, -kHeight/2));
|
||||||
|
QRect text_rect_2(text_rect.adjusted(0, kHeight/2, 0, 0));
|
||||||
|
|
||||||
|
// The text we draw depends on the type of result.
|
||||||
|
switch (result.type_) {
|
||||||
|
case SearchProvider::Result::Type_Track: {
|
||||||
|
// Line 1 is Title
|
||||||
|
p->setFont(bold_font);
|
||||||
|
|
||||||
|
// Title
|
||||||
|
p->setPen(pen);
|
||||||
|
DrawAndShrink(p, &text_rect_1, m.title());
|
||||||
|
|
||||||
|
// Line 2 is Artist - Album
|
||||||
|
p->setFont(option.font);
|
||||||
|
|
||||||
|
// Artist
|
||||||
|
p->setPen(pen);
|
||||||
|
if (!m.artist().isEmpty()) {
|
||||||
|
DrawAndShrink(p, &text_rect_2, m.artist());
|
||||||
|
} else if (!m.albumartist().isEmpty()) {
|
||||||
|
DrawAndShrink(p, &text_rect_2, m.albumartist());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m.album().isEmpty()) {
|
||||||
|
// Dash
|
||||||
|
p->setPen(light_pen);
|
||||||
|
DrawAndShrink(p, &text_rect_2, " - ");
|
||||||
|
|
||||||
|
// Album
|
||||||
|
p->setPen(pen);
|
||||||
|
DrawAndShrink(p, &text_rect_2, m.album());
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SearchProvider::Result::Type_Album: {
|
||||||
|
// Line 1 is Artist - Album
|
||||||
|
p->setFont(bold_font);
|
||||||
|
|
||||||
|
// Artist
|
||||||
|
p->setPen(pen);
|
||||||
|
if (!m.albumartist().isEmpty())
|
||||||
|
DrawAndShrink(p, &text_rect_1, m.albumartist());
|
||||||
|
else if (m.is_compilation())
|
||||||
|
DrawAndShrink(p, &text_rect_1, tr("Various Artists"));
|
||||||
|
else if (!m.artist().isEmpty())
|
||||||
|
DrawAndShrink(p, &text_rect_1, m.artist());
|
||||||
|
else
|
||||||
|
DrawAndShrink(p, &text_rect_1, tr("Unknown"));
|
||||||
|
|
||||||
|
// Dash
|
||||||
|
p->setPen(light_pen);
|
||||||
|
DrawAndShrink(p, &text_rect_1, " - ");
|
||||||
|
|
||||||
|
// Album
|
||||||
|
p->setPen(pen);
|
||||||
|
if (m.album().isEmpty())
|
||||||
|
DrawAndShrink(p, &text_rect_1, tr("Unknown"));
|
||||||
|
else
|
||||||
|
DrawAndShrink(p, &text_rect_1, m.album());
|
||||||
|
|
||||||
|
// Line 2 is <n> tracks
|
||||||
|
p->setFont(option.font);
|
||||||
|
|
||||||
|
p->setPen(pen);
|
||||||
|
DrawAndShrink(p, &text_rect_2, QString::number(result.album_size_));
|
||||||
|
|
||||||
|
p->setPen(light_pen);
|
||||||
|
DrawAndShrink(p, &text_rect_2, tr(result.album_size_ == 1 ? "track" : "tracks"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, 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 GLOBALSEARCHITEMDELEGATE_H
|
||||||
|
#define GLOBALSEARCHITEMDELEGATE_H
|
||||||
|
|
||||||
|
#include <QStyledItemDelegate>
|
||||||
|
|
||||||
|
class GlobalSearchWidget;
|
||||||
|
|
||||||
|
|
||||||
|
class GlobalSearchItemDelegate : public QStyledItemDelegate {
|
||||||
|
public:
|
||||||
|
GlobalSearchItemDelegate(GlobalSearchWidget* widget);
|
||||||
|
|
||||||
|
static const int kHeight;
|
||||||
|
static const int kMargin;
|
||||||
|
static const int kArtMargin;
|
||||||
|
static const int kWordPadding;
|
||||||
|
|
||||||
|
static QPixmap ScaleAndPad(const QImage& image);
|
||||||
|
|
||||||
|
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||||
|
void paint(QPainter* painter, const QStyleOptionViewItem& option,
|
||||||
|
const QModelIndex& index) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void DrawAndShrink(QPainter* p, QRect* rect, const QString& text) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GlobalSearchWidget* widget_;
|
||||||
|
QPixmap no_cover_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GLOBALSEARCHITEMDELEGATE_H
|
|
@ -0,0 +1,68 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, 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 "globalsearchwidget.h"
|
||||||
|
#include "globalsearchsortmodel.h"
|
||||||
|
#include "searchprovider.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
|
||||||
|
GlobalSearchSortModel::GlobalSearchSortModel(QObject* parent)
|
||||||
|
: QSortFilterProxyModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GlobalSearchSortModel::lessThan(const QModelIndex& left, const QModelIndex& right) const {
|
||||||
|
const SearchProvider::Result r1 = left.data(GlobalSearchWidget::Role_Result)
|
||||||
|
.value<SearchProvider::Result>();
|
||||||
|
const SearchProvider::Result r2 = right.data(GlobalSearchWidget::Role_Result)
|
||||||
|
.value<SearchProvider::Result>();
|
||||||
|
|
||||||
|
// Compare types first
|
||||||
|
if (r1.type_ < r2.type_) return true;
|
||||||
|
if (r1.type_ > r2.type_) return false;
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
switch (r1.type_) {
|
||||||
|
case SearchProvider::Result::Type_Track:
|
||||||
|
ret = QString::localeAwareCompare(r1.metadata_.title(), r2.metadata_.title());
|
||||||
|
if (ret < 0) return true;
|
||||||
|
if (ret > 0) return false;
|
||||||
|
|
||||||
|
ret = QString::localeAwareCompare(r1.metadata_.artist(), r2.metadata_.artist());
|
||||||
|
if (ret < 0) return true;
|
||||||
|
if (ret > 0) return false;
|
||||||
|
|
||||||
|
ret = QString::localeAwareCompare(r1.metadata_.album(), r2.metadata_.album());
|
||||||
|
if (ret < 0) return true;
|
||||||
|
if (ret > 0) return false;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SearchProvider::Result::Type_Album:
|
||||||
|
ret = QString::localeAwareCompare(r1.metadata_.artist(), r2.metadata_.artist());
|
||||||
|
if (ret < 0) return true;
|
||||||
|
if (ret > 0) return false;
|
||||||
|
|
||||||
|
ret = QString::localeAwareCompare(r1.metadata_.album(), r2.metadata_.album());
|
||||||
|
if (ret < 0) return true;
|
||||||
|
if (ret > 0) return false;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, 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 GLOBALSEARCHSORTMODEL_H
|
||||||
|
#define GLOBALSEARCHSORTMODEL_H
|
||||||
|
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
class GlobalSearchSortModel : public QSortFilterProxyModel {
|
||||||
|
public:
|
||||||
|
GlobalSearchSortModel(QObject* parent = 0);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool lessThan(const QModelIndex& left, const QModelIndex& right) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GLOBALSEARCHSORTMODEL_H
|
|
@ -16,6 +16,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "globalsearch.h"
|
#include "globalsearch.h"
|
||||||
|
#include "globalsearchitemdelegate.h"
|
||||||
|
#include "globalsearchsortmodel.h"
|
||||||
#include "globalsearchwidget.h"
|
#include "globalsearchwidget.h"
|
||||||
#include "librarysearchprovider.h"
|
#include "librarysearchprovider.h"
|
||||||
#include "ui_globalsearchwidget.h"
|
#include "ui_globalsearchwidget.h"
|
||||||
|
@ -25,177 +27,14 @@
|
||||||
|
|
||||||
#include <QListView>
|
#include <QListView>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
|
|
||||||
const int GlobalSearchItemDelegate::kHeight = SearchProvider::kArtHeight;
|
|
||||||
const int GlobalSearchItemDelegate::kMargin = 1;
|
|
||||||
const int GlobalSearchItemDelegate::kArtMargin = 6;
|
|
||||||
const int GlobalSearchItemDelegate::kWordPadding = 6;
|
|
||||||
const int GlobalSearchWidget::kMinVisibleItems = 3;
|
const int GlobalSearchWidget::kMinVisibleItems = 3;
|
||||||
const int GlobalSearchWidget::kMaxVisibleItems = 12;
|
const int GlobalSearchWidget::kMaxVisibleItems = 12;
|
||||||
|
|
||||||
|
|
||||||
GlobalSearchItemDelegate::GlobalSearchItemDelegate(GlobalSearchWidget* widget)
|
|
||||||
: QStyledItemDelegate(widget),
|
|
||||||
widget_(widget)
|
|
||||||
{
|
|
||||||
no_cover_ = ScaleAndPad(QImage(":nocover.png"));
|
|
||||||
}
|
|
||||||
|
|
||||||
QPixmap GlobalSearchItemDelegate::ScaleAndPad(const QImage& image) {
|
|
||||||
if (image.isNull())
|
|
||||||
return QPixmap();
|
|
||||||
|
|
||||||
if (image.size() == QSize(kHeight, kHeight))
|
|
||||||
return QPixmap::fromImage(image);
|
|
||||||
|
|
||||||
// Scale the image down
|
|
||||||
QImage copy;
|
|
||||||
copy = image.scaled(QSize(kHeight, kHeight),
|
|
||||||
Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
||||||
|
|
||||||
// Pad the image to kHeight x kHeight
|
|
||||||
QImage padded_image(kHeight, kHeight, QImage::Format_ARGB32);
|
|
||||||
padded_image.fill(0);
|
|
||||||
|
|
||||||
QPainter p(&padded_image);
|
|
||||||
p.drawImage((kHeight - copy.width()) / 2, (kHeight - copy.height()) / 2,
|
|
||||||
copy);
|
|
||||||
p.end();
|
|
||||||
|
|
||||||
return QPixmap::fromImage(padded_image);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize GlobalSearchItemDelegate::sizeHint(const QStyleOptionViewItem& option,
|
|
||||||
const QModelIndex& index) const {
|
|
||||||
QSize size = QStyledItemDelegate::sizeHint(option, index);
|
|
||||||
size.setHeight(kHeight + kMargin);
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GlobalSearchItemDelegate::DrawAndShrink(QPainter* p, QRect* rect,
|
|
||||||
const QString& text) const {
|
|
||||||
QRect br;
|
|
||||||
p->drawText(*rect, Qt::TextSingleLine | Qt::AlignVCenter, text, &br);
|
|
||||||
rect->setLeft(br.right() + kWordPadding);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GlobalSearchItemDelegate::paint(QPainter* p,
|
|
||||||
const QStyleOptionViewItem& option,
|
|
||||||
const QModelIndex& index) const {
|
|
||||||
const SearchProvider::Result result =
|
|
||||||
index.data(GlobalSearchWidget::Role_Result).value<SearchProvider::Result>();
|
|
||||||
const Song& m = result.metadata_;
|
|
||||||
|
|
||||||
widget_->LazyLoadArt(index);
|
|
||||||
|
|
||||||
QFont bold_font = option.font;
|
|
||||||
bold_font.setBold(true);
|
|
||||||
|
|
||||||
QColor pen = option.palette.color(QPalette::Text);
|
|
||||||
QColor light_pen = pen;
|
|
||||||
pen.setAlpha(200);
|
|
||||||
light_pen.setAlpha(128);
|
|
||||||
|
|
||||||
// Draw the background
|
|
||||||
const QStyleOptionViewItemV3* vopt = qstyleoption_cast<const QStyleOptionViewItemV3*>(&option);
|
|
||||||
const QWidget* widget = vopt->widget;
|
|
||||||
QStyle* style = widget->style() ? widget->style() : QApplication::style();
|
|
||||||
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, widget);
|
|
||||||
|
|
||||||
// Draw the album art. This will already be the correct size.
|
|
||||||
const QRect rect = option.rect;
|
|
||||||
const QRect art_rect(rect.left() + kMargin, rect.top(), kHeight, kHeight);
|
|
||||||
|
|
||||||
QPixmap art = index.data(Qt::DecorationRole).value<QPixmap>();
|
|
||||||
if (art.isNull())
|
|
||||||
art = no_cover_;
|
|
||||||
|
|
||||||
p->drawPixmap(art_rect, art);
|
|
||||||
|
|
||||||
// Position text
|
|
||||||
QRect text_rect(art_rect.right() + kArtMargin, art_rect.top(),
|
|
||||||
rect.right() - art_rect.right() - kArtMargin, kHeight);
|
|
||||||
QRect text_rect_1(text_rect.adjusted(0, 0, 0, -kHeight/2));
|
|
||||||
QRect text_rect_2(text_rect.adjusted(0, kHeight/2, 0, 0));
|
|
||||||
|
|
||||||
// The text we draw depends on the type of result.
|
|
||||||
switch (result.type_) {
|
|
||||||
case SearchProvider::Result::Type_Track: {
|
|
||||||
// Line 1 is Title
|
|
||||||
p->setFont(bold_font);
|
|
||||||
|
|
||||||
// Title
|
|
||||||
p->setPen(pen);
|
|
||||||
DrawAndShrink(p, &text_rect_1, m.title());
|
|
||||||
|
|
||||||
// Line 2 is Artist - Album
|
|
||||||
p->setFont(option.font);
|
|
||||||
|
|
||||||
// Artist
|
|
||||||
p->setPen(pen);
|
|
||||||
if (!m.artist().isEmpty()) {
|
|
||||||
DrawAndShrink(p, &text_rect_2, m.artist());
|
|
||||||
} else if (!m.albumartist().isEmpty()) {
|
|
||||||
DrawAndShrink(p, &text_rect_2, m.albumartist());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m.album().isEmpty()) {
|
|
||||||
// Dash
|
|
||||||
p->setPen(light_pen);
|
|
||||||
DrawAndShrink(p, &text_rect_2, " - ");
|
|
||||||
|
|
||||||
// Album
|
|
||||||
p->setPen(pen);
|
|
||||||
DrawAndShrink(p, &text_rect_2, m.album());
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SearchProvider::Result::Type_Album: {
|
|
||||||
// Line 1 is Artist - Album
|
|
||||||
p->setFont(bold_font);
|
|
||||||
|
|
||||||
// Artist
|
|
||||||
p->setPen(pen);
|
|
||||||
if (!m.albumartist().isEmpty())
|
|
||||||
DrawAndShrink(p, &text_rect_1, m.albumartist());
|
|
||||||
else if (m.is_compilation())
|
|
||||||
DrawAndShrink(p, &text_rect_1, tr("Various Artists"));
|
|
||||||
else if (!m.artist().isEmpty())
|
|
||||||
DrawAndShrink(p, &text_rect_1, m.artist());
|
|
||||||
else
|
|
||||||
DrawAndShrink(p, &text_rect_1, tr("Unknown"));
|
|
||||||
|
|
||||||
// Dash
|
|
||||||
p->setPen(light_pen);
|
|
||||||
DrawAndShrink(p, &text_rect_1, " - ");
|
|
||||||
|
|
||||||
// Album
|
|
||||||
p->setPen(pen);
|
|
||||||
if (m.album().isEmpty())
|
|
||||||
DrawAndShrink(p, &text_rect_1, tr("Unknown"));
|
|
||||||
else
|
|
||||||
DrawAndShrink(p, &text_rect_1, m.album());
|
|
||||||
|
|
||||||
// Line 2 is <n> tracks
|
|
||||||
p->setFont(option.font);
|
|
||||||
|
|
||||||
p->setPen(pen);
|
|
||||||
DrawAndShrink(p, &text_rect_2, QString::number(result.album_size_));
|
|
||||||
|
|
||||||
p->setPen(light_pen);
|
|
||||||
DrawAndShrink(p, &text_rect_2, tr(result.album_size_ == 1 ? "track" : "tracks"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GlobalSearchWidget::GlobalSearchWidget(QWidget* parent)
|
GlobalSearchWidget::GlobalSearchWidget(QWidget* parent)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
ui_(new Ui_GlobalSearchWidget),
|
ui_(new Ui_GlobalSearchWidget),
|
||||||
|
@ -203,18 +42,23 @@ GlobalSearchWidget::GlobalSearchWidget(QWidget* parent)
|
||||||
last_id_(0),
|
last_id_(0),
|
||||||
clear_model_on_next_result_(false),
|
clear_model_on_next_result_(false),
|
||||||
model_(new QStandardItemModel(this)),
|
model_(new QStandardItemModel(this)),
|
||||||
|
proxy_(new GlobalSearchSortModel(this)),
|
||||||
view_(new QListView),
|
view_(new QListView),
|
||||||
eat_focus_out_(false),
|
eat_focus_out_(false),
|
||||||
background_(":allthethings.png")
|
background_(":allthethings.png")
|
||||||
{
|
{
|
||||||
ui_->setupUi(this);
|
ui_->setupUi(this);
|
||||||
|
|
||||||
|
proxy_->setSourceModel(model_);
|
||||||
|
proxy_->setDynamicSortFilter(true);
|
||||||
|
proxy_->sort(0);
|
||||||
|
|
||||||
view_->setWindowFlags(Qt::Popup);
|
view_->setWindowFlags(Qt::Popup);
|
||||||
view_->setFocusPolicy(Qt::NoFocus);
|
view_->setFocusPolicy(Qt::NoFocus);
|
||||||
view_->setFocusProxy(ui_->search);
|
view_->setFocusProxy(ui_->search);
|
||||||
view_->installEventFilter(this);
|
view_->installEventFilter(this);
|
||||||
|
|
||||||
view_->setModel(model_);
|
view_->setModel(proxy_);
|
||||||
view_->setItemDelegate(new GlobalSearchItemDelegate(this));
|
view_->setItemDelegate(new GlobalSearchItemDelegate(this));
|
||||||
view_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
view_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
view_->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
view_->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
@ -222,6 +66,7 @@ GlobalSearchWidget::GlobalSearchWidget(QWidget* parent)
|
||||||
connect(ui_->search, SIGNAL(textEdited(QString)), SLOT(TextEdited(QString)));
|
connect(ui_->search, SIGNAL(textEdited(QString)), SLOT(TextEdited(QString)));
|
||||||
connect(engine_, SIGNAL(ResultsAvailable(int,SearchProvider::ResultList)),
|
connect(engine_, SIGNAL(ResultsAvailable(int,SearchProvider::ResultList)),
|
||||||
SLOT(AddResults(int,SearchProvider::ResultList)));
|
SLOT(AddResults(int,SearchProvider::ResultList)));
|
||||||
|
connect(engine_, SIGNAL(SearchFinished(int)), SLOT(SearchFinished(int)));
|
||||||
connect(engine_, SIGNAL(ArtLoaded(int,QImage)), SLOT(ArtLoaded(int,QImage)));
|
connect(engine_, SIGNAL(ArtLoaded(int,QImage)), SLOT(ArtLoaded(int,QImage)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,8 +122,33 @@ void GlobalSearchWidget::paintEvent(QPaintEvent* e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalSearchWidget::TextEdited(const QString& text) {
|
void GlobalSearchWidget::TextEdited(const QString& text) {
|
||||||
|
const QString trimmed_text = text.trimmed();
|
||||||
|
|
||||||
|
if (trimmed_text.length() < 3) {
|
||||||
|
Reset();
|
||||||
|
RepositionPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
clear_model_on_next_result_ = true;
|
clear_model_on_next_result_ = true;
|
||||||
last_id_ = engine_->SearchAsync(text);
|
last_id_ = engine_->SearchAsync(trimmed_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalSearchWidget::Reset() {
|
||||||
|
model_->clear();
|
||||||
|
art_requests_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalSearchWidget::SearchFinished(int id) {
|
||||||
|
if (id != last_id_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (clear_model_on_next_result_) {
|
||||||
|
Reset();
|
||||||
|
clear_model_on_next_result_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RepositionPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalSearchWidget::AddResults(int id, const SearchProvider::ResultList& results) {
|
void GlobalSearchWidget::AddResults(int id, const SearchProvider::ResultList& results) {
|
||||||
|
@ -286,8 +156,7 @@ void GlobalSearchWidget::AddResults(int id, const SearchProvider::ResultList& re
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (clear_model_on_next_result_) {
|
if (clear_model_on_next_result_) {
|
||||||
model_->clear();
|
Reset();
|
||||||
art_requests_.clear();
|
|
||||||
clear_model_on_next_result_ = false;
|
clear_model_on_next_result_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +219,7 @@ bool GlobalSearchWidget::eventFilter(QObject* o, QEvent* e) {
|
||||||
|
|
||||||
case Qt::Key_Up:
|
case Qt::Key_Up:
|
||||||
if (!cur_index.isValid()) {
|
if (!cur_index.isValid()) {
|
||||||
view_->setCurrentIndex(model_->index(model_->rowCount() - 1, 0));
|
view_->setCurrentIndex(proxy_->index(proxy_->rowCount() - 1, 0));
|
||||||
return true;
|
return true;
|
||||||
} else if (cur_index.row() == 0) {
|
} else if (cur_index.row() == 0) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -359,9 +228,9 @@ bool GlobalSearchWidget::eventFilter(QObject* o, QEvent* e) {
|
||||||
|
|
||||||
case Qt::Key_Down:
|
case Qt::Key_Down:
|
||||||
if (!cur_index.isValid()) {
|
if (!cur_index.isValid()) {
|
||||||
view_->setCurrentIndex(model_->index(0, 0));
|
view_->setCurrentIndex(proxy_->index(0, 0));
|
||||||
return true;
|
return true;
|
||||||
} else if (cur_index.row() == model_->rowCount() - 1) {
|
} else if (cur_index.row() == proxy_->rowCount() - 1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -430,18 +299,20 @@ bool GlobalSearchWidget::eventFilter(QObject* o, QEvent* e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalSearchWidget::LazyLoadArt(const QModelIndex& index) {
|
void GlobalSearchWidget::LazyLoadArt(const QModelIndex& proxy_index) {
|
||||||
if (!index.isValid() || index.data(Role_LazyLoadingArt).isValid()) {
|
if (!proxy_index.isValid() || proxy_index.data(Role_LazyLoadingArt).isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
model_->itemFromIndex(index)->setData(true, Role_LazyLoadingArt);
|
const QModelIndex source_index = proxy_->mapToSource(proxy_index);
|
||||||
|
|
||||||
|
model_->itemFromIndex(source_index)->setData(true, Role_LazyLoadingArt);
|
||||||
|
|
||||||
const SearchProvider::Result result =
|
const SearchProvider::Result result =
|
||||||
index.data(Role_Result).value<SearchProvider::Result>();
|
source_index.data(Role_Result).value<SearchProvider::Result>();
|
||||||
|
|
||||||
int id = engine_->LoadArtAsync(result);
|
int id = engine_->LoadArtAsync(result);
|
||||||
art_requests_[id] = index;
|
art_requests_[id] = source_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalSearchWidget::ArtLoaded(int id, const QImage& image) {
|
void GlobalSearchWidget::ArtLoaded(int id, const QImage& image) {
|
||||||
|
|
|
@ -20,42 +20,18 @@
|
||||||
|
|
||||||
#include "searchprovider.h"
|
#include "searchprovider.h"
|
||||||
|
|
||||||
#include <QStyledItemDelegate>
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
class GlobalSearch;
|
class GlobalSearch;
|
||||||
class GlobalSearchWidget;
|
|
||||||
class LibraryBackendInterface;
|
class LibraryBackendInterface;
|
||||||
class Ui_GlobalSearchWidget;
|
class Ui_GlobalSearchWidget;
|
||||||
|
|
||||||
class QListView;
|
class QListView;
|
||||||
|
class QModelIndex;
|
||||||
|
class QSortFilterProxyModel;
|
||||||
class QStandardItemModel;
|
class QStandardItemModel;
|
||||||
|
|
||||||
|
|
||||||
class GlobalSearchItemDelegate : public QStyledItemDelegate {
|
|
||||||
public:
|
|
||||||
GlobalSearchItemDelegate(GlobalSearchWidget* widget);
|
|
||||||
|
|
||||||
static const int kHeight;
|
|
||||||
static const int kMargin;
|
|
||||||
static const int kArtMargin;
|
|
||||||
static const int kWordPadding;
|
|
||||||
|
|
||||||
static QPixmap ScaleAndPad(const QImage& image);
|
|
||||||
|
|
||||||
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
|
||||||
void paint(QPainter* painter, const QStyleOptionViewItem& option,
|
|
||||||
const QModelIndex& index) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void DrawAndShrink(QPainter* p, QRect* rect, const QString& text) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
GlobalSearchWidget* widget_;
|
|
||||||
QPixmap no_cover_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class GlobalSearchWidget : public QWidget {
|
class GlobalSearchWidget : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -85,11 +61,13 @@ protected:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void TextEdited(const QString& text);
|
void TextEdited(const QString& text);
|
||||||
|
void SearchFinished(int id);
|
||||||
void AddResults(int id, const SearchProvider::ResultList& results);
|
void AddResults(int id, const SearchProvider::ResultList& results);
|
||||||
|
|
||||||
void ArtLoaded(int id, const QImage& image);
|
void ArtLoaded(int id, const QImage& image);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Reset();
|
||||||
void RepositionPopup();
|
void RepositionPopup();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -102,6 +80,7 @@ private:
|
||||||
QMap<int, QModelIndex> art_requests_;
|
QMap<int, QModelIndex> art_requests_;
|
||||||
|
|
||||||
QStandardItemModel* model_;
|
QStandardItemModel* model_;
|
||||||
|
QSortFilterProxyModel* proxy_;
|
||||||
QListView* view_;
|
QListView* view_;
|
||||||
bool eat_focus_out_;
|
bool eat_focus_out_;
|
||||||
|
|
||||||
|
|
|
@ -36,10 +36,10 @@ public:
|
||||||
Result(SearchProvider* provider = 0)
|
Result(SearchProvider* provider = 0)
|
||||||
: provider_(provider), album_size_(0) {}
|
: provider_(provider), album_size_(0) {}
|
||||||
|
|
||||||
|
// The order of types here is the order they'll appear in the UI.
|
||||||
enum Type {
|
enum Type {
|
||||||
Type_Track,
|
Type_Track = 0,
|
||||||
Type_Album,
|
Type_Album
|
||||||
Type_Stream
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SearchProvider* provider_;
|
SearchProvider* provider_;
|
||||||
|
|
Loading…
Reference in New Issue