Skip to content

Commit

Permalink
Updated taskbar notifications mechanism.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Vitozz committed May 17, 2024
1 parent f779b94 commit 127d3e4
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 56 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down
7 changes: 7 additions & 0 deletions Readme-cmake-ru.txt
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@
отключает поддержку функций X11 которые могур приводить к падению программы
(по-умолчанию OFF)

> -DUSE_TASKBARNOTIFIER=ON

Показывает количество входящих событий на значке программы.
Для систем Linux используется служба DBus com.canonical.Unity, если она доступна.
Windows использует механизм оверлея значков.
Или просто меняет иконку программы в других случаях. (по-умолчанию ON)

## Работа с плагинами:

### Следующие флаги работают только если включены флаги ENABLE_PLUGINS или ONLY_PLUGINS
Expand Down
7 changes: 7 additions & 0 deletions Readme-cmake.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions src/mainwin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1645,15 +1645,16 @@ 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 {
d->eventNotifier->setPsiIcon(anim);
d->eventNotifier->setText(QString("<b>") + numEventsString(d->nextAmount) + "</b>");
d->eventNotifier->show();
#ifdef USE_TASKBARNOTIFIER
d->taskBarNotifier->setIconCountCaption(d->nextAmount);
if (!d->asTool)
d->taskBarNotifier->setIconCountCaption(d->nextAmount);
#endif
}

Expand Down
155 changes: 102 additions & 53 deletions src/widgets/taskbarnotifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@

#include "taskbarnotifier.h"

// #include <QApplication> //Maybe it will be needed for macOS to set application icon
#include <QImage>
#include <QPaintDevice>
#include <QPainter>
#include <QPen>
#include <QStaticText>
#include <QString>
#include <QStringList>
#include <QWidget>

#ifdef USE_DBUS
Expand All @@ -28,15 +36,10 @@
#include <QDBusConnectionInterface>
#include <QDBusMessage>
#include <QLatin1String>
#include <QString>
#include <QStringList>
#include <QVariantMap>
#endif

#ifdef Q_OS_WIN
#include <QImage>
#include <QPaintDevice>
#include <QPainter>
#include <QPen>
#include <shobjidl.h>
#include <windows.h>

Expand All @@ -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);
Expand All @@ -82,41 +82,107 @@ 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;
}

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<HWND>(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()
{
Expand All @@ -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_)
Expand Down Expand Up @@ -186,15 +240,15 @@ 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;

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);

Expand Down Expand Up @@ -233,12 +287,7 @@ void TaskBarNotifier::Private::doFlashTaskbarIcon()
TaskBarNotifier::TaskBarNotifier(QWidget *parent)
{
d = std::make_unique<Private>(Private());
#ifdef USE_DBUS
Q_UNUSED(parent)
#elif defined(Q_OS_WIN)
d->setParentHWND(reinterpret_cast<HWND>(parent->winId()));
d->setDevicePixelRatio(parent->devicePixelRatio());
#endif
d->setParent(parent);
}

TaskBarNotifier::~TaskBarNotifier() = default;
Expand Down

0 comments on commit 127d3e4

Please sign in to comment.