4#include <QtCore/qt_windows.h>
12#include <QtGui/qguiapplication.h>
13#include <QtGui/qpixmap.h>
14#include <QtCore/qdebug.h>
15#include <QtCore/qlist.h>
16#include <QtCore/qrect.h>
17#include <QtCore/qsettings.h>
18#include <qpa/qwindowsysteminterface.h>
20#include <QtGui/private/qguiapplication_p.h>
29using namespace Qt::StringLiterals;
34#define MYWM_NOTIFYICON (WM_APP+101
)
36Q_GUI_EXPORT HICON qt_pixmapToWinHICON(
const QPixmap &);
41 const int length = qMin(maxLength - 1, in.size());
42 if (length < in.size())
44 in.toWCharArray(target);
45 target[length] =
wchar_t(0);
50 memset(&tnd, 0,
sizeof(NOTIFYICONDATA));
51 tnd.cbSize =
sizeof(NOTIFYICONDATA);
52 tnd.uVersion = NOTIFYICON_VERSION_4;
57 tnd.uFlags |= NIF_MESSAGE | NIF_ICON | NIF_TIP;
60 qStringToLimitedWCharArray(tip, tnd.szTip,
sizeof(tnd.szTip) /
sizeof(
wchar_t));
65 tnd.uFlags |= NIF_STATE;
66 tnd.dwStateMask = NIS_HIDDEN;
67 tnd.dwState = v ? 0 : NIS_HIDDEN;
77using HwndTrayIconEntries = QList<QWindowsHwndSystemTrayIconEntry>;
83 const HwndTrayIconEntries *entries = hwndTrayIconEntries();
84 for (
int i = 0, size = entries->size(); i < size; ++i) {
85 if (entries->at(i).hwnd == hwnd)
91LRESULT QT_WIN_CALLBACK qWindowsTrayIconWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
94 || message == WM_INITMENU || message == WM_INITMENUPOPUP
95 || message == WM_CLOSE || message == WM_COMMAND) {
96 const int index = indexOfHwnd(hwnd);
100 msg.message = message;
103 msg.pt.x = GET_X_LPARAM(lParam);
104 msg.pt.y = GET_Y_LPARAM(lParam);
106 if (hwndTrayIconEntries()->at(index).trayIcon->winEvent(msg, &result))
110 return DefWindowProc(hwnd, message, wParam, lParam);
121 const QString className =
122 ctx->registerWindowClass(QWindowsContext::classNamePrefix() +
"TrayIconMessageWindowClass"_L1,
123 qWindowsTrayIconWndProc);
124 const wchar_t windowName[] = L"QTrayIconMessageWindow";
125 return CreateWindowEx(0,
reinterpret_cast<
const wchar_t *>(className.utf16()),
126 windowName, WS_OVERLAPPED,
127 CW_USEDEFAULT, CW_USEDEFAULT,
128 CW_USEDEFAULT, CW_USEDEFAULT,
130 static_cast<HINSTANCE>(GetModuleHandle(
nullptr)),
nullptr);
134
135
136
137
138
146 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this;
152 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this;
154 if (!setIconVisible(m_visible))
160 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this;
167 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
'(' << icon <<
')' <<
this;
169 const HICON hIconToDestroy = createIcon(icon);
170 if (ensureInstalled())
171 sendTrayMessage(NIM_MODIFY);
173 DestroyIcon(hIconToDestroy);
178 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
'(' << tooltip <<
')' <<
this;
179 if (m_toolTip == tooltip)
183 sendTrayMessage(NIM_MODIFY);
188 if (!isIconVisible())
191 NOTIFYICONIDENTIFIER nid;
192 memset(&nid, 0,
sizeof(nid));
193 nid.cbSize =
sizeof(nid);
195 nid.uID = q_uNOTIFYICONID;
197 const QRect result = SUCCEEDED(Shell_NotifyIconGetRect(&nid, &rect))
198 ? QRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top)
200 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this <<
"returns" << result;
206 QPlatformSystemTrayIcon::MessageIcon iconType,
209 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
'(' << title << messageIn << icon
210 << iconType << msecsIn <<
')' <<
this;
214 QString message = messageIn;
215 if (message.isEmpty() && !title.isEmpty())
216 message.append(u' ');
219 initNotifyIconData(tnd);
220 qStringToLimitedWCharArray(message, tnd.szInfo, 256);
221 qStringToLimitedWCharArray(title, tnd.szInfoTitle, 64);
223 tnd.uID = q_uNOTIFYICONID;
225 const auto size = icon.actualSize(QSize(256, 256));
226 QPixmap pm = icon.pixmap(size);
227 if (m_hMessageIcon) {
228 DestroyIcon(m_hMessageIcon);
229 m_hMessageIcon =
nullptr;
232 tnd.dwInfoFlags = NIIF_INFO;
234 tnd.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;
235 m_hMessageIcon = qt_pixmapToWinHICON(pm);
236 tnd.hBalloonIcon = m_hMessageIcon;
239 tnd.uTimeout = msecsIn <= 0 ? UINT(10000) : UINT(msecsIn);
240 tnd.uFlags = NIF_INFO | NIF_SHOWTIP;
242 Shell_NotifyIcon(NIM_MODIFY, &tnd);
248 return QWindowsContext::readAdvancedExplorerSettings(L"EnableBalloonTips", 1) != 0;
253 if (QWindowsTheme::useNativeMenus() && m_menu.isNull())
254 m_menu =
new QWindowsPopupMenu;
255 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this <<
"returns" << m_menu.data();
256 return m_menu.data();
264 if (m_hIcon ==
nullptr)
266 m_hwnd = createTrayIconMessageWindow();
267 if (Q_UNLIKELY(m_hwnd ==
nullptr))
270 if (!MYWM_TASKBARCREATED)
273 ChangeWindowMessageFilterEx(m_hwnd, MYWM_TASKBARCREATED, MSGFLT_ALLOW,
nullptr);
274 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this <<
"MYWM_TASKBARCREATED=" << MYWM_TASKBARCREATED;
277 hwndTrayIconEntries()->append(entry);
278 sendTrayMessage(NIM_ADD);
285 const int index = indexOfHwnd(m_hwnd);
287 hwndTrayIconEntries()->removeAt(index);
288 sendTrayMessage(NIM_DELETE);
289 DestroyWindow(m_hwnd);
292 if (m_hIcon !=
nullptr)
293 DestroyIcon(m_hIcon);
294 if (m_hMessageIcon !=
nullptr)
295 DestroyIcon(m_hMessageIcon);
297 m_hMessageIcon =
nullptr;
307 initNotifyIconData(tnd);
308 tnd.uID = q_uNOTIFYICONID;
310 setIconVisibility(tnd, visible);
311 return Shell_NotifyIcon(NIM_MODIFY, &tnd) == TRUE;
316 NOTIFYICONIDENTIFIER nid;
317 memset(&nid, 0,
sizeof(nid));
318 nid.cbSize =
sizeof(nid);
320 nid.uID = q_uNOTIFYICONID;
322 const HRESULT hr = Shell_NotifyIconGetRect(&nid, &rect);
324 if (FAILED(hr) || hr == S_FALSE)
327 HMONITOR monitor = MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST);
329 info.cbSize =
sizeof(MONITORINFO);
330 GetMonitorInfo(monitor, &info);
334 return rect.bottom <= info.rcMonitor.bottom;
340 initNotifyIconData(tnd);
341 tnd.uID = q_uNOTIFYICONID;
343 tnd.uFlags = NIF_SHOWTIP;
344 if (msg != NIM_DELETE && !m_visible)
345 setIconVisibility(tnd, m_visible);
346 if (msg == NIM_ADD || msg == NIM_MODIFY)
347 setIconContents(tnd, m_toolTip, m_hIcon);
348 if (!Shell_NotifyIcon(msg, &tnd))
350 return msg != NIM_ADD || Shell_NotifyIcon(NIM_SETVERSION, &tnd);
356 const HICON oldIcon = m_hIcon;
360 const QSize requestedSize = QSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
361 const QSize size = icon.actualSize(requestedSize);
362 const QPixmap pm = icon.pixmap(size);
364 m_hIcon = qt_pixmapToWinHICON(pm);
371 switch (message.message) {
373 Q_ASSERT(q_uNOTIFYICONID == HIWORD(message.lParam));
374 const int trayMessage = LOWORD(message.lParam);
375 switch (trayMessage) {
378 if (m_ignoreNextMouseRelease)
379 m_ignoreNextMouseRelease =
false;
381 emit activated(Trigger);
383 case WM_LBUTTONDBLCLK:
384 m_ignoreNextMouseRelease =
true;
385 emit activated(DoubleClick);
387 case WM_CONTEXTMENU: {
391 const QPoint globalPos = QPoint(GET_X_LPARAM(message.wParam), GET_Y_LPARAM(message.wParam));
395 QGuiApplicationPrivate::lastCursorPosition = QCursor::pos().toPointF();
397 const QPlatformScreen *screen = screenManager
.screenAtDp(globalPos
);
399 screen = screenManager.screens().value(0);
401 emit contextMenuRequested(globalPos, screen);
402 emit activated(Context);
406 SetForegroundWindow(m_hwnd);
407 m_menu->trackPopupMenu(message.hwnd, globalPos.x(), globalPos.y());
412 case NIN_BALLOONUSERCLICK:
413 emit messageClicked();
416 emit activated(MiddleClick);
424 case WM_INITMENUPOPUP:
425 QWindowsPopupMenu::notifyAboutToShow(
reinterpret_cast<HMENU>(message.wParam));
428 QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>();
431 QWindowsPopupMenu::notifyTriggered(LOWORD(message.wParam));
434 if (message.message == MYWM_TASKBARCREATED) {
437 const QIcon oldIcon = m_icon;
441 sendTrayMessage(NIM_ADD);
448#ifndef QT_NO_DEBUG_STREAM
452 d <<
static_cast<
const void *>(
this) <<
", \"" << m_toolTip
453 <<
"\", hwnd=" << m_hwnd <<
", m_hIcon=" << m_hIcon <<
", menu="
459 QDebugStateSaver saver(d);
462 d <<
"QWindowsSystemTrayIcon(";
\inmodule QtCore\reentrant
Singleton container for all relevant information.
QWindowsScreenManager & screenManager()
static QWindowsContext * instance()
const QWindowsScreen * screenAtDp(const QPoint &p) const
Windows native system tray icon.
void cleanup() override
This method is called to cleanup the platform dependent implementation.
bool supportsMessages() const override
Returns true if the system tray supports messages on the platform.
bool winEvent(const MSG &message, long *result)
void formatDebug(QDebug &d) const
QPlatformMenu * createMenu() const override
This method allows platforms to use a different QPlatformMenu for system tray menus than what would n...
void updateToolTip(const QString &tooltip) override
This method is called when the tooltip text did change.
void init() override
This method is called to initialize the platform dependent implementation.
void updateIcon(const QIcon &icon) override
This method is called when the icon did change.
void showMessage(const QString &title, const QString &msg, const QIcon &icon, MessageIcon iconType, int msecs) override
Shows a balloon message for the entry with the given title, message msg and icon for the time specifi...
QRect geometry() const override
This method returns the geometry of the platform dependent system tray icon on the screen.
~QWindowsSystemTrayIcon() override
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)
static void setIconVisibility(NOTIFYICONDATA &tnd, bool v)
static HWND createTrayIconMessageWindow()
static const UINT q_uNOTIFYICONID
static void initNotifyIconData(NOTIFYICONDATA &tnd)
static void setIconContents(NOTIFYICONDATA &tnd, const QString &tip, HICON hIcon)
static uint MYWM_TASKBARCREATED
static void qStringToLimitedWCharArray(QString in, wchar_t *target, int maxLength)
static int indexOfHwnd(HWND hwnd)
QWindowsSystemTrayIcon * trayIcon