1 | // Copyright (C) 2016 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qsystemtrayicon.h" |
5 | #include "qsystemtrayicon_p.h" |
6 | |
7 | #ifndef QT_NO_SYSTEMTRAYICON |
8 | |
9 | #if QT_CONFIG(menu) |
10 | #include "qmenu.h" |
11 | #endif |
12 | #include "qlist.h" |
13 | #include "qevent.h" |
14 | #include "qpoint.h" |
15 | #if QT_CONFIG(label) |
16 | #include "qlabel.h" |
17 | #include "private/qlabel_p.h" |
18 | #endif |
19 | #if QT_CONFIG(pushbutton) |
20 | #include "qpushbutton.h" |
21 | #endif |
22 | #include "qpainterpath.h" |
23 | #include "qpainter.h" |
24 | #include "qstyle.h" |
25 | #include "qgridlayout.h" |
26 | #include "qapplication.h" |
27 | #include "qbitmap.h" |
28 | |
29 | #include <private/qhighdpiscaling_p.h> |
30 | #include <qpa/qplatformscreen.h> |
31 | |
32 | QT_BEGIN_NAMESPACE |
33 | |
34 | static QIcon messageIcon2qIcon(QSystemTrayIcon::MessageIcon icon) |
35 | { |
36 | QStyle::StandardPixmap stdIcon = QStyle::SP_CustomBase; // silence gcc 4.9.0 about uninited variable |
37 | switch (icon) { |
38 | case QSystemTrayIcon::Information: |
39 | stdIcon = QStyle::SP_MessageBoxInformation; |
40 | break; |
41 | case QSystemTrayIcon::Warning: |
42 | stdIcon = QStyle::SP_MessageBoxWarning; |
43 | break; |
44 | case QSystemTrayIcon::Critical: |
45 | stdIcon = QStyle::SP_MessageBoxCritical; |
46 | break; |
47 | case QSystemTrayIcon::NoIcon: |
48 | return QIcon(); |
49 | } |
50 | return QApplication::style()->standardIcon(standardIcon: stdIcon); |
51 | } |
52 | |
53 | /*! |
54 | \class QSystemTrayIcon |
55 | \brief The QSystemTrayIcon class provides an icon for an application in the system tray. |
56 | \since 4.2 |
57 | \ingroup desktop |
58 | \inmodule QtWidgets |
59 | |
60 | Modern operating systems usually provide a special area on the desktop, |
61 | called the \e{system tray} or \e{notification area}, where long-running |
62 | applications can display icons and short messages. |
63 | |
64 | \image system-tray.webp The system tray on Windows 10. |
65 | |
66 | The QSystemTrayIcon class can be used on the following platforms: |
67 | |
68 | \list |
69 | \li All supported versions of Windows. |
70 | \li All Linux desktop environments that implement the D-Bus |
71 | \l{http://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem} |
72 | {StatusNotifierItem specification}, including KDE, Gnome, Xfce, LXQt, and DDE. |
73 | \li All window managers and independent tray implementations for X11 that implement the |
74 | \l{http://standards.freedesktop.org/systemtray-spec/systemtray-spec-0.2.html} |
75 | {freedesktop.org XEmbed system tray specification}. |
76 | \li All supported versions of \macos. |
77 | \endlist |
78 | |
79 | To check whether a system tray is present on the user's desktop, |
80 | call the QSystemTrayIcon::isSystemTrayAvailable() static function. |
81 | |
82 | To add a system tray entry, create a QSystemTrayIcon object, call setContextMenu() |
83 | to provide a context menu for the icon, and call show() to make it visible in the |
84 | system tray. Status notification messages ("balloon messages") can be displayed at |
85 | any time using showMessage(). |
86 | |
87 | If the system tray is unavailable when a system tray icon is constructed, but |
88 | becomes available later, QSystemTrayIcon will automatically add an entry for the |
89 | application in the system tray if the icon is \l visible. |
90 | |
91 | The activated() signal is emitted when the user activates the icon. |
92 | |
93 | Only on X11, when a tooltip is requested, the QSystemTrayIcon receives a QHelpEvent |
94 | of type QEvent::ToolTip. Additionally, the QSystemTrayIcon receives wheel events of |
95 | type QEvent::Wheel. These are not supported on any other platform. Note: Since GNOME |
96 | Shell version 3.26, not all QSystemTrayIcon::ActivationReason are supported by the |
97 | system without shell extensions installed. |
98 | |
99 | \sa QDesktopServices, {Desktop Integration}, {System Tray Icon Example} |
100 | */ |
101 | |
102 | /*! |
103 | \enum QSystemTrayIcon::MessageIcon |
104 | |
105 | This enum describes the icon that is shown when a balloon message is displayed. |
106 | |
107 | \value NoIcon No icon is shown. |
108 | \value Information An information icon is shown. |
109 | \value Warning A standard warning icon is shown. |
110 | \value Critical A critical warning icon is shown. |
111 | |
112 | \sa QMessageBox |
113 | */ |
114 | |
115 | /*! |
116 | Constructs a QSystemTrayIcon object with the given \a parent. |
117 | |
118 | The icon is initially invisible. |
119 | |
120 | \sa visible |
121 | */ |
122 | QSystemTrayIcon::QSystemTrayIcon(QObject *parent) |
123 | : QObject(*new QSystemTrayIconPrivate(), parent) |
124 | { |
125 | } |
126 | |
127 | /*! |
128 | Constructs a QSystemTrayIcon object with the given \a icon and \a parent. |
129 | |
130 | The icon is initially invisible. |
131 | |
132 | \sa visible |
133 | */ |
134 | QSystemTrayIcon::QSystemTrayIcon(const QIcon &icon, QObject *parent) |
135 | : QSystemTrayIcon(parent) |
136 | { |
137 | setIcon(icon); |
138 | } |
139 | |
140 | /*! |
141 | Removes the icon from the system tray and frees all allocated resources. |
142 | */ |
143 | QSystemTrayIcon::~QSystemTrayIcon() |
144 | { |
145 | Q_D(QSystemTrayIcon); |
146 | d->remove_sys(); |
147 | } |
148 | |
149 | #if QT_CONFIG(menu) |
150 | |
151 | /*! |
152 | Sets the specified \a menu to be the context menu for the system tray icon. |
153 | |
154 | The menu will pop up when the user requests the context menu for the system |
155 | tray icon by clicking the mouse button. |
156 | |
157 | \note The system tray icon does not take ownership of the menu. You must |
158 | ensure that it is deleted at the appropriate time by, for example, creating |
159 | the menu with a suitable parent object. |
160 | */ |
161 | void QSystemTrayIcon::setContextMenu(QMenu *menu) |
162 | { |
163 | Q_D(QSystemTrayIcon); |
164 | QMenu *oldMenu = d->menu.data(); |
165 | if (oldMenu == menu) |
166 | return; |
167 | |
168 | d->menu = menu; |
169 | d->updateMenu_sys(); |
170 | |
171 | if (d->qpa_sys) { |
172 | // Show the QMenu-based menu for QPA plugins that do not provide native menus |
173 | if (oldMenu && !oldMenu->platformMenu()) |
174 | QObject::disconnect(sender: d->qpa_sys, signal: &QPlatformSystemTrayIcon::contextMenuRequested, receiver: oldMenu, zero: nullptr); |
175 | if (menu && !menu->platformMenu()) { |
176 | QObject::connect(sender: d->qpa_sys, signal: &QPlatformSystemTrayIcon::contextMenuRequested, |
177 | context: menu, |
178 | slot: [menu](QPoint globalNativePos, const QPlatformScreen *platformScreen) |
179 | { |
180 | QScreen *screen = platformScreen ? platformScreen->screen() : nullptr; |
181 | menu->popup(pos: QHighDpi::fromNativePixels(value: globalNativePos, context: screen), at: nullptr); |
182 | }); |
183 | } |
184 | } |
185 | } |
186 | |
187 | /*! |
188 | Returns the current context menu for the system tray entry. |
189 | */ |
190 | QMenu* QSystemTrayIcon::contextMenu() const |
191 | { |
192 | Q_D(const QSystemTrayIcon); |
193 | return d->menu; |
194 | } |
195 | |
196 | #endif // QT_CONFIG(menu) |
197 | |
198 | /*! |
199 | \property QSystemTrayIcon::icon |
200 | \brief the system tray icon |
201 | |
202 | On Windows, the system tray icon size is 16x16; on X11, the preferred size is |
203 | 22x22. The icon will be scaled to the appropriate size as necessary. |
204 | */ |
205 | void QSystemTrayIcon::setIcon(const QIcon &icon) |
206 | { |
207 | Q_D(QSystemTrayIcon); |
208 | d->icon = icon; |
209 | d->updateIcon_sys(); |
210 | } |
211 | |
212 | QIcon QSystemTrayIcon::icon() const |
213 | { |
214 | Q_D(const QSystemTrayIcon); |
215 | return d->icon; |
216 | } |
217 | |
218 | /*! |
219 | \property QSystemTrayIcon::toolTip |
220 | \brief the tooltip for the system tray entry |
221 | |
222 | On some systems, the tooltip's length is limited. The tooltip will be truncated |
223 | if necessary. |
224 | */ |
225 | void QSystemTrayIcon::setToolTip(const QString &tooltip) |
226 | { |
227 | Q_D(QSystemTrayIcon); |
228 | d->toolTip = tooltip; |
229 | d->updateToolTip_sys(); |
230 | } |
231 | |
232 | QString QSystemTrayIcon::toolTip() const |
233 | { |
234 | Q_D(const QSystemTrayIcon); |
235 | return d->toolTip; |
236 | } |
237 | |
238 | /*! |
239 | \fn void QSystemTrayIcon::show() |
240 | |
241 | Shows the icon in the system tray. |
242 | |
243 | \sa hide(), visible |
244 | */ |
245 | |
246 | /*! |
247 | \fn void QSystemTrayIcon::hide() |
248 | |
249 | Hides the system tray entry. |
250 | |
251 | \sa show(), visible |
252 | */ |
253 | |
254 | /*! |
255 | \since 4.3 |
256 | Returns the geometry of the system tray icon in screen coordinates. |
257 | |
258 | \sa visible |
259 | */ |
260 | QRect QSystemTrayIcon::geometry() const |
261 | { |
262 | Q_D(const QSystemTrayIcon); |
263 | if (!d->visible) |
264 | return QRect(); |
265 | return d->geometry_sys(); |
266 | } |
267 | |
268 | /*! |
269 | \property QSystemTrayIcon::visible |
270 | \brief whether the system tray entry is visible |
271 | |
272 | Setting this property to true or calling show() makes the system tray icon |
273 | visible; setting this property to false or calling hide() hides it. |
274 | */ |
275 | void QSystemTrayIcon::setVisible(bool visible) |
276 | { |
277 | Q_D(QSystemTrayIcon); |
278 | if (visible == d->visible) |
279 | return; |
280 | if (Q_UNLIKELY(visible && d->icon.isNull())) |
281 | qWarning(msg: "QSystemTrayIcon::setVisible: No Icon set"); |
282 | d->visible = visible; |
283 | if (d->visible) |
284 | d->install_sys(); |
285 | else |
286 | d->remove_sys(); |
287 | } |
288 | |
289 | bool QSystemTrayIcon::isVisible() const |
290 | { |
291 | Q_D(const QSystemTrayIcon); |
292 | return d->visible; |
293 | } |
294 | |
295 | /*! |
296 | \reimp |
297 | */ |
298 | bool QSystemTrayIcon::event(QEvent *e) |
299 | { |
300 | return QObject::event(event: e); |
301 | } |
302 | |
303 | /*! |
304 | \enum QSystemTrayIcon::ActivationReason |
305 | |
306 | This enum describes the reason the system tray was activated. |
307 | |
308 | \value Unknown Unknown reason |
309 | \value Context The context menu for the system tray entry was requested |
310 | \value DoubleClick The system tray entry was double clicked. \note On macOS, a |
311 | double click will only be emitted if no context menu is set, since the menu |
312 | opens on mouse press |
313 | \value Trigger The system tray entry was clicked |
314 | \value MiddleClick The system tray entry was clicked with the middle mouse button |
315 | |
316 | \sa activated() |
317 | */ |
318 | |
319 | /*! |
320 | \fn void QSystemTrayIcon::activated(QSystemTrayIcon::ActivationReason reason) |
321 | |
322 | This signal is emitted when the user activates the system tray icon. \a reason |
323 | specifies the reason for activation. QSystemTrayIcon::ActivationReason enumerates |
324 | the various reasons. |
325 | |
326 | \sa QSystemTrayIcon::ActivationReason |
327 | */ |
328 | |
329 | /*! |
330 | \fn void QSystemTrayIcon::messageClicked() |
331 | |
332 | This signal is emitted when the message displayed using showMessage() |
333 | was clicked by the user. |
334 | |
335 | \note We follow Microsoft Windows behavior, so the |
336 | signal is also emitted when the user clicks on a tray icon with |
337 | a balloon message displayed. |
338 | |
339 | \sa activated() |
340 | */ |
341 | |
342 | |
343 | /*! |
344 | Returns \c true if the system tray is available; otherwise returns \c false. |
345 | |
346 | If the system tray is currently unavailable but becomes available later, |
347 | QSystemTrayIcon will automatically add an entry in the system tray if it |
348 | is \l visible. |
349 | */ |
350 | |
351 | bool QSystemTrayIcon::isSystemTrayAvailable() |
352 | { |
353 | return QSystemTrayIconPrivate::isSystemTrayAvailable_sys(); |
354 | } |
355 | |
356 | /*! |
357 | Returns \c true if the system tray supports balloon messages; otherwise returns \c false. |
358 | |
359 | \sa showMessage() |
360 | */ |
361 | bool QSystemTrayIcon::supportsMessages() |
362 | { |
363 | return QSystemTrayIconPrivate::supportsMessages_sys(); |
364 | } |
365 | |
366 | /*! |
367 | \fn void QSystemTrayIcon::showMessage(const QString &title, const QString &message, MessageIcon icon, int millisecondsTimeoutHint) |
368 | \since 4.3 |
369 | |
370 | Shows a balloon message for the entry with the given \a title, \a message and |
371 | \a icon for the time specified in \a millisecondsTimeoutHint. \a title and \a message |
372 | must be plain text strings. |
373 | |
374 | Message can be clicked by the user; the messageClicked() signal will emitted when |
375 | this occurs. |
376 | |
377 | Note that display of messages are dependent on the system configuration and user |
378 | preferences, and that messages may not appear at all. Hence, it should not be |
379 | relied upon as the sole means for providing critical information. |
380 | |
381 | On Windows, the \a millisecondsTimeoutHint is usually ignored by the system |
382 | when the application has focus. |
383 | |
384 | Has been turned into a slot in Qt 5.2. |
385 | |
386 | \sa show(), supportsMessages() |
387 | */ |
388 | void QSystemTrayIcon::showMessage(const QString& title, const QString& msg, |
389 | QSystemTrayIcon::MessageIcon msgIcon, int msecs) |
390 | { |
391 | Q_D(QSystemTrayIcon); |
392 | if (d->visible) |
393 | d->showMessage_sys(title, msg, icon: messageIcon2qIcon(icon: msgIcon), msgIcon, msecs); |
394 | } |
395 | |
396 | /*! |
397 | \fn void QSystemTrayIcon::showMessage(const QString &title, const QString &message, const QIcon &icon, int millisecondsTimeoutHint) |
398 | |
399 | \overload showMessage() |
400 | |
401 | Shows a balloon message for the entry with the given \a title, \a message, |
402 | and custom icon \a icon for the time specified in \a millisecondsTimeoutHint. |
403 | |
404 | \since 5.9 |
405 | */ |
406 | void QSystemTrayIcon::showMessage(const QString &title, const QString &msg, |
407 | const QIcon &icon, int msecs) |
408 | { |
409 | Q_D(QSystemTrayIcon); |
410 | if (d->visible) |
411 | d->showMessage_sys(title, msg, icon, msgIcon: QSystemTrayIcon::NoIcon, msecs); |
412 | } |
413 | |
414 | void QSystemTrayIconPrivate::_q_emitActivated(QPlatformSystemTrayIcon::ActivationReason reason) |
415 | { |
416 | Q_Q(QSystemTrayIcon); |
417 | emit q->activated(reason: static_cast<QSystemTrayIcon::ActivationReason>(reason)); |
418 | } |
419 | |
420 | ////////////////////////////////////////////////////////////////////// |
421 | static QBalloonTip *theSolitaryBalloonTip = nullptr; |
422 | |
423 | void QBalloonTip::showBalloon(const QIcon &icon, const QString &title, |
424 | const QString &message, QSystemTrayIcon *trayIcon, |
425 | const QPoint &pos, int timeout, bool showArrow) |
426 | { |
427 | hideBalloon(); |
428 | if (message.isEmpty() && title.isEmpty()) |
429 | return; |
430 | |
431 | theSolitaryBalloonTip = new QBalloonTip(icon, title, message, trayIcon); |
432 | if (timeout < 0) |
433 | timeout = 10000; //10 s default |
434 | theSolitaryBalloonTip->balloon(pos, timeout, showArrow); |
435 | } |
436 | |
437 | void QBalloonTip::hideBalloon() |
438 | { |
439 | if (!theSolitaryBalloonTip) |
440 | return; |
441 | theSolitaryBalloonTip->hide(); |
442 | delete theSolitaryBalloonTip; |
443 | theSolitaryBalloonTip = nullptr; |
444 | } |
445 | |
446 | void QBalloonTip::updateBalloonPosition(const QPoint& pos) |
447 | { |
448 | if (!theSolitaryBalloonTip) |
449 | return; |
450 | theSolitaryBalloonTip->hide(); |
451 | theSolitaryBalloonTip->balloon(pos, 0, theSolitaryBalloonTip->showArrow); |
452 | } |
453 | |
454 | bool QBalloonTip::isBalloonVisible() |
455 | { |
456 | return theSolitaryBalloonTip; |
457 | } |
458 | |
459 | QBalloonTip::QBalloonTip(const QIcon &icon, const QString &title, |
460 | const QString &message, QSystemTrayIcon *ti) |
461 | : QWidget(nullptr, Qt::ToolTip), |
462 | trayIcon(ti), |
463 | timerId(-1), |
464 | showArrow(true) |
465 | { |
466 | setAttribute(Qt::WA_DeleteOnClose); |
467 | QObject::connect(sender: ti, SIGNAL(destroyed()), receiver: this, SLOT(close())); |
468 | |
469 | #if QT_CONFIG(label) |
470 | QLabel *titleLabel = new QLabel; |
471 | titleLabel->installEventFilter(filterObj: this); |
472 | titleLabel->setText(title); |
473 | QFont f = titleLabel->font(); |
474 | f.setBold(true); |
475 | titleLabel->setFont(f); |
476 | titleLabel->setTextFormat(Qt::PlainText); // to maintain compat with windows |
477 | #endif |
478 | |
479 | const int iconSize = 18; |
480 | const int closeButtonSize = 15; |
481 | |
482 | #if QT_CONFIG(pushbutton) |
483 | QPushButton *closeButton = new QPushButton; |
484 | closeButton->setIcon(style()->standardIcon(standardIcon: QStyle::SP_TitleBarCloseButton)); |
485 | closeButton->setIconSize(QSize(closeButtonSize, closeButtonSize)); |
486 | closeButton->setSizePolicy(hor: QSizePolicy::Fixed, ver: QSizePolicy::Fixed); |
487 | closeButton->setFixedSize(w: closeButtonSize, h: closeButtonSize); |
488 | QObject::connect(sender: closeButton, SIGNAL(clicked()), receiver: this, SLOT(close())); |
489 | #else |
490 | Q_UNUSED(closeButtonSize); |
491 | #endif |
492 | |
493 | #if QT_CONFIG(label) |
494 | QLabel *msgLabel = new QLabel; |
495 | msgLabel->installEventFilter(filterObj: this); |
496 | msgLabel->setText(message); |
497 | msgLabel->setTextFormat(Qt::PlainText); |
498 | msgLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); |
499 | |
500 | // smart size for the message label |
501 | int limit = QWidgetPrivate::availableScreenGeometry(widget: msgLabel).width() / 3; |
502 | if (msgLabel->sizeHint().width() > limit) { |
503 | msgLabel->setWordWrap(true); |
504 | if (msgLabel->sizeHint().width() > limit) { |
505 | msgLabel->d_func()->ensureTextControl(); |
506 | if (QWidgetTextControl *control = msgLabel->d_func()->control) { |
507 | QTextOption opt = control->document()->defaultTextOption(); |
508 | opt.setWrapMode(QTextOption::WrapAnywhere); |
509 | control->document()->setDefaultTextOption(opt); |
510 | } |
511 | } |
512 | // Here we allow the text being much smaller than the balloon widget |
513 | // to emulate the weird standard windows behavior. |
514 | msgLabel->setFixedSize(w: limit, h: msgLabel->heightForWidth(limit)); |
515 | } |
516 | #endif |
517 | |
518 | QGridLayout *layout = new QGridLayout; |
519 | #if QT_CONFIG(label) |
520 | if (!icon.isNull()) { |
521 | QLabel *iconLabel = new QLabel; |
522 | iconLabel->setPixmap(icon.pixmap(w: iconSize, h: iconSize)); |
523 | iconLabel->setSizePolicy(hor: QSizePolicy::Fixed, ver: QSizePolicy::Fixed); |
524 | iconLabel->setMargin(2); |
525 | layout->addWidget(iconLabel, row: 0, column: 0); |
526 | layout->addWidget(titleLabel, row: 0, column: 1); |
527 | } else { |
528 | layout->addWidget(titleLabel, row: 0, column: 0, rowSpan: 1, columnSpan: 2); |
529 | } |
530 | #endif |
531 | |
532 | #if QT_CONFIG(pushbutton) |
533 | layout->addWidget(closeButton, row: 0, column: 2); |
534 | #endif |
535 | |
536 | #if QT_CONFIG(label) |
537 | layout->addWidget(msgLabel, row: 1, column: 0, rowSpan: 1, columnSpan: 3); |
538 | #endif |
539 | layout->setSizeConstraint(QLayout::SetFixedSize); |
540 | layout->setContentsMargins(left: 3, top: 3, right: 3, bottom: 3); |
541 | setLayout(layout); |
542 | |
543 | QPalette pal = palette(); |
544 | pal.setColor(acr: QPalette::Window, acolor: QColor(0xff, 0xff, 0xe1)); |
545 | pal.setColor(acr: QPalette::WindowText, acolor: Qt::black); |
546 | setPalette(pal); |
547 | } |
548 | |
549 | QBalloonTip::~QBalloonTip() |
550 | { |
551 | theSolitaryBalloonTip = nullptr; |
552 | } |
553 | |
554 | void QBalloonTip::paintEvent(QPaintEvent *) |
555 | { |
556 | QPainter painter(this); |
557 | painter.drawPixmap(r: rect(), pm: pixmap); |
558 | } |
559 | |
560 | void QBalloonTip::resizeEvent(QResizeEvent *ev) |
561 | { |
562 | QWidget::resizeEvent(event: ev); |
563 | } |
564 | |
565 | void QBalloonTip::balloon(const QPoint& pos, int msecs, bool showArrow) |
566 | { |
567 | this->showArrow = showArrow; |
568 | QScreen *screen = QGuiApplication::screenAt(point: pos); |
569 | if (!screen) |
570 | screen = QGuiApplication::primaryScreen(); |
571 | QRect screenRect = screen->geometry(); |
572 | QSize sh = sizeHint(); |
573 | const int border = 1; |
574 | const int ah = 18, ao = 18, aw = 18, rc = 7; |
575 | bool arrowAtTop = (pos.y() + sh.height() + ah < screenRect.height()); |
576 | bool arrowAtLeft = (pos.x() + sh.width() - ao < screenRect.width()); |
577 | setContentsMargins(left: border + 3, top: border + (arrowAtTop ? ah : 0) + 2, right: border + 3, bottom: border + (arrowAtTop ? 0 : ah) + 2); |
578 | updateGeometry(); |
579 | sh = sizeHint(); |
580 | |
581 | int ml, mr, mt, mb; |
582 | QSize sz = sizeHint(); |
583 | if (!arrowAtTop) { |
584 | ml = mt = 0; |
585 | mr = sz.width() - 1; |
586 | mb = sz.height() - ah - 1; |
587 | } else { |
588 | ml = 0; |
589 | mt = ah; |
590 | mr = sz.width() - 1; |
591 | mb = sz.height() - 1; |
592 | } |
593 | |
594 | QPainterPath path; |
595 | path.moveTo(x: ml + rc, y: mt); |
596 | if (arrowAtTop && arrowAtLeft) { |
597 | if (showArrow) { |
598 | path.lineTo(x: ml + ao, y: mt); |
599 | path.lineTo(x: ml + ao, y: mt - ah); |
600 | path.lineTo(x: ml + ao + aw, y: mt); |
601 | } |
602 | move(ax: qMax(a: pos.x() - ao, b: screenRect.left() + 2), ay: pos.y()); |
603 | } else if (arrowAtTop && !arrowAtLeft) { |
604 | if (showArrow) { |
605 | path.lineTo(x: mr - ao - aw, y: mt); |
606 | path.lineTo(x: mr - ao, y: mt - ah); |
607 | path.lineTo(x: mr - ao, y: mt); |
608 | } |
609 | move(ax: qMin(a: pos.x() - sh.width() + ao, b: screenRect.right() - sh.width() - 2), ay: pos.y()); |
610 | } |
611 | path.lineTo(x: mr - rc, y: mt); |
612 | path.arcTo(rect: QRect(mr - rc*2, mt, rc*2, rc*2), startAngle: 90, arcLength: -90); |
613 | path.lineTo(x: mr, y: mb - rc); |
614 | path.arcTo(rect: QRect(mr - rc*2, mb - rc*2, rc*2, rc*2), startAngle: 0, arcLength: -90); |
615 | if (!arrowAtTop && !arrowAtLeft) { |
616 | if (showArrow) { |
617 | path.lineTo(x: mr - ao, y: mb); |
618 | path.lineTo(x: mr - ao, y: mb + ah); |
619 | path.lineTo(x: mr - ao - aw, y: mb); |
620 | } |
621 | move(ax: qMin(a: pos.x() - sh.width() + ao, b: screenRect.right() - sh.width() - 2), |
622 | ay: pos.y() - sh.height()); |
623 | } else if (!arrowAtTop && arrowAtLeft) { |
624 | if (showArrow) { |
625 | path.lineTo(x: ao + aw, y: mb); |
626 | path.lineTo(x: ao, y: mb + ah); |
627 | path.lineTo(x: ao, y: mb); |
628 | } |
629 | move(ax: qMax(a: pos.x() - ao, b: screenRect.x() + 2), ay: pos.y() - sh.height()); |
630 | } |
631 | path.lineTo(x: ml + rc, y: mb); |
632 | path.arcTo(rect: QRect(ml, mb - rc*2, rc*2, rc*2), startAngle: -90, arcLength: -90); |
633 | path.lineTo(x: ml, y: mt + rc); |
634 | path.arcTo(rect: QRect(ml, mt, rc*2, rc*2), startAngle: 180, arcLength: -90); |
635 | |
636 | // Set the mask |
637 | QBitmap bitmap = QBitmap(sizeHint()); |
638 | bitmap.fill(fillColor: Qt::color0); |
639 | QPainter painter1(&bitmap); |
640 | painter1.setPen(QPen(Qt::color1, border)); |
641 | painter1.setBrush(QBrush(Qt::color1)); |
642 | painter1.drawPath(path); |
643 | setMask(bitmap); |
644 | |
645 | // Draw the border |
646 | pixmap = QPixmap(sz); |
647 | QPainter painter2(&pixmap); |
648 | painter2.setPen(QPen(palette().color(cr: QPalette::Window).darker(f: 160), border)); |
649 | painter2.setBrush(palette().color(cr: QPalette::Window)); |
650 | painter2.drawPath(path); |
651 | |
652 | if (msecs > 0) |
653 | timerId = startTimer(interval: msecs); |
654 | show(); |
655 | } |
656 | |
657 | void QBalloonTip::mousePressEvent(QMouseEvent *e) |
658 | { |
659 | close(); |
660 | if (e->button() == Qt::LeftButton) |
661 | emit trayIcon->messageClicked(); |
662 | } |
663 | |
664 | void QBalloonTip::timerEvent(QTimerEvent *e) |
665 | { |
666 | if (e->timerId() == timerId) { |
667 | killTimer(id: timerId); |
668 | if (!underMouse()) |
669 | close(); |
670 | return; |
671 | } |
672 | QWidget::timerEvent(event: e); |
673 | } |
674 | |
675 | ////////////////////////////////////////////////////////////////////// |
676 | void QSystemTrayIconPrivate::install_sys_qpa() |
677 | { |
678 | qpa_sys->init(); |
679 | QObject::connect(sender: qpa_sys, SIGNAL(activated(QPlatformSystemTrayIcon::ActivationReason)), |
680 | receiver: q_func(), SLOT(_q_emitActivated(QPlatformSystemTrayIcon::ActivationReason))); |
681 | QObject::connect(sender: qpa_sys, signal: &QPlatformSystemTrayIcon::messageClicked, |
682 | context: q_func(), slot: &QSystemTrayIcon::messageClicked); |
683 | updateMenu_sys(); |
684 | updateIcon_sys(); |
685 | updateToolTip_sys(); |
686 | } |
687 | |
688 | void QSystemTrayIconPrivate::remove_sys_qpa() |
689 | { |
690 | QObject::disconnect(sender: qpa_sys, SIGNAL(activated(QPlatformSystemTrayIcon::ActivationReason)), |
691 | receiver: q_func(), SLOT(_q_emitActivated(QPlatformSystemTrayIcon::ActivationReason))); |
692 | QObject::disconnect(sender: qpa_sys, signal: &QPlatformSystemTrayIcon::messageClicked, |
693 | receiver: q_func(), slot: &QSystemTrayIcon::messageClicked); |
694 | qpa_sys->cleanup(); |
695 | } |
696 | |
697 | void QSystemTrayIconPrivate::addPlatformMenu(QMenu *menu) const |
698 | { |
699 | #if QT_CONFIG(menu) |
700 | if (menu->platformMenu()) |
701 | return; // The platform menu already exists. |
702 | |
703 | // The recursion depth is the same as menu depth, so should not |
704 | // be higher than 3 levels. |
705 | const auto actions = menu->actions(); |
706 | for (QAction *action : actions) { |
707 | if (action->menu()) |
708 | addPlatformMenu(menu: action->menu()); |
709 | } |
710 | |
711 | // This menu should be processed *after* its children, otherwise |
712 | // setMenu() is not called on respective QPlatformMenuItems. |
713 | QPlatformMenu *platformMenu = qpa_sys->createMenu(); |
714 | if (platformMenu) |
715 | menu->setPlatformMenu(platformMenu); |
716 | #else |
717 | Q_UNUSED(menu); |
718 | #endif // QT_CONFIG(menu) |
719 | } |
720 | |
721 | QT_END_NAMESPACE |
722 | |
723 | #endif // QT_NO_SYSTEMTRAYICON |
724 | |
725 | #include "moc_qsystemtrayicon.cpp" |
726 | #include "moc_qsystemtrayicon_p.cpp" |
727 |
Definitions
- messageIcon2qIcon
- QSystemTrayIcon
- QSystemTrayIcon
- ~QSystemTrayIcon
- setContextMenu
- contextMenu
- setIcon
- icon
- setToolTip
- toolTip
- geometry
- setVisible
- isVisible
- event
- isSystemTrayAvailable
- supportsMessages
- showMessage
- showMessage
- _q_emitActivated
- theSolitaryBalloonTip
- showBalloon
- hideBalloon
- updateBalloonPosition
- isBalloonVisible
- QBalloonTip
- ~QBalloonTip
- paintEvent
- resizeEvent
- balloon
- mousePressEvent
- timerEvent
- install_sys_qpa
- remove_sys_qpa
Learn to use CMake with our Intro Training
Find out more