5#include <QtCore/qt_windows.h>
14#include <QtGui/qguiapplication.h>
15#include <QtGui/qpixmap.h>
16#include <QtCore/qdebug.h>
17#include <QtCore/qlist.h>
18#include <QtCore/qrect.h>
19#include <QtCore/qsettings.h>
20#include <qpa/qwindowsysteminterface.h>
22#include <QtGui/private/qguiapplication_p.h>
31using namespace Qt::StringLiterals;
36#define MYWM_NOTIFYICON (WM_APP+101
)
38Q_GUI_EXPORT HICON qt_pixmapToWinHICON(
const QPixmap &);
43 const int length = qMin(maxLength - 1, in.size());
44 if (length < in.size())
46 in.toWCharArray(target);
47 target[length] =
wchar_t(0);
52 memset(&tnd, 0,
sizeof(NOTIFYICONDATA));
53 tnd.cbSize =
sizeof(NOTIFYICONDATA);
54 tnd.uVersion = NOTIFYICON_VERSION_4;
59 tnd.uFlags |= NIF_MESSAGE | NIF_ICON | NIF_TIP;
62 qStringToLimitedWCharArray(tip, tnd.szTip,
sizeof(tnd.szTip) /
sizeof(
wchar_t));
67 tnd.uFlags |= NIF_STATE;
68 tnd.dwStateMask = NIS_HIDDEN;
69 tnd.dwState = v ? 0 : NIS_HIDDEN;
79using HwndTrayIconEntries = QList<QWindowsHwndSystemTrayIconEntry>;
85 const HwndTrayIconEntries *entries = hwndTrayIconEntries();
86 for (
int i = 0, size = entries->size(); i < size; ++i) {
87 if (entries->at(i).hwnd == hwnd)
93LRESULT QT_WIN_CALLBACK qWindowsTrayIconWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
96 || message == WM_INITMENU || message == WM_INITMENUPOPUP
97 || message == WM_CLOSE || message == WM_COMMAND) {
98 const int index = indexOfHwnd(hwnd);
102 msg.message = message;
105 msg.pt.x = GET_X_LPARAM(lParam);
106 msg.pt.y = GET_Y_LPARAM(lParam);
108 if (hwndTrayIconEntries()->at(index).trayIcon->winEvent(msg, &result))
112 return DefWindowProc(hwnd, message, wParam, lParam);
123 const QString className = ctx->registerWindowClass(
124 "TrayIconMessageWindowClass"_L1,
125 qWindowsTrayIconWndProc);
126 const wchar_t windowName[] = L"QTrayIconMessageWindow";
127 return CreateWindowEx(0,
reinterpret_cast<
const wchar_t *>(className.utf16()),
128 windowName, WS_OVERLAPPED,
129 CW_USEDEFAULT, CW_USEDEFAULT,
130 CW_USEDEFAULT, CW_USEDEFAULT,
132 static_cast<HINSTANCE>(GetModuleHandle(
nullptr)),
nullptr);
136
137
138
139
140
148 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this;
154 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this;
156 if (!setIconVisible(m_visible))
162 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this;
169 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
'(' << icon <<
')' <<
this;
171 const HICON hIconToDestroy = createIcon(icon);
172 if (ensureInstalled())
173 sendTrayMessage(NIM_MODIFY);
175 DestroyIcon(hIconToDestroy);
180 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
'(' << tooltip <<
')' <<
this;
181 if (m_toolTip == tooltip)
185 sendTrayMessage(NIM_MODIFY);
190 if (!isIconVisible())
193 NOTIFYICONIDENTIFIER nid;
194 memset(&nid, 0,
sizeof(nid));
195 nid.cbSize =
sizeof(nid);
197 nid.uID = q_uNOTIFYICONID;
199 const QRect result = SUCCEEDED(Shell_NotifyIconGetRect(&nid, &rect))
200 ? QRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top)
202 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this <<
"returns" << result;
208 QPlatformSystemTrayIcon::MessageIcon iconType,
211 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
'(' << title << messageIn << icon
212 << iconType << msecsIn <<
')' <<
this;
216 QString message = messageIn;
217 if (message.isEmpty() && !title.isEmpty())
218 message.append(u' ');
221 initNotifyIconData(tnd);
222 qStringToLimitedWCharArray(message, tnd.szInfo, 256);
223 qStringToLimitedWCharArray(title, tnd.szInfoTitle, 64);
225 tnd.uID = q_uNOTIFYICONID;
227 const auto size = icon.actualSize(QSize(256, 256));
228 QPixmap pm = icon.pixmap(size);
229 if (m_hMessageIcon) {
230 DestroyIcon(m_hMessageIcon);
231 m_hMessageIcon =
nullptr;
234 tnd.dwInfoFlags = NIIF_INFO;
236 tnd.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;
237 m_hMessageIcon = qt_pixmapToWinHICON(pm);
238 tnd.hBalloonIcon = m_hMessageIcon;
241 tnd.uTimeout = msecsIn <= 0 ? UINT(10000) : UINT(msecsIn);
242 tnd.uFlags = NIF_INFO | NIF_SHOWTIP;
244 Shell_NotifyIcon(NIM_MODIFY, &tnd);
250 return QWindowsContext::readAdvancedExplorerSettings(L"EnableBalloonTips", 1) != 0;
255 if (QWindowsTheme::useNativeMenus() && m_menu.isNull())
256 m_menu =
new QWindowsPopupMenu;
257 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this <<
"returns" << m_menu.data();
258 return m_menu.data();
266 if (m_hIcon ==
nullptr)
268 m_hwnd = createTrayIconMessageWindow();
269 if (Q_UNLIKELY(m_hwnd ==
nullptr))
272 if (!MYWM_TASKBARCREATED)
275 ChangeWindowMessageFilterEx(m_hwnd, MYWM_TASKBARCREATED, MSGFLT_ALLOW,
nullptr);
276 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this <<
"MYWM_TASKBARCREATED=" << MYWM_TASKBARCREATED;
279 hwndTrayIconEntries()->append(entry);
280 sendTrayMessage(NIM_ADD);
287 const int index = indexOfHwnd(m_hwnd);
289 hwndTrayIconEntries()->removeAt(index);
290 sendTrayMessage(NIM_DELETE);
291 DestroyWindow(m_hwnd);
294 if (m_hIcon !=
nullptr)
295 DestroyIcon(m_hIcon);
296 if (m_hMessageIcon !=
nullptr)
297 DestroyIcon(m_hMessageIcon);
299 m_hMessageIcon =
nullptr;
309 initNotifyIconData(tnd);
310 tnd.uID = q_uNOTIFYICONID;
312 setIconVisibility(tnd, visible);
313 return Shell_NotifyIcon(NIM_MODIFY, &tnd) == TRUE;
318 NOTIFYICONIDENTIFIER nid;
319 memset(&nid, 0,
sizeof(nid));
320 nid.cbSize =
sizeof(nid);
322 nid.uID = q_uNOTIFYICONID;
324 const HRESULT hr = Shell_NotifyIconGetRect(&nid, &rect);
326 if (FAILED(hr) || hr == S_FALSE)
329 HMONITOR monitor = MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST);
331 info.cbSize =
sizeof(MONITORINFO);
332 GetMonitorInfo(monitor, &info);
336 return rect.bottom <= info.rcMonitor.bottom;
342 initNotifyIconData(tnd);
343 tnd.uID = q_uNOTIFYICONID;
345 tnd.uFlags = NIF_SHOWTIP;
346 if (msg != NIM_DELETE && !m_visible)
347 setIconVisibility(tnd, m_visible);
348 if (msg == NIM_ADD || msg == NIM_MODIFY)
349 setIconContents(tnd, m_toolTip, m_hIcon);
350 if (!Shell_NotifyIcon(msg, &tnd))
352 return msg != NIM_ADD || Shell_NotifyIcon(NIM_SETVERSION, &tnd);
358 const HICON oldIcon = m_hIcon;
362 const QSize requestedSize = QSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
363 const QSize size = icon.actualSize(requestedSize);
364 const QPixmap pm = icon.pixmap(size);
366 m_hIcon = qt_pixmapToWinHICON(pm);
373 switch (message.message) {
375 Q_ASSERT(q_uNOTIFYICONID == HIWORD(message.lParam));
376 const int trayMessage = LOWORD(message.lParam);
377 switch (trayMessage) {
380 if (m_ignoreNextMouseRelease)
381 m_ignoreNextMouseRelease =
false;
383 emit activated(Trigger);
385 case WM_LBUTTONDBLCLK:
386 m_ignoreNextMouseRelease =
true;
387 emit activated(DoubleClick);
389 case WM_CONTEXTMENU: {
393 const QPoint globalPos = QPoint(GET_X_LPARAM(message.wParam), GET_Y_LPARAM(message.wParam));
397 QGuiApplicationPrivate::lastCursorPosition = QCursor::pos().toPointF();
399 const QPlatformScreen *screen = screenManager
.screenAtDp(globalPos
);
401 screen = screenManager.screens().value(0);
403 emit contextMenuRequested(globalPos, screen);
404 emit activated(Context);
408 SetForegroundWindow(m_hwnd);
409 m_menu->trackPopupMenu(message.hwnd, globalPos.x(), globalPos.y());
414 case NIN_BALLOONUSERCLICK:
415 emit messageClicked();
418 emit activated(MiddleClick);
426 case WM_INITMENUPOPUP:
427 QWindowsPopupMenu::notifyAboutToShow(
reinterpret_cast<HMENU>(message.wParam));
430 QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>();
433 QWindowsPopupMenu::notifyTriggered(LOWORD(message.wParam));
436 if (message.message == MYWM_TASKBARCREATED) {
439 const QIcon oldIcon = m_icon;
443 sendTrayMessage(NIM_ADD);
450#ifndef QT_NO_DEBUG_STREAM
454 d <<
static_cast<
const void *>(
this) <<
", \"" << m_toolTip
455 <<
"\", hwnd=" << m_hwnd <<
", m_hIcon=" << m_hIcon <<
", menu="
461 QDebugStateSaver saver(d);
464 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
static QWindowsWindowClassRegistry * instance()
Combined button and popup list for selecting options.
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