From 127d3e4ef482892faec9b484240817ca0f7db1c8 Mon Sep 17 00:00:00 2001 From: Vitozz Date: Fri, 17 May 2024 14:59:45 +0300 Subject: [PATCH] Updated taskbar notifications mechanism. Added USE_TASKBARNOTIFIER CMake option enabled by default. Now on Linux when com.canonical.Unity sevrice is not available it just draws the incoming event count number on program icon. For non-Windows systems it uses the same behavior --- CMakeLists.txt | 1 + Readme-cmake-ru.txt | 7 ++ Readme-cmake.txt | 7 ++ src/CMakeLists.txt | 2 +- src/mainwin.cpp | 5 +- src/widgets/taskbarnotifier.cpp | 155 +++++++++++++++++++++----------- 6 files changed, 121 insertions(+), 56 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 588931b64..29f440f53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ option( BUNDLED_IRIS_ALL "Build bundled iris library with bundled QCA and bundle option( IRIS_BUNDLED_QCA "Adds: DTLS, Blake2b (needed with Qt5) and other useful for XMPP crypto-stuff" ${DEFAULT_BUNDLED_QCA} ) option( IRIS_BUNDLED_USRSCTP "Compile compatible usrsctp lib when system one is not available or uncompatible (required for p2p file transfer)" ${DEFAULT_BUNDLED_USRSCTP} ) option( BUNDLED_KEYCHAIN "Build QtKeychain library bundled" OFF ) +option( USE_TASKBARNOTIFIER "Use taskbar notificstions on incoming event" ON ) if (UNIX AND "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") option( ENABLE_ASAN "Enable compilation with address sanitizer" OFF ) diff --git a/Readme-cmake-ru.txt b/Readme-cmake-ru.txt index b95df6567..4f06d7ccc 100644 --- a/Readme-cmake-ru.txt +++ b/Readme-cmake-ru.txt @@ -193,6 +193,13 @@ отключает поддержку функций X11 которые могур приводить к падению программы (по-умолчанию OFF) +> -DUSE_TASKBARNOTIFIER=ON + + Показывает количество входящих событий на значке программы. + Для систем Linux используется служба DBus com.canonical.Unity, если она доступна. + Windows использует механизм оверлея значков. + Или просто меняет иконку программы в других случаях. (по-умолчанию ON) + ## Работа с плагинами: ### Следующие флаги работают только если включены флаги ENABLE_PLUGINS или ONLY_PLUGINS diff --git a/Readme-cmake.txt b/Readme-cmake.txt index a038c4613..9678c6fec 100644 --- a/Readme-cmake.txt +++ b/Readme-cmake.txt @@ -186,6 +186,13 @@ or Disable usage of X11 features which may crash program (default OFF) +> -DUSE_TASKBARNOTIFIER=ON + + Shows the incoming events count on the program icon. + For Linux systems, it uses the DBus service com.canonical.Unity if available. + Windows it uses an icon overlay mechanism. + Or simply changes the program icon for other cases. (default ON) + ## Work with plugins: ### Next flags are working only if ENABLE_PLUGINS or ONLY_PLUGINS are enabled diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2a9bdb0af..e41c94927 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -503,7 +503,7 @@ if(LINUX) endif() #TaskbarNotifier definition -if(WIN32 OR USE_DBUS) +if(USE_TASKBARNOTIFIER) target_compile_definitions(${PROJECT_NAME} PRIVATE USE_TASKBARNOTIFIER) endif() if(USE_CRASH) diff --git a/src/mainwin.cpp b/src/mainwin.cpp index 67914958e..29a96a49d 100644 --- a/src/mainwin.cpp +++ b/src/mainwin.cpp @@ -1645,7 +1645,7 @@ void MainWin::updateReadNext(PsiIcon *anim, int amount) d->eventNotifier->setText(""); d->eventNotifier->setPsiIcon(""); #ifdef USE_TASKBARNOTIFIER - if (d->taskBarNotifier->isActive()) + if (!d->asTool && d->taskBarNotifier->isActive()) d->taskBarNotifier->removeIconCountCaption(); #endif } else { @@ -1653,7 +1653,8 @@ void MainWin::updateReadNext(PsiIcon *anim, int amount) d->eventNotifier->setText(QString("") + numEventsString(d->nextAmount) + ""); d->eventNotifier->show(); #ifdef USE_TASKBARNOTIFIER - d->taskBarNotifier->setIconCountCaption(d->nextAmount); + if (!d->asTool) + d->taskBarNotifier->setIconCountCaption(d->nextAmount); #endif } diff --git a/src/widgets/taskbarnotifier.cpp b/src/widgets/taskbarnotifier.cpp index aca588ae5..5ef0ce3b6 100644 --- a/src/widgets/taskbarnotifier.cpp +++ b/src/widgets/taskbarnotifier.cpp @@ -19,6 +19,14 @@ #include "taskbarnotifier.h" +// #include //Maybe it will be needed for macOS to set application icon +#include +#include +#include +#include +#include +#include +#include #include #ifdef USE_DBUS @@ -28,15 +36,10 @@ #include #include #include -#include -#include #include #endif + #ifdef Q_OS_WIN -#include -#include -#include -#include #include #include @@ -57,20 +60,17 @@ static const QLatin1String ULAUNCHER_CMD("Update"); class TaskBarNotifier::Private { public: Private() = default; + ~Private(); bool active() const; void setIconCount(uint count = 0); void restoreDefaultIcon(); + void setParent(QWidget *parent); #ifdef USE_DBUS - ~Private() = default; void setDesktopPath(const QString &appName); -#elif defined(Q_OS_WIN) - ~Private(); - void setParentHWND(HWND hwnd); - void setDevicePixelRatio(int ratio); #endif - private: + QIcon setImageCountCaption(uint count = 0); #ifdef USE_DBUS bool checkDBusSeviceAvailable(); void sendDBusSignal(bool isVisible, uint number = 0); @@ -82,25 +82,42 @@ class TaskBarNotifier::Private { #endif private: - bool urgent_ = false; - bool active_ = false; + bool urgent_ = false; + bool active_ = false; + QWidget *parent_; + QImage *image_; + int devicePixelRatio_; #ifdef Q_OS_WIN HWND hwnd_; - int devicePixelRatio_; HICON icon_; #endif }; +TaskBarNotifier::Private::~Private() +{ +#ifdef Q_OS_WIN + if (icon_) + DestroyIcon(icon_); +#endif + if (image_) + delete image_; +} + bool TaskBarNotifier::Private::active() const { return active_; } void TaskBarNotifier::Private::setIconCount(uint count) { urgent_ = true; -#ifdef USE_DBUS - sendDBusSignal(true, count); -#elif defined(Q_OS_WIN) +#ifdef Q_OS_WIN setTaskBarIcon(makeIconCaption(QString::number(count))); doFlashTaskbarIcon(); +#else +#ifdef USE_DBUS + if (checkDBusSeviceAvailable()) + sendDBusSignal(true, count); + else +#endif + parent_->setWindowIcon(setImageCountCaption(count)); // qApp->setWindowIcon(setImageCountCaption(count)); #endif active_ = true; } @@ -108,15 +125,64 @@ void TaskBarNotifier::Private::setIconCount(uint count) void TaskBarNotifier::Private::restoreDefaultIcon() { urgent_ = false; -#ifdef USE_DBUS - sendDBusSignal(false, 0); -#elif defined(Q_OS_WIN) +#ifdef Q_OS_WIN setTaskBarIcon(); doFlashTaskbarIcon(); +#else +#ifdef USE_DBUS + if (checkDBusSeviceAvailable()) + sendDBusSignal(false, 0); + else +#endif + parent_->setWindowIcon( + QIcon(QPixmap::fromImage(*image_))); // qApp->setWindowIcon(QIcon(QPixmap::fromImage(*image_))); #endif active_ = false; } +void TaskBarNotifier::Private::setParent(QWidget *parent) +{ + parent_ = parent; + image_ = new QImage(parent->windowIcon().pixmap({ 128, 128 }).toImage()); + devicePixelRatio_ = parent->devicePixelRatio(); +#ifdef Q_OS_WIN + hwnd_ = reinterpret_cast(parent->winId())); +#endif +} + +QIcon TaskBarNotifier::Private::setImageCountCaption(uint count) +{ + auto imSize = image_->size() * devicePixelRatio_; + QImage img = *image_; + auto number = QString::number(count); + auto letters = number.length(); + auto text = (letters < 3) ? QStaticText(number) : QStaticText("∞"); + auto textDelta = (letters <= 2) ? 3 : 4; + + QPainter p(&img); + p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + auto font = QFont(parent_->font().defaultFamily(), imSize.height() / textDelta, QFont::Bold); + auto fm = QFontMetrics(font); + auto fh = fm.height(); + auto fw = fm.horizontalAdvance(text.text()); + auto radius = fh / 2; + + Qt::BrushStyle style = Qt::SolidPattern; + QBrush brush(Qt::black, style); + p.setBrush(brush); + p.setPen(QPen(Qt::NoPen)); + QRect rect(imSize.width() - fw - radius, radius / 4, fw + radius, fh); + p.drawRoundedRect(rect, radius, radius); + + p.setFont(font); + p.setPen(QPen(Qt::white)); + auto offset = rect.width() / ((letters + 1) * 2); + p.drawStaticText(rect.x() + offset, rect.y(), text); + + p.end(); + return QIcon(QPixmap::fromImage(img)); +} + #ifdef USE_DBUS bool TaskBarNotifier::Private::checkDBusSeviceAvailable() { @@ -130,35 +196,23 @@ bool TaskBarNotifier::Private::checkDBusSeviceAvailable() void TaskBarNotifier::Private::sendDBusSignal(bool isVisible, uint number) { - if (checkDBusSeviceAvailable()) { - auto appName = ApplicationInfo::desktopFileBaseName(); + auto appName = ApplicationInfo::desktopFileBaseName(); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - auto desktopPath_ = QLatin1String("application://%1").arg(appName); + auto desktopPath_ = QLatin1String("application://%1").arg(appName); #else - auto desktopPath_ = QLatin1String("application://%1.desktop").arg(appName); -#endif - QDBusMessage signal = QDBusMessage::createSignal(ULAUNCHER_PATH, ULAUNCHER_IFACE, ULAUNCHER_CMD); - signal << desktopPath_; - QVariantMap args; - args["count-visible"] = isVisible; - args["count"] = number; - args["urgent"] = urgent_; - signal << args; - QDBusConnection::sessionBus().send(signal); - } + auto desktopPath_ = QLatin1String("application://%1.desktop").arg(appName); +#endif + QDBusMessage signal = QDBusMessage::createSignal(ULAUNCHER_PATH, ULAUNCHER_IFACE, ULAUNCHER_CMD); + signal << desktopPath_; + QVariantMap args; + args["count-visible"] = isVisible; + args["count"] = number; + args["urgent"] = urgent_; + signal << args; + QDBusConnection::sessionBus().send(signal); } #elif defined(Q_OS_WIN) -TaskBarNotifier::Private::~Private() -{ - if (icon_) - DestroyIcon(icon_); -} - -void TaskBarNotifier::Private::setParentHWND(HWND hwnd) { hwnd_ = hwnd; } - -void TaskBarNotifier::Private::setDevicePixelRatio(int ratio) { devicePixelRatio_ = ratio; } - void TaskBarNotifier::Private::setTaskBarIcon(const HICON &icon) { if (icon_) @@ -186,7 +240,7 @@ HICON TaskBarNotifier::Private::makeIconCaption(const QString &number) const QPainter p(&img); p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); - auto font = QFont("Times", imSize.height() / textDelta, QFont::Bold); + auto font = QFont(parent_->font().defaultFamily(), imSize.height() / textDelta, QFont::Bold); auto fm = QFontMetrics(font); auto fh = fm.height(); auto radius = fh / 2; @@ -194,7 +248,7 @@ HICON TaskBarNotifier::Private::makeIconCaption(const QString &number) const Qt::BrushStyle style = Qt::SolidPattern; QBrush brush(Qt::black, style); p.setBrush(brush); - p.setPen(QPen(Qt::yellow)); + p.setPen(QPen(Qt::NoPen)); QRect rect(0, 0, imSize.width() - 2, imSize.height() - 2); p.drawRoundedRect(rect, radius, radius); @@ -233,12 +287,7 @@ void TaskBarNotifier::Private::doFlashTaskbarIcon() TaskBarNotifier::TaskBarNotifier(QWidget *parent) { d = std::make_unique(Private()); -#ifdef USE_DBUS - Q_UNUSED(parent) -#elif defined(Q_OS_WIN) - d->setParentHWND(reinterpret_cast(parent->winId())); - d->setDevicePixelRatio(parent->devicePixelRatio()); -#endif + d->setParent(parent); } TaskBarNotifier::~TaskBarNotifier() = default;