Auto-expand tree items and lazy load album cover art
This commit is contained in:
parent
9c36cfa199
commit
41fab25569
@ -138,6 +138,7 @@ set(SOURCES
|
|||||||
|
|
||||||
globalsearch/digitallyimportedsearchprovider.cpp
|
globalsearch/digitallyimportedsearchprovider.cpp
|
||||||
globalsearch/globalsearch.cpp
|
globalsearch/globalsearch.cpp
|
||||||
|
globalsearch/globalsearchitemdelegate.cpp
|
||||||
globalsearch/globalsearchsettingspage.cpp
|
globalsearch/globalsearchsettingspage.cpp
|
||||||
globalsearch/globalsearchsortmodel.cpp
|
globalsearch/globalsearchsortmodel.cpp
|
||||||
globalsearch/globalsearchview.cpp
|
globalsearch/globalsearchview.cpp
|
||||||
|
34
src/globalsearch/globalsearchitemdelegate.cpp
Normal file
34
src/globalsearch/globalsearchitemdelegate.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2012, 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 "globalsearchview.h"
|
||||||
|
|
||||||
|
GlobalSearchItemDelegate::GlobalSearchItemDelegate(GlobalSearchView* view)
|
||||||
|
: LibraryItemDelegate(view),
|
||||||
|
view_(view)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalSearchItemDelegate::paint(
|
||||||
|
QPainter* painter, const QStyleOptionViewItem& option,
|
||||||
|
const QModelIndex& index) const {
|
||||||
|
// Tell the view we painted this item so it can lazy load some art.
|
||||||
|
const_cast<GlobalSearchView*>(view_)->LazyLoadArt(index);
|
||||||
|
|
||||||
|
LibraryItemDelegate::paint(painter, option, index);
|
||||||
|
}
|
36
src/globalsearch/globalsearchitemdelegate.h
Normal file
36
src/globalsearch/globalsearchitemdelegate.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2012, 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 "library/libraryview.h"
|
||||||
|
|
||||||
|
class GlobalSearchView;
|
||||||
|
|
||||||
|
class GlobalSearchItemDelegate : public LibraryItemDelegate {
|
||||||
|
public:
|
||||||
|
GlobalSearchItemDelegate(GlobalSearchView* view);
|
||||||
|
|
||||||
|
void paint(QPainter* painter, const QStyleOptionViewItem& option,
|
||||||
|
const QModelIndex& index) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GlobalSearchView* view_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GLOBALSEARCHITEMDELEGATE_H
|
@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "globalsearch.h"
|
#include "globalsearch.h"
|
||||||
|
#include "globalsearchitemdelegate.h"
|
||||||
#include "globalsearchsortmodel.h"
|
#include "globalsearchsortmodel.h"
|
||||||
#include "globalsearchview.h"
|
#include "globalsearchview.h"
|
||||||
#include "searchprovider.h"
|
#include "searchprovider.h"
|
||||||
@ -24,7 +25,6 @@
|
|||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/mimedata.h"
|
#include "core/mimedata.h"
|
||||||
#include "library/librarymodel.h"
|
#include "library/librarymodel.h"
|
||||||
#include "library/libraryview.h"
|
|
||||||
|
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
#include <QStandardItem>
|
#include <QStandardItem>
|
||||||
@ -44,18 +44,24 @@ GlobalSearchView::GlobalSearchView(Application* app, QWidget* parent)
|
|||||||
front_proxy_(new GlobalSearchSortModel(this)),
|
front_proxy_(new GlobalSearchSortModel(this)),
|
||||||
back_proxy_(new GlobalSearchSortModel(this)),
|
back_proxy_(new GlobalSearchSortModel(this)),
|
||||||
current_proxy_(front_proxy_),
|
current_proxy_(front_proxy_),
|
||||||
swap_models_timer_(new QTimer(this))
|
swap_models_timer_(new QTimer(this)),
|
||||||
|
artist_icon_(":/icons/22x22/x-clementine-artist.png"),
|
||||||
|
album_icon_(":/icons/22x22/x-clementine-album.png")
|
||||||
{
|
{
|
||||||
ui_->setupUi(this);
|
ui_->setupUi(this);
|
||||||
|
|
||||||
connect(ui_->search, SIGNAL(textChanged(QString)), SLOT(TextEdited(QString)));
|
connect(ui_->search, SIGNAL(textChanged(QString)), SLOT(TextEdited(QString)));
|
||||||
|
|
||||||
ui_->results->setItemDelegate(new LibraryItemDelegate(this));
|
ui_->results->setItemDelegate(new GlobalSearchItemDelegate(this));
|
||||||
|
|
||||||
group_by_[0] = LibraryModel::GroupBy_Artist;
|
group_by_[0] = LibraryModel::GroupBy_Artist;
|
||||||
group_by_[1] = LibraryModel::GroupBy_Album;
|
group_by_[1] = LibraryModel::GroupBy_Album;
|
||||||
group_by_[2] = LibraryModel::GroupBy_None;
|
group_by_[2] = LibraryModel::GroupBy_None;
|
||||||
|
|
||||||
|
no_cover_icon_ = QPixmap(":nocover.png").scaled(
|
||||||
|
LibraryModel::kPrettyCoverSize, LibraryModel::kPrettyCoverSize,
|
||||||
|
Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
|
||||||
// Set up the sorting proxy model
|
// Set up the sorting proxy model
|
||||||
front_proxy_->setSourceModel(front_model_);
|
front_proxy_->setSourceModel(front_model_);
|
||||||
front_proxy_->setDynamicSortFilter(true);
|
front_proxy_->setDynamicSortFilter(true);
|
||||||
@ -164,6 +170,7 @@ QStandardItem* GlobalSearchView::BuildContainers(
|
|||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QIcon icon;
|
||||||
QString display_text;
|
QString display_text;
|
||||||
QString sort_text;
|
QString sort_text;
|
||||||
int year = 0;
|
int year = 0;
|
||||||
@ -172,12 +179,14 @@ QStandardItem* GlobalSearchView::BuildContainers(
|
|||||||
case LibraryModel::GroupBy_Artist:
|
case LibraryModel::GroupBy_Artist:
|
||||||
display_text = LibraryModel::TextOrUnknown(s.artist());
|
display_text = LibraryModel::TextOrUnknown(s.artist());
|
||||||
sort_text = LibraryModel::SortTextForArtist(s.artist());
|
sort_text = LibraryModel::SortTextForArtist(s.artist());
|
||||||
|
icon = artist_icon_;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LibraryModel::GroupBy_YearAlbum:
|
case LibraryModel::GroupBy_YearAlbum:
|
||||||
year = qMax(0, s.year());
|
year = qMax(0, s.year());
|
||||||
display_text = LibraryModel::PrettyYearAlbum(year, s.album());
|
display_text = LibraryModel::PrettyYearAlbum(year, s.album());
|
||||||
sort_text = LibraryModel::SortTextForYear(year) + s.album();
|
sort_text = LibraryModel::SortTextForYear(year) + s.album();
|
||||||
|
icon = album_icon_;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LibraryModel::GroupBy_Year:
|
case LibraryModel::GroupBy_Year:
|
||||||
@ -192,6 +201,7 @@ QStandardItem* GlobalSearchView::BuildContainers(
|
|||||||
case LibraryModel::GroupBy_AlbumArtist: if (display_text.isNull()) display_text = s.effective_albumartist();
|
case LibraryModel::GroupBy_AlbumArtist: if (display_text.isNull()) display_text = s.effective_albumartist();
|
||||||
display_text = LibraryModel::TextOrUnknown(display_text);
|
display_text = LibraryModel::TextOrUnknown(display_text);
|
||||||
sort_text = LibraryModel::SortTextForArtist(display_text);
|
sort_text = LibraryModel::SortTextForArtist(display_text);
|
||||||
|
icon = album_icon_;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LibraryModel::GroupBy_FileType:
|
case LibraryModel::GroupBy_FileType:
|
||||||
@ -207,7 +217,7 @@ QStandardItem* GlobalSearchView::BuildContainers(
|
|||||||
key->group_[level] = display_text;
|
key->group_[level] = display_text;
|
||||||
QStandardItem* container = containers_[*key];
|
QStandardItem* container = containers_[*key];
|
||||||
if (!container) {
|
if (!container) {
|
||||||
container = new QStandardItem(display_text);
|
container = new QStandardItem(icon, display_text);
|
||||||
container->setData(key->provider_index_, Role_ProviderIndex);
|
container->setData(key->provider_index_, Role_ProviderIndex);
|
||||||
container->setData(sort_text, LibraryModel::Role_SortText);
|
container->setData(sort_text, LibraryModel::Role_SortText);
|
||||||
container->setData(group_by_[level], LibraryModel::Role_ContainerType);
|
container->setData(group_by_[level], LibraryModel::Role_ContainerType);
|
||||||
@ -237,12 +247,30 @@ void GlobalSearchView::LazyLoadArt(const QModelIndex& proxy_index) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only load art for albums
|
||||||
|
const LibraryModel::GroupBy container_type = LibraryModel::GroupBy(
|
||||||
|
proxy_index.data(LibraryModel::Role_ContainerType).toInt());
|
||||||
|
if (container_type != LibraryModel::GroupBy_Album &&
|
||||||
|
container_type != LibraryModel::GroupBy_AlbumArtist &&
|
||||||
|
container_type != LibraryModel::GroupBy_YearAlbum) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the item as loading art
|
||||||
const QModelIndex source_index = front_proxy_->mapToSource(proxy_index);
|
const QModelIndex source_index = front_proxy_->mapToSource(proxy_index);
|
||||||
front_model_->itemFromIndex(source_index)->setData(true, Role_LazyLoadingArt);
|
QStandardItem* item = front_model_->itemFromIndex(source_index);
|
||||||
|
item->setData(true, Role_LazyLoadingArt);
|
||||||
|
|
||||||
|
// Walk down the item's children until we find a track
|
||||||
|
while (item->rowCount()) {
|
||||||
|
item = item->child(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the track's Result
|
||||||
const SearchProvider::Result result =
|
const SearchProvider::Result result =
|
||||||
source_index.data(Role_Result).value<SearchProvider::Result>();
|
item->data(Role_Result).value<SearchProvider::Result>();
|
||||||
|
|
||||||
|
// Load the art.
|
||||||
int id = engine_->LoadArtAsync(result);
|
int id = engine_->LoadArtAsync(result);
|
||||||
art_requests_[id] = source_index;
|
art_requests_[id] = source_index;
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,10 @@ private:
|
|||||||
|
|
||||||
QMap<int, QAction*> track_requests_;
|
QMap<int, QAction*> track_requests_;
|
||||||
QMap<int, QModelIndex> art_requests_;
|
QMap<int, QModelIndex> art_requests_;
|
||||||
|
|
||||||
|
QIcon artist_icon_;
|
||||||
|
QIcon album_icon_;
|
||||||
|
QPixmap no_cover_icon_;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline uint qHash(const GlobalSearchView::ContainerKey& key) {
|
inline uint qHash(const GlobalSearchView::ContainerKey& key) {
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTreeView" name="results">
|
<widget class="AutoExpandingTreeView" name="results">
|
||||||
<property name="editTriggers">
|
<property name="editTriggers">
|
||||||
<set>QAbstractItemView::NoEditTriggers</set>
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
</property>
|
</property>
|
||||||
@ -48,6 +48,11 @@
|
|||||||
<extends>QLineEdit</extends>
|
<extends>QLineEdit</extends>
|
||||||
<header>widgets/lineedit.h</header>
|
<header>widgets/lineedit.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>AutoExpandingTreeView</class>
|
||||||
|
<extends>QTreeView</extends>
|
||||||
|
<header>widgets/autoexpandingtreeview.h</header>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
@ -71,7 +71,6 @@ LibraryModel::LibraryModel(LibraryBackend* backend, Application* app,
|
|||||||
total_song_count_(0),
|
total_song_count_(0),
|
||||||
artist_icon_(":/icons/22x22/x-clementine-artist.png"),
|
artist_icon_(":/icons/22x22/x-clementine-artist.png"),
|
||||||
album_icon_(":/icons/22x22/x-clementine-album.png"),
|
album_icon_(":/icons/22x22/x-clementine-album.png"),
|
||||||
no_cover_icon_(":nocover.png"),
|
|
||||||
playlists_dir_icon_(IconLoader::Load("folder-sound")),
|
playlists_dir_icon_(IconLoader::Load("folder-sound")),
|
||||||
playlist_icon_(":/icons/22x22/x-clementine-albums.png"),
|
playlist_icon_(":/icons/22x22/x-clementine-albums.png"),
|
||||||
init_task_id_(-1),
|
init_task_id_(-1),
|
||||||
@ -92,7 +91,7 @@ LibraryModel::LibraryModel(LibraryBackend* backend, Application* app,
|
|||||||
SIGNAL(ImageLoaded(quint64,QImage)),
|
SIGNAL(ImageLoaded(quint64,QImage)),
|
||||||
SLOT(AlbumArtLoaded(quint64,QImage)));
|
SLOT(AlbumArtLoaded(quint64,QImage)));
|
||||||
|
|
||||||
no_cover_icon_pretty_ = QPixmap(":nocover.png").scaled(
|
no_cover_icon_ = QPixmap(":nocover.png").scaled(
|
||||||
kPrettyCoverSize, kPrettyCoverSize,
|
kPrettyCoverSize, kPrettyCoverSize,
|
||||||
Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
}
|
}
|
||||||
@ -405,7 +404,7 @@ QString LibraryModel::AlbumIconPixmapCacheKey(const QModelIndex& index) const {
|
|||||||
QVariant LibraryModel::AlbumIcon(const QModelIndex& index) {
|
QVariant LibraryModel::AlbumIcon(const QModelIndex& index) {
|
||||||
LibraryItem* item = IndexToItem(index);
|
LibraryItem* item = IndexToItem(index);
|
||||||
if (!item)
|
if (!item)
|
||||||
return no_cover_icon_pretty_;
|
return no_cover_icon_;
|
||||||
|
|
||||||
// Check the cache for a pixmap we already loaded.
|
// Check the cache for a pixmap we already loaded.
|
||||||
const QString cache_key = AlbumIconPixmapCacheKey(index);
|
const QString cache_key = AlbumIconPixmapCacheKey(index);
|
||||||
@ -416,7 +415,7 @@ QVariant LibraryModel::AlbumIcon(const QModelIndex& index) {
|
|||||||
|
|
||||||
// Maybe we're loading a pixmap already?
|
// Maybe we're loading a pixmap already?
|
||||||
if (pending_cache_keys_.contains(cache_key)) {
|
if (pending_cache_keys_.contains(cache_key)) {
|
||||||
return no_cover_icon_pretty_;
|
return no_cover_icon_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No art is cached and we're not loading it already. Load art for the first
|
// No art is cached and we're not loading it already. Load art for the first
|
||||||
@ -429,7 +428,7 @@ QVariant LibraryModel::AlbumIcon(const QModelIndex& index) {
|
|||||||
pending_cache_keys_.insert(cache_key);
|
pending_cache_keys_.insert(cache_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return no_cover_icon_pretty_;
|
return no_cover_icon_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibraryModel::AlbumArtLoaded(quint64 id, const QImage& image) {
|
void LibraryModel::AlbumArtLoaded(quint64 id, const QImage& image) {
|
||||||
@ -444,7 +443,7 @@ void LibraryModel::AlbumArtLoaded(quint64 id, const QImage& image) {
|
|||||||
// Insert this image in the cache.
|
// Insert this image in the cache.
|
||||||
if (image.isNull()) {
|
if (image.isNull()) {
|
||||||
// Set the no_cover image so we don't continually try to load art.
|
// Set the no_cover image so we don't continually try to load art.
|
||||||
QPixmapCache::insert(cache_key, no_cover_icon_pretty_);
|
QPixmapCache::insert(cache_key, no_cover_icon_);
|
||||||
} else {
|
} else {
|
||||||
QPixmapCache::insert(cache_key, QPixmap::fromImage(image));
|
QPixmapCache::insert(cache_key, QPixmap::fromImage(image));
|
||||||
}
|
}
|
||||||
|
@ -136,8 +136,9 @@ class LibraryModel : public SimpleTreeModel<LibraryItem> {
|
|||||||
|
|
||||||
// Whether or not to use album cover art, if it exists, in the library view
|
// Whether or not to use album cover art, if it exists, in the library view
|
||||||
void set_pretty_covers(bool use_pretty_covers);
|
void set_pretty_covers(bool use_pretty_covers);
|
||||||
|
bool use_pretty_covers() const { return use_pretty_covers_; }
|
||||||
|
|
||||||
//Whether or not to show letters heading in the library view
|
// Whether or not to show letters heading in the library view
|
||||||
void set_show_dividers(bool show_dividers);
|
void set_show_dividers(bool show_dividers);
|
||||||
|
|
||||||
// Utility functions for manipulating text
|
// Utility functions for manipulating text
|
||||||
@ -259,8 +260,7 @@ class LibraryModel : public SimpleTreeModel<LibraryItem> {
|
|||||||
QIcon album_icon_;
|
QIcon album_icon_;
|
||||||
// used as a generic icon to show when no cover art is found,
|
// used as a generic icon to show when no cover art is found,
|
||||||
// fixed to the same size as the artwork (32x32)
|
// fixed to the same size as the artwork (32x32)
|
||||||
QPixmap no_cover_icon_pretty_;
|
QPixmap no_cover_icon_;
|
||||||
QIcon no_cover_icon_;
|
|
||||||
QIcon playlists_dir_icon_;
|
QIcon playlists_dir_icon_;
|
||||||
QIcon playlist_icon_;
|
QIcon playlist_icon_;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user