summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2017-04-21 12:26:44 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2017-04-27 09:30:41 +0000
commitaffb6700a4a9621e5c80ac1f733f891cb31be701 (patch)
tree234602f2b76b076985d2f8ea8ea607c50e823b75 /src/plugins
parent7849aa6e96aa923fca5523afc8cf88edcc0bcf90 (diff)
Windows QPA: Move system tray icon implementation into the plugin
The system tray icon implementation relied on QMenu. After the introduction of QPlatformSystemTrayIcon::contextMenuRequested(), the code can be moved into the plugin, making use of native menus when enabled or falling back to QMenu. This enables the SystemTrayIcon QML Type to work. [ChangeLog][QtGui][Windows] A native system tray icon is now available for SystemTrayIcon. Change-Id: I0fdbfb5cbb815c1ea6fb19305a9bceb9c5bcc034 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp1
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h1
-rw-r--r--src/plugins/platforms/windows/qwindowssystemtrayicon.cpp456
-rw-r--r--src/plugins/platforms/windows/qwindowssystemtrayicon.h103
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp10
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.h3
-rw-r--r--src/plugins/platforms/windows/windows.pri5
7 files changed, 579 insertions, 0 deletions
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index b6b51cd60a..56275f7028 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -94,6 +94,7 @@ Q_LOGGING_CATEGORY(lcQpaDialogs, "qt.qpa.dialogs")
Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus")
Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility")
+Q_LOGGING_CATEGORY(lcQpaTrayIcon, "qt.qpa.trayicon")
int QWindowsContext::verbose = 0;
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index 7f4a76af05..338c27e785 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -66,6 +66,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaDialogs)
Q_DECLARE_LOGGING_CATEGORY(lcQpaMenus)
Q_DECLARE_LOGGING_CATEGORY(lcQpaTablet)
Q_DECLARE_LOGGING_CATEGORY(lcQpaAccessibility)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaTrayIcon)
class QWindow;
class QPlatformScreen;
diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
new file mode 100644
index 0000000000..049989e9e6
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
@@ -0,0 +1,456 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if defined(WINVER) && WINVER < 0x0601
+# undef WINVER
+#endif
+#if !defined(WINVER)
+# define WINVER 0x0601 // required for NOTIFYICONDATA_V2_SIZE, ChangeWindowMessageFilterEx() (MinGW 5.3)
+#endif
+
+#if defined(NTDDI_VERSION) && NTDDI_VERSION < 0x06010000
+# undef NTDDI_VERSION
+#endif
+#if !defined(NTDDI_VERSION)
+# define NTDDI_VERSION 0x06010000 // required for Shell_NotifyIconGetRect (MinGW 5.3)
+#endif
+
+#include "qwindowssystemtrayicon.h"
+#include "qwindowscontext.h"
+#include "qwindowstheme.h"
+#include "qwindowsmenu.h"
+#include "qwindowsscreen.h"
+
+#include <QtGui/qpixmap.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qsettings.h>
+
+#include <qt_windows.h>
+#include <commctrl.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <windowsx.h>
+
+QT_BEGIN_NAMESPACE
+
+static const UINT q_uNOTIFYICONID = 0;
+
+static uint MYWM_TASKBARCREATED = 0;
+#define MYWM_NOTIFYICON (WM_APP+101)
+
+Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &);
+
+// Copy QString data to a limited wchar_t array including \0.
+static inline void qStringToLimitedWCharArray(QString in, wchar_t *target, int maxLength)
+{
+ const int length = qMin(maxLength - 1, in.size());
+ if (length < in.size())
+ in.truncate(length);
+ in.toWCharArray(target);
+ target[length] = wchar_t(0);
+}
+
+static inline void initNotifyIconData(NOTIFYICONDATA &tnd)
+{
+ memset(&tnd, 0, sizeof(NOTIFYICONDATA));
+ tnd.cbSize = sizeof(NOTIFYICONDATA);
+ tnd.uVersion = NOTIFYICON_VERSION_4;
+}
+
+static void setIconContents(NOTIFYICONDATA &tnd, const QString &tip, HICON hIcon)
+{
+ tnd.uFlags |= NIF_MESSAGE | NIF_ICON | NIF_TIP;
+ tnd.uCallbackMessage = MYWM_NOTIFYICON;
+ tnd.hIcon = hIcon;
+ qStringToLimitedWCharArray(tip, tnd.szTip, sizeof(tnd.szTip) / sizeof(wchar_t));
+}
+
+// Match the HWND of the dummy window to the instances
+struct QWindowsHwndSystemTrayIconEntry
+{
+ HWND hwnd;
+ QWindowsSystemTrayIcon *trayIcon;
+};
+
+typedef QVector<QWindowsHwndSystemTrayIconEntry> HwndTrayIconEntries;
+
+Q_GLOBAL_STATIC(HwndTrayIconEntries, hwndTrayIconEntries)
+
+static int indexOfHwnd(HWND hwnd)
+{
+ const HwndTrayIconEntries *entries = hwndTrayIconEntries();
+ for (int i = 0, size = entries->size(); i < size; ++i) {
+ if (entries->at(i).hwnd == hwnd)
+ return i;
+ }
+ return -1;
+}
+
+extern "C" LRESULT QT_WIN_CALLBACK qWindowsTrayIconWndProc(HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam)
+{
+ if (message == MYWM_TASKBARCREATED || message == MYWM_NOTIFYICON
+ || message == WM_INITMENU || message == WM_INITMENUPOPUP
+ || message == WM_COMMAND) {
+ const int index = indexOfHwnd(hwnd);
+ if (index >= 0) {
+ MSG msg;
+ msg.hwnd = hwnd; // re-create MSG structure
+ msg.message = message; // time and pt fields ignored
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ msg.pt.x = GET_X_LPARAM(lParam);
+ msg.pt.y = GET_Y_LPARAM(lParam);
+ long result = 0;
+ if (hwndTrayIconEntries()->at(index).trayIcon->winEvent(msg, &result))
+ return result;
+ }
+ }
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+// Note: Message windows (HWND_MESSAGE) are not sufficient, they
+// will not receive the "TaskbarCreated" message.
+static inline HWND createTrayIconMessageWindow()
+{
+ QWindowsContext *ctx = QWindowsContext::instance();
+ if (!ctx)
+ return 0;
+ // Register window class in the platform plugin.
+ const QString className =
+ ctx->registerWindowClass(QStringLiteral("QTrayIconMessageWindowClass"),
+ qWindowsTrayIconWndProc);
+ const wchar_t windowName[] = L"QTrayIconMessageWindow";
+ return CreateWindowEx(0, reinterpret_cast<const wchar_t *>(className.utf16()),
+ windowName, WS_OVERLAPPED,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, (HINSTANCE)GetModuleHandle(0), NULL);
+}
+
+/*!
+ \class QWindowsSystemTrayIcon
+ \brief Windows native system tray icon
+
+ \internal
+ \ingroup qt-lighthouse-win
+*/
+
+QWindowsSystemTrayIcon::QWindowsSystemTrayIcon()
+{
+ // For restoring the tray icon after explorer crashes
+ if (!MYWM_TASKBARCREATED)
+ MYWM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated");
+ // Allow the WM_TASKBARCREATED message through the UIPI filter
+ ChangeWindowMessageFilterEx(m_hwnd, MYWM_TASKBARCREATED, MSGFLT_ALLOW, 0);
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this << "MYWM_TASKBARCREATED=" << MYWM_TASKBARCREATED;
+}
+
+QWindowsSystemTrayIcon::~QWindowsSystemTrayIcon()
+{
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this;
+ ensureCleanup();
+}
+
+void QWindowsSystemTrayIcon::init()
+{
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this;
+ ensureInstalled();
+}
+
+void QWindowsSystemTrayIcon::cleanup()
+{
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this;
+ ensureCleanup();
+}
+
+void QWindowsSystemTrayIcon::updateIcon(const QIcon &icon)
+{
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << '(' << icon << ')' << this;
+ if (icon.cacheKey() == m_icon.cacheKey())
+ return;
+ const HICON hIconToDestroy = createIcon(icon);
+ if (ensureInstalled())
+ sendTrayMessage(NIM_MODIFY);
+ if (hIconToDestroy)
+ DestroyIcon(hIconToDestroy);
+}
+
+void QWindowsSystemTrayIcon::updateToolTip(const QString &tooltip)
+{
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << '(' << tooltip << ')' << this;
+ if (m_toolTip == tooltip)
+ return;
+ m_toolTip = tooltip;
+ if (isInstalled())
+ sendTrayMessage(NIM_MODIFY);
+}
+
+QRect QWindowsSystemTrayIcon::geometry() const
+{
+ NOTIFYICONIDENTIFIER nid;
+ memset(&nid, 0, sizeof(nid));
+ nid.cbSize = sizeof(nid);
+ nid.hWnd = m_hwnd;
+ nid.uID = q_uNOTIFYICONID;
+ RECT rect;
+ const QRect result = SUCCEEDED(Shell_NotifyIconGetRect(&nid, &rect))
+ ? QRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top)
+ : QRect();
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this << "returns" << result;
+ return result;
+}
+
+void QWindowsSystemTrayIcon::showMessage(const QString &title, const QString &messageIn,
+ const QIcon &icon,
+ QPlatformSystemTrayIcon::MessageIcon iconType,
+ int msecsIn)
+{
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << '(' << title << messageIn << icon
+ << iconType << msecsIn << ')' << this;
+ if (!supportsMessages())
+ return;
+ // For empty messages, ensures that they show when only title is set
+ QString message = messageIn;
+ if (message.isEmpty() && !title.isEmpty())
+ message.append(QLatin1Char(' '));
+
+ NOTIFYICONDATA tnd;
+ initNotifyIconData(tnd);
+ qStringToLimitedWCharArray(message, tnd.szInfo, 256);
+ qStringToLimitedWCharArray(title, tnd.szInfoTitle, 64);
+
+ tnd.uID = q_uNOTIFYICONID;
+ tnd.dwInfoFlags = NIIF_USER;
+
+ QSize size(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
+ const QSize largeIcon(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
+ const QSize more = icon.actualSize(largeIcon);
+ if (more.height() > (largeIcon.height() * 3/4) || more.width() > (largeIcon.width() * 3/4)) {
+ tnd.dwInfoFlags |= NIIF_LARGE_ICON;
+ size = largeIcon;
+ }
+ QPixmap pm = icon.pixmap(size);
+ if (pm.isNull()) {
+ tnd.dwInfoFlags = NIIF_INFO;
+ } else {
+ if (pm.size() != size) {
+ qWarning("QSystemTrayIcon::showMessage: Wrong icon size (%dx%d), please add standard one: %dx%d",
+ pm.size().width(), pm.size().height(), size.width(), size.height());
+ pm = pm.scaled(size, Qt::IgnoreAspectRatio);
+ }
+ tnd.hBalloonIcon = qt_pixmapToWinHICON(pm);
+ }
+ tnd.hWnd = m_hwnd;
+ tnd.uTimeout = msecsIn <= 0 ? UINT(10000) : UINT(msecsIn); // 10s default
+ tnd.uFlags = NIF_INFO | NIF_SHOWTIP;
+
+ Shell_NotifyIcon(NIM_MODIFY, &tnd);
+}
+
+bool QWindowsSystemTrayIcon::supportsMessages() const
+{
+ bool result = true; // The key does typically not exist on Windows 10, default to true.
+ static const wchar_t regKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
+ HKEY handle;
+ if (RegOpenKeyEx(HKEY_CURRENT_USER, regKey, 0, KEY_READ, &handle) == ERROR_SUCCESS) {
+ DWORD type;
+ static const wchar_t subKey[] = L"EnableBalloonTips";
+ if (RegQueryValueEx(handle, subKey, 0, &type, 0, 0) == ERROR_SUCCESS && type == REG_DWORD) {
+ DWORD value;
+ DWORD size = sizeof(value);
+ if (RegQueryValueEx(handle, subKey, 0, 0, reinterpret_cast<unsigned char *>(&value), &size) == ERROR_SUCCESS)
+ result = value != 0;
+ }
+ RegCloseKey(handle);
+ }
+ return result;
+}
+
+QPlatformMenu *QWindowsSystemTrayIcon::createMenu() const
+{
+ if (QWindowsTheme::useNativeMenus() && m_menu.isNull())
+ m_menu = new QWindowsPopupMenu;
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this << "returns" << m_menu.data();
+ return m_menu.data();
+}
+
+// Delay-install until an Icon exists
+bool QWindowsSystemTrayIcon::ensureInstalled()
+{
+ if (isInstalled())
+ return true;
+ if (m_hIcon == nullptr)
+ return false;
+ m_hwnd = createTrayIconMessageWindow();
+ if (Q_UNLIKELY(m_hwnd == nullptr))
+ return false;
+ QWindowsHwndSystemTrayIconEntry entry{m_hwnd, this};
+ hwndTrayIconEntries()->append(entry);
+ sendTrayMessage(NIM_ADD);
+ return true;
+}
+
+void QWindowsSystemTrayIcon::ensureCleanup()
+{
+ if (isInstalled()) {
+ const int index = indexOfHwnd(m_hwnd);
+ if (index >= 0)
+ hwndTrayIconEntries()->removeAt(index);
+ sendTrayMessage(NIM_DELETE);
+ DestroyWindow(m_hwnd);
+ m_hwnd = nullptr;
+ }
+ if (m_hIcon != nullptr)
+ DestroyIcon(m_hIcon);
+ m_hIcon = nullptr;
+ m_menu = nullptr; // externally owned
+ m_toolTip.clear();
+}
+
+bool QWindowsSystemTrayIcon::sendTrayMessage(DWORD msg)
+{
+ NOTIFYICONDATA tnd;
+ initNotifyIconData(tnd);
+ tnd.uID = q_uNOTIFYICONID;
+ tnd.hWnd = m_hwnd;
+ tnd.uFlags = NIF_SHOWTIP;
+ if (msg == NIM_ADD || msg == NIM_MODIFY)
+ setIconContents(tnd, m_toolTip, m_hIcon);
+ if (!Shell_NotifyIcon(msg, &tnd))
+ return false;
+ return msg != NIM_ADD || Shell_NotifyIcon(NIM_SETVERSION, &tnd);
+}
+
+// Return the old icon to be freed after modifying the tray icon.
+HICON QWindowsSystemTrayIcon::createIcon(const QIcon &icon)
+{
+ const HICON oldIcon = m_hIcon;
+ m_hIcon = nullptr;
+ if (icon.isNull())
+ return oldIcon;
+ const QSize requestedSize(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
+ const QSize size = icon.actualSize(requestedSize);
+ const QPixmap pm = icon.pixmap(size);
+ if (!pm.isNull())
+ m_hIcon = qt_pixmapToWinHICON(pm);
+ return oldIcon;
+}
+
+bool QWindowsSystemTrayIcon::winEvent(const MSG &message, long *result)
+{
+ *result = 0;
+ switch (message.message) {
+ case MYWM_NOTIFYICON: {
+ Q_ASSERT(q_uNOTIFYICONID == HIWORD(message.lParam));
+ const int trayMessage = LOWORD(message.lParam);
+ switch (trayMessage) {
+ case NIN_SELECT:
+ case NIN_KEYSELECT:
+ if (m_ignoreNextMouseRelease)
+ m_ignoreNextMouseRelease = false;
+ else
+ emit activated(Trigger);
+ break;
+ case WM_LBUTTONDBLCLK:
+ m_ignoreNextMouseRelease = true; // Since DBLCLICK Generates a second mouse
+ emit activated(DoubleClick); // release we must ignore it
+ break;
+ case WM_CONTEXTMENU: {
+ const QPoint globalPos = QPoint(GET_X_LPARAM(message.wParam), GET_Y_LPARAM(message.wParam));
+ const QPlatformScreen *screen = QWindowsContext::instance()->screenManager().screenAtDp(globalPos);
+ emit contextMenuRequested(globalPos, screen);
+ emit activated(Context);
+ if (m_menu)
+ m_menu->trackPopupMenu(message.hwnd, globalPos.x(), globalPos.y());
+ }
+ break;
+ case NIN_BALLOONUSERCLICK:
+ emit messageClicked();
+ break;
+ case WM_MBUTTONUP:
+ emit activated(MiddleClick);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case WM_INITMENU:
+ case WM_INITMENUPOPUP:
+ QWindowsPopupMenu::notifyAboutToShow(reinterpret_cast<HMENU>(message.wParam));
+ break;
+ case WM_COMMAND:
+ QWindowsPopupMenu::notifyTriggered(LOWORD(message.wParam));
+ break;
+ default:
+ if (message.message == MYWM_TASKBARCREATED) // self-registered message id (tray crashed)
+ sendTrayMessage(NIM_ADD);
+ break;
+ }
+ return false;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+void QWindowsSystemTrayIcon::formatDebug(QDebug &d) const
+{
+ d << static_cast<const void *>(this) << ", \"" << m_toolTip
+ << "\", hwnd=" << m_hwnd << ", m_hIcon=" << m_hIcon << ", menu="
+ << m_menu.data();
+}
+
+QDebug operator<<(QDebug d, const QWindowsSystemTrayIcon *t)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d.noquote();
+ d << "QWindowsSystemTrayIcon(";
+ if (t)
+ t->formatDebug(d);
+ else
+ d << '0';
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.h b/src/plugins/platforms/windows/qwindowssystemtrayicon.h
new file mode 100644
index 0000000000..1f696180cd
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSSYSTEMTRAYICON_H
+#define QWINDOWSSYSTEMTRAYICON_H
+
+#include <QtGui/qicon.h>
+#include <QtGui/qpa/qplatformsystemtrayicon.h>
+
+#include <QtCore/qpointer.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+
+class QWindowsPopupMenu;
+
+class QWindowsSystemTrayIcon : public QPlatformSystemTrayIcon
+{
+public:
+ QWindowsSystemTrayIcon();
+ ~QWindowsSystemTrayIcon();
+
+ void init() override;
+ void cleanup() override;
+ void updateIcon(const QIcon &icon) override;
+ void updateToolTip(const QString &tooltip) override;
+ void updateMenu(QPlatformMenu *) override {}
+ QRect geometry() const override;
+ void showMessage(const QString &title, const QString &msg,
+ const QIcon &icon, MessageIcon iconType, int msecs) override;
+
+ bool isSystemTrayAvailable() const override { return true; }
+ bool supportsMessages() const override;
+
+ QPlatformMenu *createMenu() const override;
+
+ bool winEvent(const MSG &message, long *result);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
+private:
+ bool isInstalled() const { return m_hwnd != nullptr; }
+ bool ensureInstalled();
+ void ensureCleanup();
+ bool sendTrayMessage(DWORD msg);
+ HICON createIcon(const QIcon &icon);
+
+ QIcon m_icon;
+ QString m_toolTip;
+ HWND m_hwnd = nullptr;
+ HICON m_hIcon = nullptr;
+ mutable QPointer<QWindowsPopupMenu> m_menu;
+ bool m_ignoreNextMouseRelease = false;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const QWindowsSystemTrayIcon *);
+#endif // !QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSSYSTEMTRAYICON_H
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index 5fe7c7c071..3165835d2d 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -48,6 +48,9 @@
#include "qwindowsdialoghelpers.h"
#include "qwindowscontext.h"
#include "qwindowsintegration.h"
+#if QT_CONFIG(systemtrayicon)
+# include "qwindowssystemtrayicon.h"
+#endif
#include "qt_windows.h"
#include <commctrl.h>
#include <objbase.h>
@@ -549,6 +552,13 @@ QPlatformDialogHelper *QWindowsTheme::createPlatformDialogHelper(DialogType type
return QWindowsDialogs::createHelper(type);
}
+#if QT_CONFIG(systemtrayicon)
+QPlatformSystemTrayIcon *QWindowsTheme::createPlatformSystemTrayIcon() const
+{
+ return new QWindowsSystemTrayIcon;
+}
+#endif
+
void QWindowsTheme::windowsThemeChanged(QWindow * window)
{
refresh();
diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h
index 7ce3023111..237e8158fa 100644
--- a/src/plugins/platforms/windows/qwindowstheme.h
+++ b/src/plugins/platforms/windows/qwindowstheme.h
@@ -59,6 +59,9 @@ public:
bool usePlatformNativeDialog(DialogType type) const override;
QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override;
+#if QT_CONFIG(systemtrayicon)
+ QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
+#endif
QVariant themeHint(ThemeHint) const override;
const QPalette *palette(Palette type = SystemPalette) const override
{ return m_palettes[type]; }
diff --git a/src/plugins/platforms/windows/windows.pri b/src/plugins/platforms/windows/windows.pri
index a7166e90e4..26f9e3aefe 100644
--- a/src/plugins/platforms/windows/windows.pri
+++ b/src/plugins/platforms/windows/windows.pri
@@ -70,6 +70,11 @@ qtConfig(dynamicgl) {
HEADERS += $$PWD/qwindowseglcontext.h
}
+qtConfig(systemtrayicon) {
+ SOURCES += $$PWD/qwindowssystemtrayicon.cpp
+ HEADERS += $$PWD/qwindowssystemtrayicon.h
+}
+
qtConfig(vulkan) {
SOURCES += $$PWD/qwindowsvulkaninstance.cpp
HEADERS += $$PWD/qwindowsvulkaninstance.h