Have a state struct in Linux tray

This commit is contained in:
Ilya Fedin 2025-02-23 06:49:49 +04:00 committed by John Preston
parent 0d7175058b
commit e60d501e4a

View file

@ -44,66 +44,49 @@ public:
explicit IconGraphic(); explicit IconGraphic();
~IconGraphic(); ~IconGraphic();
[[nodiscard]] bool isRefreshNeeded( void updateState();
const QIcon &systemIcon, [[nodiscard]] bool isRefreshNeeded() const;
const QString &iconThemeName, [[nodiscard]] QIcon trayIcon();
int counter,
bool muted) const;
[[nodiscard]] QIcon systemIcon(
const QString &iconThemeName,
bool monochrome,
int counter,
bool muted) const;
[[nodiscard]] QIcon trayIcon(
const QIcon &systemIcon,
const QString &iconThemeName,
bool monochrome,
int counter,
bool muted);
private: private:
struct State {
QIcon systemIcon;
QString iconThemeName;
bool monochrome = false;
int32 counter = 0;
bool muted = false;
};
[[nodiscard]] QIcon systemIcon() const;
[[nodiscard]] int counterSlice(int counter) const; [[nodiscard]] int counterSlice(int counter) const;
void updateIconRegenerationNeeded(
const QIcon &icon,
const QIcon &systemIcon,
const QString &iconThemeName,
bool monochrome,
int counter,
bool muted);
[[nodiscard]] QSize dprSize(const QImage &image) const; [[nodiscard]] QSize dprSize(const QImage &image) const;
const int _iconSizes[7]; const int _iconSizes[7];
bool _muted = true;
int32 _count = 0;
base::flat_map<int, QImage> _imageBack; base::flat_map<int, QImage> _imageBack;
QIcon _trayIcon; QIcon _trayIcon;
QIcon _systemIcon; State _current;
QString _themeName; State _new;
bool _monochrome;
}; };
IconGraphic::IconGraphic() IconGraphic::IconGraphic()
: _iconSizes{ 16, 22, 32, 48, 64, 128, 256 } { : _iconSizes{ 16, 22, 32, 48, 64, 128, 256 } {
updateState();
} }
IconGraphic::~IconGraphic() = default; IconGraphic::~IconGraphic() = default;
QIcon IconGraphic::systemIcon( QIcon IconGraphic::systemIcon() const {
const QString &iconThemeName, if (_new.iconThemeName == _current.iconThemeName
bool monochrome, && _new.monochrome == _current.monochrome
int counter, && (_new.counter > 0) == (_current.counter > 0)
bool muted) const { && _new.muted == _current.muted) {
if (iconThemeName == _themeName return _current.systemIcon;
&& monochrome == _monochrome
&& (counter > 0) == (_count > 0)
&& muted == _muted) {
return _systemIcon;
} }
const auto candidates = { const auto candidates = {
monochrome ? PanelIconName(counter, muted) : QString(), _new.monochrome ? PanelIconName(_new.counter, _new.muted) : QString(),
base::IconName(), base::IconName(),
}; };
@ -120,80 +103,63 @@ QIcon IconGraphic::systemIcon(
return QIcon(); return QIcon();
} }
int IconGraphic::counterSlice(int counter) const { int IconGraphic::counterSlice(int counter) const {
return (counter >= 1000) return (counter >= 1000)
? (1000 + (counter % 100)) ? (1000 + (counter % 100))
: counter; : counter;
} }
bool IconGraphic::isRefreshNeeded(
const QIcon &systemIcon,
const QString &iconThemeName,
int counter,
bool muted) const {
return _trayIcon.isNull()
|| iconThemeName != _themeName
|| systemIcon.name() != _systemIcon.name()
|| (systemIcon.name() != PanelIconName(counter, muted)
? muted != _muted || counterSlice(counter) != _count
: false);
}
void IconGraphic::updateIconRegenerationNeeded(
const QIcon &icon,
const QIcon &systemIcon,
const QString &iconThemeName,
bool monochrome,
int counter,
bool muted) {
_trayIcon = icon;
_systemIcon = systemIcon;
_themeName = iconThemeName;
_monochrome = monochrome;
_count = counterSlice(counter);
_muted = muted;
}
QSize IconGraphic::dprSize(const QImage &image) const { QSize IconGraphic::dprSize(const QImage &image) const {
return image.size() / image.devicePixelRatio(); return image.size() / image.devicePixelRatio();
} }
QIcon IconGraphic::trayIcon( void IconGraphic::updateState() {
const QIcon &systemIcon, _new.iconThemeName = QIcon::themeName();
const QString &iconThemeName, _new.monochrome = Core::App().settings().trayIconMonochrome();
bool monochrome, _new.counter = Core::App().unreadBadge();
int counter, _new.muted = Core::App().unreadBadgeMuted();
bool muted) { _new.systemIcon = systemIcon();
if (!isRefreshNeeded(systemIcon, iconThemeName, counter, muted)) { }
bool IconGraphic::isRefreshNeeded() const {
return _trayIcon.isNull()
|| _new.iconThemeName != _current.iconThemeName
|| _new.systemIcon.name() != _current.systemIcon.name()
|| (_new.systemIcon.name() != PanelIconName(_new.counter, _new.muted)
? _new.muted != _current.muted
|| counterSlice(_new.counter) != counterSlice(
_current.counter)
: false);
}
QIcon IconGraphic::trayIcon() {
if (!isRefreshNeeded()) {
return _trayIcon; return _trayIcon;
} }
if (systemIcon.name() == PanelIconName(counter, muted)) { const auto guard = gsl::finally([&] {
updateIconRegenerationNeeded( _current = _new;
systemIcon, });
systemIcon,
iconThemeName,
monochrome,
counter,
muted);
return systemIcon; if (_new.systemIcon.name() == PanelIconName(
_new.counter,
_new.muted)) {
_trayIcon = _new.systemIcon;
return _trayIcon;
} }
QIcon result; QIcon result;
for (const auto iconSize : _iconSizes) { for (const auto iconSize : _iconSizes) {
auto &currentImageBack = _imageBack[iconSize]; auto &currentImageBack = _imageBack[iconSize];
const auto desiredSize = QSize(iconSize, iconSize); const auto desiredSize = QSize(iconSize, iconSize);
if (currentImageBack.isNull() if (currentImageBack.isNull()
|| iconThemeName != _themeName || _new.iconThemeName != _current.iconThemeName
|| systemIcon.name() != _systemIcon.name()) { || _new.systemIcon.name() != _current.systemIcon.name()) {
if (!systemIcon.isNull()) { if (!_new.systemIcon.isNull()) {
// We can't use QIcon::actualSize here // We can't use QIcon::actualSize here
// since it works incorrectly with svg icon themes // since it works incorrectly with svg icon themes
currentImageBack = systemIcon currentImageBack = _new.systemIcon
.pixmap(desiredSize) .pixmap(desiredSize)
.toImage(); .toImage();
@ -202,7 +168,8 @@ QIcon IconGraphic::trayIcon(
// if current icon theme is not a svg one, Qt can return // if current icon theme is not a svg one, Qt can return
// a pixmap that less in size even if there are a bigger one // a pixmap that less in size even if there are a bigger one
if (firstAttemptSize.width() < desiredSize.width()) { if (firstAttemptSize.width() < desiredSize.width()) {
const auto availableSizes = systemIcon.availableSizes(); const auto availableSizes
= _new.systemIcon.availableSizes();
const auto biggestSize = ranges::max_element( const auto biggestSize = ranges::max_element(
availableSizes, availableSizes,
@ -210,7 +177,7 @@ QIcon IconGraphic::trayIcon(
&QSize::width); &QSize::width);
if (biggestSize->width() > firstAttemptSize.width()) { if (biggestSize->width() > firstAttemptSize.width()) {
currentImageBack = systemIcon currentImageBack = _new.systemIcon
.pixmap(*biggestSize) .pixmap(*biggestSize)
.toImage(); .toImage();
} }
@ -227,24 +194,17 @@ QIcon IconGraphic::trayIcon(
} }
} }
result.addPixmap(Ui::PixmapFromImage(counter > 0 result.addPixmap(Ui::PixmapFromImage(_new.counter > 0
? Window::WithSmallCounter(std::move(currentImageBack), { ? Window::WithSmallCounter(std::move(currentImageBack), {
.size = iconSize, .size = iconSize,
.count = counter, .count = _new.counter,
.bg = muted ? st::trayCounterBgMute : st::trayCounterBg, .bg = _new.muted ? st::trayCounterBgMute : st::trayCounterBg,
.fg = st::trayCounterFg, .fg = st::trayCounterFg,
}) : std::move(currentImageBack))); }) : std::move(currentImageBack)));
} }
updateIconRegenerationNeeded( _trayIcon = result;
result, return _trayIcon;
systemIcon,
iconThemeName,
monochrome,
counter,
muted);
return result;
} }
class TrayEventFilter final : public QObject { class TrayEventFilter final : public QObject {
@ -321,22 +281,8 @@ void Tray::createIcon() {
}); });
}; };
const auto iconThemeName = QIcon::themeName();
const auto monochrome = Core::App().settings().trayIconMonochrome();
const auto counter = Core::App().unreadBadge();
const auto muted = Core::App().unreadBadgeMuted();
_icon = base::make_unique_q<QSystemTrayIcon>(nullptr); _icon = base::make_unique_q<QSystemTrayIcon>(nullptr);
_icon->setIcon(_iconGraphic->trayIcon( _icon->setIcon(_iconGraphic->trayIcon());
_iconGraphic->systemIcon(
iconThemeName,
monochrome,
counter,
muted),
iconThemeName,
monochrome,
counter,
muted));
_icon->setToolTip(AppName.utf16()); _icon->setToolTip(AppName.utf16());
using Reason = QSystemTrayIcon::ActivationReason; using Reason = QSystemTrayIcon::ActivationReason;
@ -375,27 +321,10 @@ void Tray::updateIcon() {
if (!_icon || !_iconGraphic) { if (!_icon || !_iconGraphic) {
return; return;
} }
const auto counter = Core::App().unreadBadge();
const auto muted = Core::App().unreadBadgeMuted();
const auto monochrome = Core::App().settings().trayIconMonochrome();
const auto iconThemeName = QIcon::themeName();
const auto systemIcon = _iconGraphic->systemIcon(
iconThemeName,
monochrome,
counter,
muted);
if (_iconGraphic->isRefreshNeeded( _iconGraphic->updateState();
systemIcon, if (_iconGraphic->isRefreshNeeded()) {
iconThemeName, _icon->setIcon(_iconGraphic->trayIcon());
counter,
muted)) {
_icon->setIcon(_iconGraphic->trayIcon(
systemIcon,
iconThemeName,
monochrome,
counter,
muted));
} }
} }