4#include <QtCore/qt_windows.h>
13#include <QtGui/qguiapplication.h>
14#include <QtGui/qpixmap.h>
15#include <QtCore/qdebug.h>
16#include <QtCore/qlist.h>
17#include <QtCore/qrect.h>
18#include <QtCore/qsettings.h>
19#include <qpa/qwindowsysteminterface.h>
21#include <QtGui/private/qguiapplication_p.h>
30using namespace Qt::StringLiterals;
35#define MYWM_NOTIFYICON (WM_APP+101
)
37Q_GUI_EXPORT HICON qt_pixmapToWinHICON(
const QPixmap &);
42 const int length = qMin(maxLength - 1, in.size());
43 if (length < in.size())
45 in.toWCharArray(target);
46 target[length] =
wchar_t(0);
51 memset(&tnd, 0,
sizeof(NOTIFYICONDATA));
52 tnd.cbSize =
sizeof(NOTIFYICONDATA);
53 tnd.uVersion = NOTIFYICON_VERSION_4;
58 tnd.uFlags |= NIF_MESSAGE | NIF_ICON | NIF_TIP;
61 qStringToLimitedWCharArray(tip, tnd.szTip,
sizeof(tnd.szTip) /
sizeof(
wchar_t));
66 tnd.uFlags |= NIF_STATE;
67 tnd.dwStateMask = NIS_HIDDEN;
68 tnd.dwState = v ? 0 : NIS_HIDDEN;
78using HwndTrayIconEntries = QList<QWindowsHwndSystemTrayIconEntry>;
84 const HwndTrayIconEntries *entries = hwndTrayIconEntries();
85 for (
int i = 0, size = entries->size(); i < size; ++i) {
86 if (entries->at(i).hwnd == hwnd)
92LRESULT QT_WIN_CALLBACK qWindowsTrayIconWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
95 || message == WM_INITMENU || message == WM_INITMENUPOPUP
96 || message == WM_CLOSE || message == WM_COMMAND) {
97 const int index = indexOfHwnd(hwnd);
101 msg.message = message;
104 msg.pt.x = GET_X_LPARAM(lParam);
105 msg.pt.y = GET_Y_LPARAM(lParam);
107 if (hwndTrayIconEntries()->at(index).trayIcon->winEvent(msg, &result))
111 return DefWindowProc(hwnd, message, wParam, lParam);
122 const QString className = ctx->registerWindowClass(
123 "TrayIconMessageWindowClass"_L1,
124 qWindowsTrayIconWndProc);
125 const wchar_t windowName[] = L"QTrayIconMessageWindow";
126 return CreateWindowEx(0,
reinterpret_cast<
const wchar_t *>(className.utf16()),
127 windowName, WS_OVERLAPPED,
128 CW_USEDEFAULT, CW_USEDEFAULT,
129 CW_USEDEFAULT, CW_USEDEFAULT,
131 static_cast<HINSTANCE>(GetModuleHandle(
nullptr)),
nullptr);
135
136
137
138
139
147 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this;
153 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this;
155 if (!setIconVisible(m_visible))
161 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this;
168 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
'(' << icon <<
')' <<
this;
170 const HICON hIconToDestroy = createIcon(icon);
171 if (ensureInstalled())
172 sendTrayMessage(NIM_MODIFY);
174 DestroyIcon(hIconToDestroy);
179 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
'(' << tooltip <<
')' <<
this;
180 if (m_toolTip == tooltip)
184 sendTrayMessage(NIM_MODIFY);
189 if (!isIconVisible())
192 NOTIFYICONIDENTIFIER nid;
193 memset(&nid, 0,
sizeof(nid));
194 nid.cbSize =
sizeof(nid);
196 nid.uID = q_uNOTIFYICONID;
198 const QRect result = SUCCEEDED(Shell_NotifyIconGetRect(&nid, &rect))
199 ? QRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top)
201 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this <<
"returns" << result;
207 QPlatformSystemTrayIcon::MessageIcon iconType,
210 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
'(' << title << messageIn << icon
211 << iconType << msecsIn <<
')' <<
this;
215 QString message = messageIn;
216 if (message.isEmpty() && !title.isEmpty())
217 message.append(u' ');
220 initNotifyIconData(tnd);
221 qStringToLimitedWCharArray(message, tnd.szInfo, 256);
222 qStringToLimitedWCharArray(title, tnd.szInfoTitle, 64);
224 tnd.uID = q_uNOTIFYICONID;
226 const auto size = icon.actualSize(QSize(256, 256));
227 QPixmap pm = icon.pixmap(size);
228 if (m_hMessageIcon) {
229 DestroyIcon(m_hMessageIcon);
230 m_hMessageIcon =
nullptr;
233 tnd.dwInfoFlags = NIIF_INFO;
235 tnd.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;
236 m_hMessageIcon = qt_pixmapToWinHICON(pm);
237 tnd.hBalloonIcon = m_hMessageIcon;
240 tnd.uTimeout = msecsIn <= 0 ? UINT(10000) : UINT(msecsIn);
241 tnd.uFlags = NIF_INFO | NIF_SHOWTIP;
243 Shell_NotifyIcon(NIM_MODIFY, &tnd);
249 return QWindowsContext::readAdvancedExplorerSettings(L"EnableBalloonTips", 1) != 0;
254 if (QWindowsTheme::useNativeMenus() && m_menu.isNull())
255 m_menu =
new QWindowsPopupMenu;
256 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this <<
"returns" << m_menu.data();
257 return m_menu.data();
265 if (m_hIcon ==
nullptr)
267 m_hwnd = createTrayIconMessageWindow();
268 if (Q_UNLIKELY(m_hwnd ==
nullptr))
271 if (!MYWM_TASKBARCREATED)
274 ChangeWindowMessageFilterEx(m_hwnd, MYWM_TASKBARCREATED, MSGFLT_ALLOW,
nullptr);
275 qCDebug(lcQpaTrayIcon) <<
__FUNCTION__ <<
this <<
"MYWM_TASKBARCREATED=" << MYWM_TASKBARCREATED;
278 hwndTrayIconEntries()->append(entry);
279 sendTrayMessage(NIM_ADD);
286 const int index = indexOfHwnd(m_hwnd);
288 hwndTrayIconEntries()->removeAt(index);
289 sendTrayMessage(NIM_DELETE);
290 DestroyWindow(m_hwnd);
293 if (m_hIcon !=
nullptr)
294 DestroyIcon(m_hIcon);
295 if (m_hMessageIcon !=
nullptr)
296 DestroyIcon(m_hMessageIcon);
298 m_hMessageIcon =
nullptr;
308 initNotifyIconData(tnd);
309 tnd.uID = q_uNOTIFYICONID;
311 setIconVisibility(tnd, visible);
312 return Shell_NotifyIcon(NIM_MODIFY, &tnd) == TRUE;
317 NOTIFYICONIDENTIFIER nid;
318 memset(&nid, 0,
sizeof(nid));
319 nid.cbSize =
sizeof(nid);
321 nid.uID = q_uNOTIFYICONID;
323 const HRESULT hr = Shell_NotifyIconGetRect(&nid, &rect);
325 if (FAILED(hr) || hr == S_FALSE)
328 HMONITOR monitor = MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST);
330 info.cbSize =
sizeof(MONITORINFO);
331 GetMonitorInfo(monitor, &info);
335 return rect.bottom <= info.rcMonitor.bottom;
341 initNotifyIconData(tnd);
342 tnd.uID = q_uNOTIFYICONID;
344 tnd.uFlags = NIF_SHOWTIP;
345 if (msg != NIM_DELETE && !m_visible)
346 setIconVisibility(tnd, m_visible);
347 if (msg == NIM_ADD || msg == NIM_MODIFY)
348 setIconContents(tnd, m_toolTip, m_hIcon);
349 if (!Shell_NotifyIcon(msg, &tnd))
351 return msg != NIM_ADD || Shell_NotifyIcon(NIM_SETVERSION, &tnd);
357 const HICON oldIcon = m_hIcon;
361 const QSize requestedSize = QSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
362 const QSize size = icon.actualSize(requestedSize);
363 const QPixmap pm = icon.pixmap(size);
365 m_hIcon = qt_pixmapToWinHICON(pm);
372 switch (message.message) {
374 Q_ASSERT(q_uNOTIFYICONID == HIWORD(message.lParam));
375 const int trayMessage = LOWORD(message.lParam);
376 switch (trayMessage) {
379 if (m_ignoreNextMouseRelease)
380 m_ignoreNextMouseRelease =
false;
382 emit activated(Trigger);
384 case WM_LBUTTONDBLCLK:
385 m_ignoreNextMouseRelease =
true;
386 emit activated(DoubleClick);
388 case WM_CONTEXTMENU: {
392 const QPoint globalPos = QPoint(GET_X_LPARAM(message.wParam), GET_Y_LPARAM(message.wParam));
396 QGuiApplicationPrivate::lastCursorPosition = QCursor::pos().toPointF();
398 const QPlatformScreen *screen = screenManager
.screenAtDp(globalPos
);
400 screen = screenManager.screens().value(0);
402 emit contextMenuRequested(globalPos, screen);
403 emit activated(Context);
407 SetForegroundWindow(m_hwnd);
408 m_menu->trackPopupMenu(message.hwnd, globalPos.x(), globalPos.y());
413 case NIN_BALLOONUSERCLICK:
414 emit messageClicked();
417 emit activated(MiddleClick);
425 case WM_INITMENUPOPUP:
426 QWindowsPopupMenu::notifyAboutToShow(
reinterpret_cast<HMENU>(message.wParam));
429 QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>();
432 QWindowsPopupMenu::notifyTriggered(LOWORD(message.wParam));
435 if (message.message == MYWM_TASKBARCREATED) {
438 const QIcon oldIcon = m_icon;
442 sendTrayMessage(NIM_ADD);
449#ifndef QT_NO_DEBUG_STREAM
453 d <<
static_cast<
const void *>(
this) <<
", \"" << m_toolTip
454 <<
"\", hwnd=" << m_hwnd <<
", m_hIcon=" << m_hIcon <<
", menu="
460 QDebugStateSaver saver(d);
463 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