SystemTrayIcon: Respect device aspect ratio

Fixes #1782
This commit is contained in:
Jonas Kvinge
2025-08-31 02:34:13 +02:00
parent f628914173
commit 3c3480fb84
6 changed files with 103 additions and 37 deletions

View File

@@ -408,6 +408,8 @@ MainWindow::MainWindow(Application *app,
setWindowIcon(IconLoader::Load(u"strawberry"_s));
}
systemtrayicon_->SetDevicePixelRatioF(devicePixelRatioF());
QObject::connect(&*app->database(), &Database::Error, this, &MainWindow::ShowErrorDialog);
album_cover_choice_controller_->Init(app->network(), app->tagreader_client(), app->collection()->backend(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->streaming_services());

View File

@@ -2,7 +2,7 @@
*Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -47,6 +47,7 @@ class SystemTrayIcon : public QObject {
bool isVisible() const { return true; }
void setVisible(const bool) {}
void SetDevicePixelRatioF(const qreal device_pixel_ratio);
void SetTrayiconProgress(const bool enabled);
void SetupMenu(QAction *previous, QAction *play, QAction *stop, QAction *stop_after, QAction *next, QAction *mute, QAction *love, QAction *quit);
@@ -93,6 +94,7 @@ class SystemTrayIcon : public QObject {
QPixmap playing_icon_;
QPixmap paused_icon_;
QPixmap current_state_icon_;
qreal device_pixel_ratio_;
bool trayicon_progress_;
int song_progress_;
Q_DISABLE_COPY(SystemTrayIcon);

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -159,6 +159,7 @@ SystemTrayIcon::SystemTrayIcon(QObject *parent)
grey_icon_(QPixmap(u":/pictures/strawberry-grey.png"_s).scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)),
playing_icon_(u":/pictures/tiny-play.png"_s),
paused_icon_(u":/pictures/tiny-pause.png"_s),
device_pixel_ratio_(1.0),
trayicon_progress_(false),
song_progress_(0) {
@@ -168,6 +169,12 @@ SystemTrayIcon::SystemTrayIcon(QObject *parent)
SystemTrayIcon::~SystemTrayIcon() {}
void SystemTrayIcon::SetDevicePixelRatioF(const qreal device_pixel_ratio) {
device_pixel_ratio_ = device_pixel_ratio;
}
void SystemTrayIcon::SetTrayiconProgress(const bool enabled) {
trayicon_progress_ = enabled;

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -36,33 +36,30 @@
using namespace Qt::Literals::StringLiterals;
namespace {
constexpr int kSystemTrayIconSize = 48;
}
SystemTrayIcon::SystemTrayIcon(QObject *parent)
: QSystemTrayIcon(parent),
menu_(new QMenu),
pixmap_playing_(u":/pictures/tiny-play.png"_s),
pixmap_paused_(u":/pictures/tiny-pause.png"_s),
icon_normal_(IconLoader::Load(u"strawberry"_s)),
icon_grey_(IconLoader::Load(u"strawberry-grey"_s)),
icon_playing_(QIcon(u":/pictures/tiny-play.png"_s)),
icon_paused_(QIcon(u":/pictures/tiny-pause.png"_s)),
action_play_pause_(nullptr),
action_stop_(nullptr),
action_stop_after_this_track_(nullptr),
action_mute_(nullptr),
action_love_(nullptr),
available_(false),
device_pixel_ratio_(1.0),
trayicon_progress_(false),
song_progress_(0) {
const QIcon icon = IconLoader::Load(u"strawberry"_s);
const QIcon icon_grey = IconLoader::Load(u"strawberry-grey"_s);
pixmap_normal_ = icon.pixmap(48, QIcon::Normal);
if (icon_grey.isNull()) {
pixmap_grey_ = icon.pixmap(48, QIcon::Disabled);
}
else {
pixmap_grey_ = icon_grey.pixmap(48, QIcon::Disabled);
}
if (isSystemTrayAvailable()) {
available_ = true;
setIcon(icon);
setIcon(icon_normal_);
setToolTip(QCoreApplication::applicationName());
}
@@ -74,6 +71,41 @@ SystemTrayIcon::~SystemTrayIcon() {
delete menu_;
}
void SystemTrayIcon::InitPixmaps() {
if (pixmap_normal_.isNull() || pixmap_normal_.devicePixelRatioF() != device_pixel_ratio_) {
pixmap_normal_ = icon_normal_.pixmap(QSize(kSystemTrayIconSize, kSystemTrayIconSize), device_pixel_ratio_, QIcon::Normal);
}
if (pixmap_grey_.isNull() || pixmap_grey_.devicePixelRatioF() != device_pixel_ratio_) {
if (icon_grey_.isNull()) {
pixmap_grey_ = icon_normal_.pixmap(QSize(kSystemTrayIconSize, kSystemTrayIconSize), device_pixel_ratio_, QIcon::Disabled);
}
else {
pixmap_grey_ = icon_grey_.pixmap(QSize(kSystemTrayIconSize, kSystemTrayIconSize), device_pixel_ratio_, QIcon::Disabled);
}
}
if (pixmap_playing_.isNull() || pixmap_playing_.devicePixelRatioF() != device_pixel_ratio_) {
pixmap_playing_ = icon_playing_.pixmap(icon_playing_.availableSizes().at(0));
pixmap_playing_.setDevicePixelRatio(device_pixel_ratio_);
}
if (pixmap_paused_.isNull() || pixmap_paused_.devicePixelRatioF() != device_pixel_ratio_) {
pixmap_paused_ = icon_paused_.pixmap(icon_paused_.availableSizes().at(0));
pixmap_paused_.setDevicePixelRatio(device_pixel_ratio_);
}
}
void SystemTrayIcon::SetDevicePixelRatioF(const qreal device_pixel_ratio) {
device_pixel_ratio_ = device_pixel_ratio;
InitPixmaps();
}
void SystemTrayIcon::SetTrayiconProgress(const bool enabled) {
trayicon_progress_ = enabled;
@@ -131,13 +163,17 @@ void SystemTrayIcon::ShowPopup(const QString &summary, const QString &message, c
void SystemTrayIcon::UpdateIcon() {
if (available_) setIcon(CreateIcon(pixmap_normal_, pixmap_grey_));
if (available_) {
InitPixmaps();
setIcon(CreateIcon(pixmap_normal_, pixmap_grey_));
}
}
void SystemTrayIcon::SetPlaying(bool enable_play_pause) {
void SystemTrayIcon::SetPlaying(const bool enable_play_pause) {
current_state_icon_ = pixmap_playing_;
UpdateIcon();
action_stop_->setEnabled(true);

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -46,6 +46,9 @@ class SystemTrayIcon : public QSystemTrayIcon {
bool IsSystemTrayAvailable() const { return available_; }
void InitPixmaps();
void SetDevicePixelRatioF(const qreal device_pixel_ratio);
void SetTrayiconProgress(const bool enabled);
void SetupMenu(QAction *previous, QAction *play, QAction *stop, QAction *stop_after, QAction *next, QAction *mute, QAction *love, QAction *quit);
@@ -82,6 +85,12 @@ class SystemTrayIcon : public QSystemTrayIcon {
private:
QMenu *menu_;
QIcon icon_normal_;
QIcon icon_grey_;
QIcon icon_playing_;
QIcon icon_paused_;
QPixmap pixmap_normal_;
QPixmap pixmap_grey_;
QPixmap pixmap_playing_;
@@ -94,6 +103,7 @@ class SystemTrayIcon : public QSystemTrayIcon {
QAction *action_love_;
bool available_;
qreal device_pixel_ratio_;
bool trayicon_progress_;
int song_progress_;

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2019-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2019-2025, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,45 +35,54 @@
# include "qtsystemtrayicon.h"
#endif
QPixmap SystemTrayIcon::CreateIcon(const QPixmap &icon, const QPixmap &grey_icon) {
QPixmap SystemTrayIcon::CreateIcon(const QPixmap &pixmap_icon_normal, const QPixmap &pixmap_icon_grey) {
QRect rect(icon.rect());
const qreal dpr = pixmap_icon_normal.devicePixelRatio();
const QRect pixmap_rect = pixmap_icon_normal.rect();
QPixmap ret(icon);
QPainter p(&ret);
QPixmap pixmap_drawn(pixmap_icon_normal.size());
pixmap_drawn.setDevicePixelRatio(dpr);
pixmap_drawn.fill(Qt::transparent);
QPainter p(&pixmap_drawn);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
p.drawPixmap(0, 0, pixmap_icon_normal);
if (trayicon_progress_) {
// The angle of the line that's used to cover the icon.
// Centered on rect.topLeft()
double angle = static_cast<double>(100 - song_progress_) / 100.0 * M_PI_2;
double length = sqrt(pow(rect.width(), 2.0) + pow(rect.height(), 2.0));
const double angle = static_cast<double>(100 - song_progress_) / 100.0 * M_PI_2;
const double length = std::sqrt(std::pow(pixmap_rect.width(), 2.0) + std::pow(pixmap_rect.height(), 2.0));
QPolygon mask;
mask << rect.topLeft();
mask << rect.topLeft() + QPoint(static_cast<int>(length * sin(angle)), static_cast<int>(length * cos(angle)));
mask << pixmap_rect.topLeft();
mask << pixmap_rect.topLeft() + QPoint(static_cast<int>(length * std::sin(angle)), static_cast<int>(length * std::cos(angle)));
if (song_progress_ > 50) mask << rect.bottomRight();
if (song_progress_ > 50) {
mask << pixmap_rect.bottomRight();
}
mask << rect.topRight();
mask << rect.topLeft();
mask << pixmap_rect.topRight();
mask << pixmap_rect.topLeft();
// Draw the grey bit
p.setClipRegion(mask);
p.drawPixmap(0, 0, grey_icon);
p.drawPixmap(0, 0, pixmap_icon_grey);
p.setClipping(false);
}
// Draw the playing or paused icon in the top-right
if (!current_state_icon_.isNull()) {
int height = rect.height() / 2;
QPixmap scaled(current_state_icon_.scaledToHeight(height, Qt::SmoothTransformation));
const int height = pixmap_rect.height() / 2;
QPixmap current_state_scaled = current_state_icon_.scaledToHeight(height, Qt::SmoothTransformation);
current_state_scaled.setDevicePixelRatio(dpr);
QRect state_rect(rect.width() - scaled.width(), 0, scaled.width(), scaled.height());
p.drawPixmap(state_rect, scaled);
const QRect state_rect(static_cast<int>((pixmap_rect.width() - current_state_scaled.width()) / dpr), 0, static_cast<int>(current_state_scaled.width() / dpr), static_cast<int>(current_state_scaled.height() / dpr));
p.drawPixmap(state_rect, current_state_scaled);
}
p.end();
return ret;
return pixmap_drawn;
}