mirror of
https://github.com/clementine-player/Clementine
synced 2025-01-19 04:50:16 +01:00
Collapse all info sections by default except the first one of each type but add support for using user preferences (they don't get saved yet), add a fade in/out animation to collapsed section headers.
This commit is contained in:
parent
0fcba76195
commit
abbc908aaa
@ -58,4 +58,6 @@ void ArtistInfoView::ResultReady(int id, const SongInfoFetcher::Result& result)
|
||||
foreach (const CollapsibleInfoPane::Data& data, result.info_) {
|
||||
AddSection(new CollapsibleInfoPane(data, this));
|
||||
}
|
||||
|
||||
CollapseSections();
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <QApplication>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QStyleOption>
|
||||
|
||||
const int CollapsibleInfoHeader::kHeight = 20;
|
||||
@ -26,8 +27,10 @@ const int CollapsibleInfoHeader::kIconSize = 16;
|
||||
|
||||
CollapsibleInfoHeader::CollapsibleInfoHeader(QWidget* parent)
|
||||
: QWidget(parent),
|
||||
expanded_(true),
|
||||
hovering_(false)
|
||||
expanded_(false),
|
||||
hovering_(false),
|
||||
animation_(new QPropertyAnimation(this, "opacity", this)),
|
||||
opacity_(0.0)
|
||||
{
|
||||
setMinimumHeight(kHeight);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
@ -56,17 +59,45 @@ void CollapsibleInfoHeader::SetExpanded(bool expanded) {
|
||||
|
||||
void CollapsibleInfoHeader::enterEvent(QEvent*) {
|
||||
hovering_ = true;
|
||||
update();
|
||||
if (!expanded_) {
|
||||
animation_->stop();
|
||||
animation_->setEndValue(1.0);
|
||||
animation_->setDuration(80);
|
||||
animation_->start();
|
||||
}
|
||||
}
|
||||
|
||||
void CollapsibleInfoHeader::leaveEvent(QEvent*) {
|
||||
hovering_ = false;
|
||||
if (!expanded_) {
|
||||
animation_->stop();
|
||||
animation_->setEndValue(0.0);
|
||||
animation_->setDuration(160);
|
||||
animation_->start();
|
||||
}
|
||||
}
|
||||
|
||||
void CollapsibleInfoHeader::set_opacity(float opacity) {
|
||||
opacity_ = opacity;
|
||||
update();
|
||||
}
|
||||
|
||||
void CollapsibleInfoHeader::paintEvent(QPaintEvent* e) {
|
||||
QPainter p(this);
|
||||
|
||||
QColor active_text_color(palette().color(QPalette::Active, QPalette::HighlightedText));
|
||||
QColor inactive_text_color(palette().color(QPalette::Active, QPalette::Text));
|
||||
QColor text_color;
|
||||
if (expanded_) {
|
||||
text_color = active_text_color;
|
||||
} else {
|
||||
p.setOpacity(0.4 + opacity_ * 0.6);
|
||||
text_color = QColor(
|
||||
active_text_color.red() * opacity_ + inactive_text_color.red() * (1.0 - opacity_),
|
||||
active_text_color.green() * opacity_ + inactive_text_color.green() * (1.0 - opacity_),
|
||||
active_text_color.blue() * opacity_ + inactive_text_color.blue() * (1.0 - opacity_));
|
||||
}
|
||||
|
||||
QRect indicator_rect(0, 0, height(), height());
|
||||
QRect icon_rect(height() + 2, (kHeight - kIconSize) / 2, kIconSize, kIconSize);
|
||||
QRect text_rect(rect());
|
||||
@ -112,7 +143,7 @@ void CollapsibleInfoHeader::paintEvent(QPaintEvent* e) {
|
||||
bold_font.setBold(true);
|
||||
p.setFont(bold_font);
|
||||
|
||||
p.setPen(palette().color(QPalette::Active, QPalette::HighlightedText));
|
||||
p.setPen(text_color);
|
||||
p.drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, title_);
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,11 @@
|
||||
#include <QIcon>
|
||||
#include <QWidget>
|
||||
|
||||
class QPropertyAnimation;
|
||||
|
||||
class CollapsibleInfoHeader : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float opacity READ opacity WRITE set_opacity);
|
||||
|
||||
public:
|
||||
CollapsibleInfoHeader(QWidget* parent = 0);
|
||||
@ -34,6 +37,9 @@ public:
|
||||
const QString& title() const { return title_; }
|
||||
const QIcon& icon() const { return icon_; }
|
||||
|
||||
float opacity() const { return opacity_; }
|
||||
void set_opacity(float opacity);
|
||||
|
||||
public slots:
|
||||
void SetExpanded(bool expanded);
|
||||
void SetTitle(const QString& title);
|
||||
@ -55,6 +61,9 @@ private:
|
||||
bool hovering_;
|
||||
QString title_;
|
||||
QIcon icon_;
|
||||
|
||||
QPropertyAnimation* animation_;
|
||||
float opacity_;
|
||||
};
|
||||
|
||||
#endif // COLLAPSIBLEINFOHEADER_H
|
||||
|
@ -32,6 +32,7 @@ CollapsibleInfoPane::CollapsibleInfoPane(const Data& data, QWidget* parent)
|
||||
|
||||
layout->addWidget(header_);
|
||||
layout->addWidget(data.contents_);
|
||||
data.contents_->hide();
|
||||
|
||||
header_->SetTitle(data.title_);
|
||||
header_->SetIcon(data.icon_);
|
||||
|
@ -41,6 +41,7 @@ public:
|
||||
TypeCount
|
||||
};
|
||||
|
||||
QString id_;
|
||||
QString title_;
|
||||
QIcon icon_;
|
||||
Type type_;
|
||||
|
@ -77,6 +77,7 @@ void EchoNestBiographies::RequestFinished() {
|
||||
already_seen.insert(canonical_site);
|
||||
|
||||
CollapsibleInfoPane::Data data;
|
||||
data.id_ = "echonest/bio/" + bio.site();
|
||||
data.title_ = tr("Biography from %1").arg(bio.site());
|
||||
data.type_ = CollapsibleInfoPane::Data::Type_Biography;
|
||||
|
||||
|
@ -51,6 +51,7 @@ void EchoNestSimilarArtists::RequestFinished() {
|
||||
|
||||
if (!artists.isEmpty()) {
|
||||
CollapsibleInfoPane::Data data;
|
||||
data.id_ = "echonest/similarartists";
|
||||
data.title_ = tr("Similar artists");
|
||||
data.type_ = CollapsibleInfoPane::Data::Type_Similar;
|
||||
data.icon_ = QIcon(":/providers/echonest.png");
|
||||
|
@ -53,6 +53,7 @@ void EchoNestTags::RequestFinished() {
|
||||
|
||||
if (!request->artist_->terms().isEmpty()) {
|
||||
CollapsibleInfoPane::Data data;
|
||||
data.id_ = "echonest/artisttags";
|
||||
data.title_ = tr("Artist tags");
|
||||
data.type_ = CollapsibleInfoPane::Data::Type_Tags;
|
||||
data.icon_ = QIcon(":/last.fm/icon_tag.png");
|
||||
|
@ -81,6 +81,7 @@ void LastfmTrackInfoProvider::GetPlayCounts(int id, const lastfm::XmlQuery& q) {
|
||||
return; // No useful data
|
||||
|
||||
CollapsibleInfoPane::Data data;
|
||||
data.id_ = "lastfm/playcounts";
|
||||
data.title_ = tr("Last.fm play counts");
|
||||
data.type_ = CollapsibleInfoPane::Data::Type_PlayCounts;
|
||||
data.icon_ = QIcon(":/last.fm/as.png");
|
||||
@ -113,6 +114,7 @@ void LastfmTrackInfoProvider::GetWiki(int id, const lastfm::XmlQuery& q) {
|
||||
return; // No useful data
|
||||
|
||||
CollapsibleInfoPane::Data data;
|
||||
data.id_ = "lastfm/songwiki";
|
||||
data.title_ = tr("Last.fm wiki");
|
||||
data.type_ = CollapsibleInfoPane::Data::Type_Biography;
|
||||
data.icon_ = QIcon(":/last.fm/as.png");
|
||||
@ -131,6 +133,7 @@ void LastfmTrackInfoProvider::GetTags(int id, const lastfm::XmlQuery& q) {
|
||||
return; // No tag elements
|
||||
|
||||
CollapsibleInfoPane::Data data;
|
||||
data.id_ = "lastfm/songtags";
|
||||
data.title_ = tr("Last.fm tags");
|
||||
data.type_ = CollapsibleInfoPane::Data::Type_Biography;
|
||||
data.icon_ = QIcon(":/last.fm/icon_tag.png");
|
||||
|
@ -18,10 +18,13 @@
|
||||
|
||||
#include <QFile>
|
||||
#include <QScrollArea>
|
||||
#include <QSettings>
|
||||
#include <QSpacerItem>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
const char* SongInfoBase::kSettingsGroup = "SongInfo";
|
||||
|
||||
SongInfoBase::SongInfoBase(NetworkAccessManager* network, QWidget* parent)
|
||||
: QWidget(parent),
|
||||
network_(network),
|
||||
@ -141,3 +144,42 @@ void SongInfoBase::ResultReady(int id, const SongInfoFetcher::Result& result) {
|
||||
delete data.contents_;
|
||||
}
|
||||
}
|
||||
|
||||
void SongInfoBase::CollapseSections() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
|
||||
// Sections are already sorted by type and relevance, so the algorithm we use
|
||||
// to determine which ones to show by default is:
|
||||
// * In the absense of any user preference, show the first (highest
|
||||
// relevance section of each type and hide the rest)
|
||||
// * If one or more sections in a type have been explicitly hidden/shown
|
||||
// by the user before then hide all sections in that type and show only
|
||||
// the ones that are explicitly shown. If there are multiple sections in
|
||||
// that type, but they are all hidden, then show the first one.
|
||||
|
||||
QMap<CollapsibleInfoPane::Data::Type, CollapsibleInfoPane*> types_;
|
||||
QSet<CollapsibleInfoPane::Data::Type> has_user_preference_;
|
||||
QSet<CollapsibleInfoPane::Data::Type> has_user_preference_on_;
|
||||
foreach (CollapsibleInfoPane* pane, sections_) {
|
||||
const CollapsibleInfoPane::Data::Type type = pane->data().type_;
|
||||
types_.insertMulti(type, pane);
|
||||
|
||||
QVariant preference = s.value(pane->data().id_);
|
||||
if (preference.isValid()) {
|
||||
has_user_preference_.insert(type);
|
||||
if (preference.toBool()) {
|
||||
has_user_preference_on_.insert(type);
|
||||
pane->Expand();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (CollapsibleInfoPane::Data::Type type, types_.keys()) {
|
||||
if (!has_user_preference_.contains(type) ||
|
||||
(!has_user_preference_on_.contains(type) && types_.values(type).count() > 1)) {
|
||||
// Expand the first one
|
||||
types_.values(type).last()->Expand();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ class SongInfoBase : public QWidget {
|
||||
public:
|
||||
SongInfoBase(NetworkAccessManager* network, QWidget* parent = 0);
|
||||
|
||||
static const char* kSettingsGroup;
|
||||
|
||||
public slots:
|
||||
void SongChanged(const Song& metadata);
|
||||
void SongFinished();
|
||||
@ -51,6 +53,7 @@ protected:
|
||||
void AddWidget(QWidget* widget);
|
||||
void AddSection(CollapsibleInfoPane* section);
|
||||
void Clear();
|
||||
void CollapseSections();
|
||||
|
||||
protected slots:
|
||||
virtual void ResultReady(int id, const SongInfoFetcher::Result& result);
|
||||
|
@ -78,6 +78,8 @@ void SongInfoView::ResultReady(int id, const SongInfoFetcher::Result& result) {
|
||||
foreach (const CollapsibleInfoPane::Data& data, result.info_) {
|
||||
AddSection(new CollapsibleInfoPane(data, this));
|
||||
}
|
||||
|
||||
CollapseSections();
|
||||
}
|
||||
|
||||
void SongInfoView::ReloadSettings() {
|
||||
|
@ -116,6 +116,7 @@ void UltimateLyricsProvider::LyricsFetched(quint64 id, QNetworkReply* reply) {
|
||||
|
||||
if (!lyrics.isEmpty()) {
|
||||
CollapsibleInfoPane::Data data;
|
||||
data.id_ = "ultimatelyrics/" + name_;
|
||||
data.title_ = tr("Lyrics from %1").arg(name_);
|
||||
data.type_ = CollapsibleInfoPane::Data::Type_Lyrics;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user