From a888a2886dc1466eb0b1bb3591f43350623c6330 Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Fri, 15 Jun 2018 00:59:15 +0200 Subject: [PATCH] qtui: Rework the attention state behavior of the tray icon Move the attention behavior handling into the SystemTray base class, which now determines the correct icon names to display based on the tray state and notification settings. Systray implementations now just react on signals from the base class to update their icons accordingly. Extend the systray notification settings to allow for a more fine- grained configuration. Users can now choose if the tray should be alerted at all, and if so, if the tray icon should change color or even blink instead of relying on the visualizer's default. This now works with both StatusNotifierItem and the legacy tray. Supersedes and closes GH-327. --- src/qtui/CMakeLists.txt | 3 +- src/qtui/knotificationbackend.cpp | 2 - src/qtui/legacysystemtray.cpp | 39 +------ src/qtui/legacysystemtray.h | 14 +-- src/qtui/statusnotifieritem.cpp | 18 +-- src/qtui/systemtray.cpp | 81 ++++++++++++-- src/qtui/systemtray.h | 25 ++++- .../systrayanimationnotificationbackend.cpp | 83 +++++++------- .../systrayanimationnotificationbackend.h | 32 +++--- src/qtui/ui/systrayanimationconfigwidget.ui | 105 ++++++++++++++++++ 10 files changed, 267 insertions(+), 135 deletions(-) create mode 100644 src/qtui/ui/systrayanimationconfigwidget.ui diff --git a/src/qtui/CMakeLists.txt b/src/qtui/CMakeLists.txt index d78f0f9b..0a5a3add 100644 --- a/src/qtui/CMakeLists.txt +++ b/src/qtui/CMakeLists.txt @@ -77,10 +77,11 @@ set(FORMS msgprocessorstatuswidget.ui nicklistwidget.ui passwordchangedlg.ui + receivefiledlg.ui settingsdlg.ui settingspagedlg.ui simplenetworkeditor.ui - receivefiledlg.ui + systrayanimationconfigwidget.ui topicwidget.ui ) diff --git a/src/qtui/knotificationbackend.cpp b/src/qtui/knotificationbackend.cpp index 4f35d644..c23a83b8 100644 --- a/src/qtui/knotificationbackend.cpp +++ b/src/qtui/knotificationbackend.cpp @@ -77,7 +77,6 @@ void KNotificationBackend::notify(const Notification &n) _notifications.append(qMakePair(n.notificationId, QPointer(notification))); updateToolTip(); - QtUi::mainWindow()->systemTray()->setAlert(true); } @@ -101,7 +100,6 @@ void KNotificationBackend::close(uint notificationId) { removeNotificationById(notificationId); //if(!_notifications.count()) // FIXME make configurable - QtUi::mainWindow()->systemTray()->setAlert(false); } diff --git a/src/qtui/legacysystemtray.cpp b/src/qtui/legacysystemtray.cpp index 7513bc81..0f1135a4 100644 --- a/src/qtui/legacysystemtray.cpp +++ b/src/qtui/legacysystemtray.cpp @@ -27,9 +27,7 @@ #include "qtui.h" LegacySystemTray::LegacySystemTray(QWidget *parent) - : SystemTray(parent), - _blinkState(false), - _lastMessageId(0) + : SystemTray(parent) { #ifndef HAVE_KDE4 _trayIcon = new QSystemTrayIcon(associatedWidget()); @@ -53,13 +51,9 @@ LegacySystemTray::LegacySystemTray(QWidget *parent) connect(this, SIGNAL(visibilityChanged(bool)), this, SLOT(onVisibilityChanged(bool))); connect(this, SIGNAL(modeChanged(Mode)), this, SLOT(onModeChanged(Mode))); - connect(this, SIGNAL(stateChanged(State)), this, SLOT(onStateChanged(State))); connect(this, SIGNAL(toolTipChanged(QString, QString)), SLOT(updateToolTip())); connect(this, SIGNAL(iconsChanged()), this, SLOT(updateIcon())); - - _blinkTimer.setInterval(750); - _blinkTimer.setSingleShot(false); - connect(&_blinkTimer, SIGNAL(timeout()), SLOT(onBlinkTimeout())); + connect(this, SIGNAL(currentIconNameChanged()), this, SLOT(updateIcon())); updateIcon(); updateToolTip(); @@ -93,28 +87,10 @@ void LegacySystemTray::onModeChanged(Mode mode) } -void LegacySystemTray::onStateChanged(State state) -{ - if (state == NeedsAttention && animationEnabled()) - _blinkTimer.start(); - else { - _blinkTimer.stop(); - _blinkState = false; - } - updateIcon(); -} - - void LegacySystemTray::updateIcon() { - QString icon; - if (state() == State::NeedsAttention && !_blinkState) { - icon = iconName(State::Active); - } - else { - icon = iconName(state()); - } - _trayIcon->setIcon(QIcon::fromTheme(icon, QIcon{QString{":/icons/hicolor/24x24/status/%1.svg"}.arg(icon)})); + QString iconName = (state() == NeedsAttention) ? currentAttentionIconName() : currentIconName(); + _trayIcon->setIcon(QIcon::fromTheme(iconName, QIcon{QString{":/icons/hicolor/24x24/status/%1.svg"}.arg(iconName)})); } @@ -134,13 +110,6 @@ void LegacySystemTray::updateToolTip() } -void LegacySystemTray::onBlinkTimeout() -{ - _blinkState = !_blinkState; - updateIcon(); -} - - void LegacySystemTray::onActivated(QSystemTrayIcon::ActivationReason reason) { activate((SystemTray::ActivationReason)reason); diff --git a/src/qtui/legacysystemtray.h b/src/qtui/legacysystemtray.h index 15134b18..e2bc3386 100644 --- a/src/qtui/legacysystemtray.h +++ b/src/qtui/legacysystemtray.h @@ -30,8 +30,6 @@ # include #endif -#include - #include "systemtray.h" class LegacySystemTray : public SystemTray @@ -49,10 +47,8 @@ public slots: private slots: void onModeChanged(Mode mode); - void onStateChanged(State state); void onVisibilityChanged(bool isVisible); - void onBlinkTimeout(); void onActivated(QSystemTrayIcon::ActivationReason); void onMessageClicked(); @@ -60,9 +56,7 @@ private slots: void updateToolTip(); private: - QTimer _blinkTimer; - bool _blinkState; - uint _lastMessageId; + uint _lastMessageId {0}; #ifdef HAVE_KDE4 KSystemTrayIcon *_trayIcon; @@ -71,10 +65,4 @@ private: #endif }; - -// inlines - - - - #endif /* QT_NO_SYSTEMTRAYICON */ diff --git a/src/qtui/statusnotifieritem.cpp b/src/qtui/statusnotifieritem.cpp index 68d81abb..5932746c 100644 --- a/src/qtui/statusnotifieritem.cpp +++ b/src/qtui/statusnotifieritem.cpp @@ -105,8 +105,9 @@ StatusNotifierItem::StatusNotifierItem(QWidget *parent) // Our own SNI service _statusNotifierItemDBus = new StatusNotifierItemDBus(this); + connect(this, SIGNAL(currentIconNameChanged()), _statusNotifierItemDBus, SIGNAL(NewIcon())); + connect(this, SIGNAL(currentIconNameChanged()), _statusNotifierItemDBus, SIGNAL(NewAttentionIcon())); connect(this, SIGNAL(toolTipChanged(QString, QString)), _statusNotifierItemDBus, SIGNAL(NewToolTip())); - connect(this, SIGNAL(animationEnabledChanged(bool)), _statusNotifierItemDBus, SIGNAL(NewAttentionIcon())); // Service watcher to keep track of the StatusNotifierWatcher service QDBusServiceWatcher *watcher = new QDBusServiceWatcher(kSniWatcherService, @@ -252,7 +253,6 @@ void StatusNotifierItem::onModeChanged(Mode mode) void StatusNotifierItem::onStateChanged(State state) { if (mode() == Mode::StatusNotifier) { - emit _statusNotifierItemDBus->NewIcon(); emit _statusNotifierItemDBus->NewStatus(metaObject()->enumerator(metaObject()->indexOfEnumerator("State")).valueToKey(state)); } } @@ -280,23 +280,13 @@ QString StatusNotifierItem::title() const QString StatusNotifierItem::iconName() const { - if (state() == Passive) { - return SystemTray::iconName(State::Passive); - } - else { - return SystemTray::iconName(State::Active); - } + return currentIconName(); } QString StatusNotifierItem::attentionIconName() const { - if (animationEnabled()) { - return SystemTray::iconName(State::NeedsAttention); - } - else { - return SystemTray::iconName(State::NeedsAttention); - } + return currentAttentionIconName(); } diff --git a/src/qtui/systemtray.cpp b/src/qtui/systemtray.cpp index 9d2078be..0e1e5c5d 100644 --- a/src/qtui/systemtray.cpp +++ b/src/qtui/systemtray.cpp @@ -40,7 +40,8 @@ SystemTray::SystemTray(QWidget *parent) { Q_ASSERT(parent); - NotificationSettings{}.initAndNotify("Systray/Animate", this, SLOT(enableAnimationChanged(QVariant)), true); + NotificationSettings{}.initAndNotify("Systray/ChangeColor", this, SLOT(enableChangeColorChanged(QVariant)), true); + NotificationSettings{}.initAndNotify("Systray/Animate", this, SLOT(enableBlinkChanged(QVariant)), false); UiStyleSettings{}.initAndNotify("Icons/InvertTray", this, SLOT(invertTrayIconChanged(QVariant)), false); ActionCollection *coll = QtUi::actionCollection("General"); @@ -69,6 +70,10 @@ SystemTray::SystemTray(QWidget *parent) connect(_trayMenu, SIGNAL(aboutToShow()), SLOT(trayMenuAboutToShow())); connect(QtUi::instance(), SIGNAL(iconThemeRefreshed()), this, SIGNAL(iconsChanged())); + + _blinkTimer.setInterval(1000); + _blinkTimer.setSingleShot(false); + connect(&_blinkTimer, SIGNAL(timeout()), SLOT(onBlinkTimeout())); } @@ -141,6 +146,16 @@ void SystemTray::setState(State state) if (_state != state) { _state = state; emit stateChanged(state); + + if (state == NeedsAttention && _attentionBehavior == AttentionBehavior::Blink) { + _blinkTimer.start(); + _blinkState = true; + } + else { + _blinkTimer.stop(); + _blinkState = false; + } + emit currentIconNameChanged(); } } @@ -168,6 +183,32 @@ QString SystemTray::iconName(State state) const } +QString SystemTray::currentIconName() const +{ + if (state() == State::NeedsAttention) { + if (_attentionBehavior == AttentionBehavior::ChangeColor) { + return iconName(State::NeedsAttention); + } + if (_attentionBehavior == AttentionBehavior::Blink && _blinkState) { + return iconName(State::NeedsAttention); + } + return iconName(State::Active); + } + else { + return iconName(state()); + } +} + + +QString SystemTray::currentAttentionIconName() const +{ + if (state() == State::NeedsAttention && _attentionBehavior == AttentionBehavior::Blink && !_blinkState) { + return iconName(State::Active); + } + return iconName(State::NeedsAttention); +} + + bool SystemTray::isAlerted() const { return state() == State::NeedsAttention; @@ -176,10 +217,19 @@ bool SystemTray::isAlerted() const void SystemTray::setAlert(bool alerted) { - if (alerted) + if (alerted) { setState(NeedsAttention); - else + } + else { setState(Client::isConnected() ? Active : Passive); + } +} + + +void SystemTray::onBlinkTimeout() +{ + _blinkState = !_blinkState; + emit currentIconNameChanged(); } @@ -198,16 +248,31 @@ void SystemTray::trayMenuAboutToShow() } -bool SystemTray::animationEnabled() const +void SystemTray::enableChangeColorChanged(const QVariant &v) { - return _animationEnabled; + if (v.toBool()) { + _attentionBehavior = AttentionBehavior::ChangeColor; + } + else { + if (_attentionBehavior == AttentionBehavior::ChangeColor) { + _attentionBehavior = AttentionBehavior::DoNothing; + } + } + emit currentIconNameChanged(); } -void SystemTray::enableAnimationChanged(const QVariant &v) +void SystemTray::enableBlinkChanged(const QVariant &v) { - _animationEnabled = v.toBool(); - emit animationEnabledChanged(v.toBool()); + if (v.toBool()) { + _attentionBehavior = AttentionBehavior::Blink; + } + else { + if (_attentionBehavior == AttentionBehavior::Blink) { + _attentionBehavior = AttentionBehavior::DoNothing; + } + } + emit currentIconNameChanged(); } diff --git a/src/qtui/systemtray.h b/src/qtui/systemtray.h index 733a6f09..c5fee199 100644 --- a/src/qtui/systemtray.h +++ b/src/qtui/systemtray.h @@ -22,6 +22,7 @@ #include #include +#include class Action; class QMenu; @@ -61,6 +62,12 @@ public: MiddleClick }; + enum class AttentionBehavior { + DoNothing, + ChangeColor, + Blink + }; + explicit SystemTray(QWidget *parent); ~SystemTray() override; @@ -87,7 +94,7 @@ signals: void stateChanged(State state); void visibilityChanged(bool isVisible); void iconsChanged(); - void animationEnabledChanged(bool); + void currentIconNameChanged(); void toolTipChanged(const QString &title, const QString &subtitle); void activated(SystemTray::ActivationReason); @@ -99,28 +106,36 @@ protected slots: protected: void setMode(Mode mode); - bool animationEnabled() const; QString toolTipTitle() const; QString toolTipSubTitle() const; QMenu *trayMenu() const; QString iconName(State state) const; + QString currentIconName() const; + QString currentAttentionIconName() const; private slots: void minimizeRestore(); void trayMenuAboutToShow(); - void enableAnimationChanged(const QVariant &); void invertTrayIconChanged(const QVariant &); + void enableChangeColorChanged(const QVariant &); + void enableBlinkChanged(const QVariant &); + + void onBlinkTimeout(); private: bool _isVisible{false}; Mode _mode{Mode::Invalid}; State _state{State::Passive}; - bool _animationEnabled{true}; bool _trayIconInverted{false}; + AttentionBehavior _attentionBehavior{AttentionBehavior::ChangeColor}; + + QTimer _blinkTimer; + bool _blinkState{false}; - QString _toolTipTitle, _toolTipSubTitle; + QString _toolTipTitle; + QString _toolTipSubTitle; QMenu *_trayMenu{nullptr}; QWidget *_associatedWidget{nullptr}; diff --git a/src/qtui/systrayanimationnotificationbackend.cpp b/src/qtui/systrayanimationnotificationbackend.cpp index 65cc38b5..4af91099 100644 --- a/src/qtui/systrayanimationnotificationbackend.cpp +++ b/src/qtui/systrayanimationnotificationbackend.cpp @@ -18,18 +18,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include -#include -#include #include -#include #include "systrayanimationnotificationbackend.h" -#include "client.h" #include "clientsettings.h" #include "mainwin.h" -#include "networkmodel.h" #include "qtui.h" #include "systemtray.h" @@ -37,7 +31,7 @@ SystrayAnimationNotificationBackend::SystrayAnimationNotificationBackend(QObject : AbstractNotificationBackend(parent) { NotificationSettings notificationSettings; - notificationSettings.initAndNotify("Systray/Animate", this, SLOT(animateChanged(QVariant)), true); + notificationSettings.initAndNotify("Systray/Alert", this, SLOT(alertChanged(QVariant)), true); } @@ -46,20 +40,21 @@ void SystrayAnimationNotificationBackend::notify(const Notification &n) if (n.type != Highlight && n.type != PrivMsg) return; - if (_animate) + if (_alert) QtUi::mainWindow()->systemTray()->setAlert(true); } void SystrayAnimationNotificationBackend::close(uint notificationId) { + Q_UNUSED(notificationId) QtUi::mainWindow()->systemTray()->setAlert(false); } -void SystrayAnimationNotificationBackend::animateChanged(const QVariant &v) +void SystrayAnimationNotificationBackend::alertChanged(const QVariant &v) { - _animate = v.toBool(); + _alert = v.toBool(); } @@ -71,49 +66,57 @@ SettingsPage *SystrayAnimationNotificationBackend::createConfigWidget() const /***************************************************************************/ -SystrayAnimationNotificationBackend::ConfigWidget::ConfigWidget(QWidget *parent) : SettingsPage("Internal", "SystrayNotification", parent) +SystrayAnimationNotificationBackend::ConfigWidget::ConfigWidget(QWidget *parent) : SettingsPage("Internal", "SystrayAnimation", parent) { - _animateBox = new QCheckBox(tr("Animate system tray icon")); - _animateBox->setIcon(QIcon::fromTheme("dialog-information")); - connect(_animateBox, SIGNAL(toggled(bool)), this, SLOT(widgetChanged())); - QHBoxLayout *layout = new QHBoxLayout(this); - layout->addWidget(_animateBox); -} + ui.setupUi(this); + ui.enableAlert->setIcon(QIcon::fromTheme("dialog-information")); + ui.attentionBehavior->setEnabled(ui.enableAlert->isChecked()); -void SystrayAnimationNotificationBackend::ConfigWidget::widgetChanged() -{ - bool changed = (_animate != _animateBox->isChecked()); - if (changed != hasChanged()) - setChangedState(changed); + initAutoWidgets(); } -bool SystrayAnimationNotificationBackend::ConfigWidget::hasDefaults() const +QString SystrayAnimationNotificationBackend::ConfigWidget::settingsKey() const { - return true; + return "Notification"; } -void SystrayAnimationNotificationBackend::ConfigWidget::defaults() +QVariant SystrayAnimationNotificationBackend::ConfigWidget::loadAutoWidgetValue(const QString &widgetName) { - _animateBox->setChecked(false); - widgetChanged(); + if (widgetName == "attentionBehavior") { + NotificationSettings s; + if (s.value("Systray/Animate", false).toBool()) { + return 2; + } + if (s.value("Systray/ChangeColor", true).toBool()) { + return 1; + } + return 0; + } + + return SettingsPage::loadAutoWidgetValue(widgetName); } -void SystrayAnimationNotificationBackend::ConfigWidget::load() +void SystrayAnimationNotificationBackend::ConfigWidget::saveAutoWidgetValue(const QString &widgetName, const QVariant &value) { - NotificationSettings s; - _animate = s.value("Systray/Animate", true).toBool(); - _animateBox->setChecked(_animate); - setChangedState(false); + if (widgetName == "attentionBehavior") { + NotificationSettings s; + s.setValue("Systray/ChangeColor", false); + s.setValue("Systray/Animate", false); + switch (value.toInt()) { + case 1: + s.setValue("Systray/ChangeColor", true); + return; + case 2: + s.setValue("Systray/Animate", true); + return; + default: + return; + } + } + + SettingsPage::saveAutoWidgetValue(widgetName, value); } - - -void SystrayAnimationNotificationBackend::ConfigWidget::save() -{ - NotificationSettings s; - s.setValue("Systray/Animate", _animateBox->isChecked()); - load(); -} \ No newline at end of file diff --git a/src/qtui/systrayanimationnotificationbackend.h b/src/qtui/systrayanimationnotificationbackend.h index cbe934d5..c9155e73 100644 --- a/src/qtui/systrayanimationnotificationbackend.h +++ b/src/qtui/systrayanimationnotificationbackend.h @@ -22,7 +22,8 @@ #include "abstractnotificationbackend.h" #include "settingspage.h" -#include "systemtray.h" + +#include "ui_systrayanimationconfigwidget.h" class QCheckBox; @@ -31,18 +32,18 @@ class SystrayAnimationNotificationBackend : public AbstractNotificationBackend Q_OBJECT public: - SystrayAnimationNotificationBackend(QObject *parent = 0); + SystrayAnimationNotificationBackend(QObject *parent = nullptr); - void notify(const Notification &); - void close(uint notificationId); - virtual SettingsPage *createConfigWidget() const; + void notify(const Notification &) override; + void close(uint notificationId) override; + virtual SettingsPage *createConfigWidget() const override; private slots: - void animateChanged(const QVariant &); + void alertChanged(const QVariant &); private: + bool _alert{false}; class ConfigWidget; - bool _animate; }; @@ -51,16 +52,13 @@ class SystrayAnimationNotificationBackend::ConfigWidget : public SettingsPage Q_OBJECT public: - ConfigWidget(QWidget *parent = 0); - void save(); - void load(); - bool hasDefaults() const; - void defaults(); + ConfigWidget(QWidget *parent = nullptr); + QString settingsKey() const override; -private slots: - void widgetChanged(); +private: + QVariant loadAutoWidgetValue(const QString &widgetName) override; + void saveAutoWidgetValue(const QString &widgetName, const QVariant &value) override; private: - QCheckBox *_animateBox; - bool _animate; -}; \ No newline at end of file + Ui::SystrayAnimationConfigWidget ui; +}; diff --git a/src/qtui/ui/systrayanimationconfigwidget.ui b/src/qtui/ui/systrayanimationconfigwidget.ui new file mode 100644 index 00000000..9736a47b --- /dev/null +++ b/src/qtui/ui/systrayanimationconfigwidget.ui @@ -0,0 +1,105 @@ + + + SystrayAnimationConfigWidget + + + + 0 + 0 + 438 + 50 + + + + + 0 + 0 + + + + Form + + + + + + + + <html><head/><body><p>If enabled, alert the system tray or dock in case of a notification.</p><p>It depends on your desktop environment how an alert is visualized. For example, Plasma will pulsate the tray icon, while Windows will change the icon's color. You may choose to forcefully change color or even letting the icon blink, if desired.</p><p>Note that not all icon themes support changing the color of the tray icon.</p></body></html> + + + Alert tray icon and + + + Systray/Alert + + + true + + + + + + + 1 + + + + + + 1 + + + + do nothing + + + + + change color + + + + + blink + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + enableAlert + toggled(bool) + attentionBehavior + setEnabled(bool) + + + 121 + 26 + + + 257 + 28 + + + + + -- 2.20.1