Creating Custom System Tray Icons in Qt

Qt offers powerful UI capabilities beyond just interface design, including excellent platform extension features. This article explores Qt's system tray implementation and demonstrates how to create custom tray icons that overcome the limitations of the standard QSystemTrayIcon class.

Common messaging applications like QQ and WeChat exhibit several tray behaviors that developers often want to implement:

  • Icon flashing for new messages
  • Bubble notification popups
  • Various mouse click interactions (single, double, right-click, scroll)

While QSystemTrayIcon handles these standard events effectively, it lacks support for hover events on most platforms. The Qt documentation explicitly mentions that tooltip events are only available on X11 systems, leaving other platforms without native hover support. This limitation became apparent when examining Qt's source code, confirming the platform-depandent behavior.

To achieve hover functionality similar to QQ's tray menu that appears on mouse hover, we need to implement a custom solution that bypasses QSystemTrayIcon's limitations while still leveraging some of Qt's underlying code.

Windows Implementation Approach

For Windows-based custom tray implementation, we need to understand two key components:

  • NOTIFYICONDATA structure - defines tray icon properties and behavior
  • Shell_NotifyIcon API - Windows function for managing tray icons

The main challenge involves capturing hover and leave events for the tray icon using Qt's event system.

Implementation Steps

1. Global Event Filtering

First, implement the QAbstractNativeEventFilter interface to capture system-wide events. Register your filter with the application instance:

qApp->installNativeEventFilter(this);

2. Tray Icon Creation

Create the tray icon using Windows APIs. The following demonstrates icon initialization:

NOTIFYICONDATA trayData;
QWidget* eventWidget = new QWidget;
trayData.cbSize = sizeof(trayData);
trayData.hIcon = qt_pixmapToWinHICON(
    QIcon(":/assets/tray_icon.png").pixmap(16, 16)
);
trayData.hWnd = HWND(eventWidget->winId());
trayData.uCallbackMessage = WM_TRAY_EVENT;
trayData.uID = TRAY_ID;
trayData.uFlags = NIF_ICON | NIF_MESSAGE;

Shell_NotifyIcon(NIM_ADD, &trayData);

The event widget provides a window handle (hWnd) necessary for receiving mouse events. The uFlags parameter indicates which structure members are valid, enabling subsequent modifications to tooltip text or icons.

3. Mouse Event Processing

Handle tray events in the nativeEventFilter implementation:

bool CustomTray::nativeEventFilter(const QByteArray& eventType, 
                                   void* message, 
                                   long* result)
{
    if (eventType == "windows_generic_MSG" || 
        eventType == "windows_dispatcher_MSG") {
        MSG* msg = reinterpret_cast<msg>(message);
        
        if (msg->message == WM_TRAY_EVENT) {
            switch (msg->lParam) {
                case WM_MOUSEMOVE:
                    m_hoverTracker.processMouseMove();
                    break;
                case WM_MOUSEHOVER:
                    showTrayMenu();
                    break;
                case WM_MOUSELEAVE:
                    hideTrayMenu();
                    break;
                case WM_LBUTTONDBLCLK:
                    handleDoubleClick();
                    break;
                case WM_LBUTTONDOWN:
                    handleLeftClick();
                    break;
                case WM_RBUTTONDOWN:
                    handleRightClick();
                    break;
            }
        }
    }
    return false;
}
</msg>

Windows provides only mouse movement events for tray icons, but we can simulate hover and leave events by tracking mouse movement patterns. The HoverTracker class (included in the sample code) handles this simulation logic.

4. Cleanup

Remove the tray icon when the application exits:

Shell_NotifyIcon(NIM_DELETE, &trayData);

Advanced Implementation

For a complete implementation, reference Qt's source file qsystemtrayicon_win.cpp, which contains the full QSystemTrayIcon Windows implementation. This provides insights into proper resource management and edge case handling.

The sample implementation focuses specifically on hover and leave events, but the foundation allows for extending with additional custom behaviors as needed.

Tags: Qt Windows SystemTray NativeEvents QSystemTrayIcon

Posted on Sat, 04 Jul 2026 16:32:19 +0000 by Mightywayne