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.png The system tray on Windows XP. |
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::(QMenu *) |
162 | { |
163 | Q_D(QSystemTrayIcon); |
164 | QMenu * = d->menu.data(); |
165 | d->menu = menu; |
166 | d->updateMenu_sys(); |
167 | if (oldMenu != menu && d->qpa_sys) { |
168 | // Show the QMenu-based menu for QPA plugins that do not provide native menus |
169 | if (oldMenu && !oldMenu->platformMenu()) |
170 | QObject::disconnect(sender: d->qpa_sys, signal: &QPlatformSystemTrayIcon::contextMenuRequested, receiver: menu, zero: nullptr); |
171 | if (menu && !menu->platformMenu()) { |
172 | QObject::connect(sender: d->qpa_sys, signal: &QPlatformSystemTrayIcon::contextMenuRequested, |
173 | context: menu, |
174 | slot: [menu](QPoint globalNativePos, const QPlatformScreen *platformScreen) |
175 | { |
176 | QScreen *screen = platformScreen ? platformScreen->screen() : nullptr; |
177 | menu->popup(pos: QHighDpi::fromNativePixels(value: globalNativePos, context: screen), at: nullptr); |
178 | }); |
179 | } |
180 | } |
181 | } |
182 | |
183 | /*! |
184 | Returns the current context menu for the system tray entry. |
185 | */ |
186 | QMenu* QSystemTrayIcon::() const |
187 | { |
188 | Q_D(const QSystemTrayIcon); |
189 | return d->menu; |
190 | } |
191 | |
192 | #endif // QT_CONFIG(menu) |
193 | |
194 | /*! |
195 | \property QSystemTrayIcon::icon |
196 | \brief the system tray icon |
197 | |
198 | On Windows, the system tray icon size is 16x16; on X11, the preferred size is |
199 | 22x22. The icon will be scaled to the appropriate size as necessary. |
200 | */ |
201 | void QSystemTrayIcon::setIcon(const QIcon &icon) |
202 | { |
203 | Q_D(QSystemTrayIcon); |
204 | d->icon = icon; |
205 | d->updateIcon_sys(); |
206 | } |
207 | |
208 | QIcon QSystemTrayIcon::icon() const |
209 | { |
210 | Q_D(const QSystemTrayIcon); |
211 | return d->icon; |
212 | } |
213 | |
214 | /*! |
215 | \property QSystemTrayIcon::toolTip |
216 | \brief the tooltip for the system tray entry |
217 | |
218 | On some systems, the tooltip's length is limited. The tooltip will be truncated |
219 | if necessary. |
220 | */ |
221 | void QSystemTrayIcon::setToolTip(const QString &tooltip) |
222 | { |
223 | Q_D(QSystemTrayIcon); |
224 | d->toolTip = tooltip; |
225 | d->updateToolTip_sys(); |
226 | } |
227 | |
228 | QString QSystemTrayIcon::toolTip() const |
229 | { |
230 | Q_D(const QSystemTrayIcon); |
231 | return d->toolTip; |
232 | } |
233 | |
234 | /*! |
235 | \fn void QSystemTrayIcon::show() |
236 | |
237 | Shows the icon in the system tray. |
238 | |
239 | \sa hide(), visible |
240 | */ |
241 | |
242 | /*! |
243 | \fn void QSystemTrayIcon::hide() |
244 | |
245 | Hides the system tray entry. |
246 | |
247 | \sa show(), visible |
248 | */ |
249 | |
250 | /*! |
251 | \since 4.3 |
252 | Returns the geometry of the system tray icon in screen coordinates. |
253 | |
254 | \sa visible |
255 | */ |
256 | QRect QSystemTrayIcon::geometry() const |
257 | { |
258 | Q_D(const QSystemTrayIcon); |
259 | if (!d->visible) |
260 | return QRect(); |
261 | return d->geometry_sys(); |
262 | } |
263 | |
264 | /*! |
265 | \property QSystemTrayIcon::visible |
266 | \brief whether the system tray entry is visible |
267 | |
268 | Setting this property to true or calling show() makes the system tray icon |
269 | visible; setting this property to false or calling hide() hides it. |
270 | */ |
271 | void QSystemTrayIcon::setVisible(bool visible) |
272 | { |
273 | Q_D(QSystemTrayIcon); |
274 | if (visible == d->visible) |
275 | return; |
276 | if (Q_UNLIKELY(visible && d->icon.isNull())) |
277 | qWarning(msg: "QSystemTrayIcon::setVisible: No Icon set" ); |
278 | d->visible = visible; |
279 | if (d->visible) |
280 | d->install_sys(); |
281 | else |
282 | d->remove_sys(); |
283 | } |
284 | |
285 | bool QSystemTrayIcon::isVisible() const |
286 | { |
287 | Q_D(const QSystemTrayIcon); |
288 | return d->visible; |
289 | } |
290 | |
291 | /*! |
292 | \reimp |
293 | */ |
294 | bool QSystemTrayIcon::event(QEvent *e) |
295 | { |
296 | return QObject::event(event: e); |
297 | } |
298 | |
299 | /*! |
300 | \enum QSystemTrayIcon::ActivationReason |
301 | |
302 | This enum describes the reason the system tray was activated. |
303 | |
304 | \value Unknown Unknown reason |
305 | \value Context The context menu for the system tray entry was requested |
306 | \value DoubleClick The system tray entry was double clicked. \note On macOS, a |
307 | double click will only be emitted if no context menu is set, since the menu |
308 | opens on mouse press |
309 | \value Trigger The system tray entry was clicked |
310 | \value MiddleClick The system tray entry was clicked with the middle mouse button |
311 | |
312 | \sa activated() |
313 | */ |
314 | |
315 | /*! |
316 | \fn void QSystemTrayIcon::activated(QSystemTrayIcon::ActivationReason reason) |
317 | |
318 | This signal is emitted when the user activates the system tray icon. \a reason |
319 | specifies the reason for activation. QSystemTrayIcon::ActivationReason enumerates |
320 | the various reasons. |
321 | |
322 | \sa QSystemTrayIcon::ActivationReason |
323 | */ |
324 | |
325 | /*! |
326 | \fn void QSystemTrayIcon::messageClicked() |
327 | |
328 | This signal is emitted when the message displayed using showMessage() |
329 | was clicked by the user. |
330 | |
331 | \note We follow Microsoft Windows behavior, so the |
332 | signal is also emitted when the user clicks on a tray icon with |
333 | a balloon message displayed. |
334 | |
335 | \sa activated() |
336 | */ |
337 | |
338 | |
339 | /*! |
340 | Returns \c true if the system tray is available; otherwise returns \c false. |
341 | |
342 | If the system tray is currently unavailable but becomes available later, |
343 | QSystemTrayIcon will automatically add an entry in the system tray if it |
344 | is \l visible. |
345 | */ |
346 | |
347 | bool QSystemTrayIcon::isSystemTrayAvailable() |
348 | { |
349 | return QSystemTrayIconPrivate::isSystemTrayAvailable_sys(); |
350 | } |
351 | |
352 | /*! |
353 | Returns \c true if the system tray supports balloon messages; otherwise returns \c false. |
354 | |
355 | \sa showMessage() |
356 | */ |
357 | bool QSystemTrayIcon::supportsMessages() |
358 | { |
359 | return QSystemTrayIconPrivate::supportsMessages_sys(); |
360 | } |
361 | |
362 | /*! |
363 | \fn void QSystemTrayIcon::showMessage(const QString &title, const QString &message, MessageIcon icon, int millisecondsTimeoutHint) |
364 | \since 4.3 |
365 | |
366 | Shows a balloon message for the entry with the given \a title, \a message and |
367 | \a icon for the time specified in \a millisecondsTimeoutHint. \a title and \a message |
368 | must be plain text strings. |
369 | |
370 | Message can be clicked by the user; the messageClicked() signal will emitted when |
371 | this occurs. |
372 | |
373 | Note that display of messages are dependent on the system configuration and user |
374 | preferences, and that messages may not appear at all. Hence, it should not be |
375 | relied upon as the sole means for providing critical information. |
376 | |
377 | On Windows, the \a millisecondsTimeoutHint is usually ignored by the system |
378 | when the application has focus. |
379 | |
380 | Has been turned into a slot in Qt 5.2. |
381 | |
382 | \sa show(), supportsMessages() |
383 | */ |
384 | void QSystemTrayIcon::showMessage(const QString& title, const QString& msg, |
385 | QSystemTrayIcon::MessageIcon msgIcon, int msecs) |
386 | { |
387 | Q_D(QSystemTrayIcon); |
388 | if (d->visible) |
389 | d->showMessage_sys(title, msg, icon: messageIcon2qIcon(icon: msgIcon), msgIcon, msecs); |
390 | } |
391 | |
392 | /*! |
393 | \fn void QSystemTrayIcon::showMessage(const QString &title, const QString &message, const QIcon &icon, int millisecondsTimeoutHint) |
394 | |
395 | \overload showMessage() |
396 | |
397 | Shows a balloon message for the entry with the given \a title, \a message, |
398 | and custom icon \a icon for the time specified in \a millisecondsTimeoutHint. |
399 | |
400 | \since 5.9 |
401 | */ |
402 | void QSystemTrayIcon::showMessage(const QString &title, const QString &msg, |
403 | const QIcon &icon, int msecs) |
404 | { |
405 | Q_D(QSystemTrayIcon); |
406 | if (d->visible) |
407 | d->showMessage_sys(title, msg, icon, msgIcon: QSystemTrayIcon::NoIcon, msecs); |
408 | } |
409 | |
410 | void QSystemTrayIconPrivate::_q_emitActivated(QPlatformSystemTrayIcon::ActivationReason reason) |
411 | { |
412 | Q_Q(QSystemTrayIcon); |
413 | emit q->activated(reason: static_cast<QSystemTrayIcon::ActivationReason>(reason)); |
414 | } |
415 | |
416 | ////////////////////////////////////////////////////////////////////// |
417 | static QBalloonTip *theSolitaryBalloonTip = nullptr; |
418 | |
419 | void QBalloonTip::showBalloon(const QIcon &icon, const QString &title, |
420 | const QString &message, QSystemTrayIcon *trayIcon, |
421 | const QPoint &pos, int timeout, bool showArrow) |
422 | { |
423 | hideBalloon(); |
424 | if (message.isEmpty() && title.isEmpty()) |
425 | return; |
426 | |
427 | theSolitaryBalloonTip = new QBalloonTip(icon, title, message, trayIcon); |
428 | if (timeout < 0) |
429 | timeout = 10000; //10 s default |
430 | theSolitaryBalloonTip->balloon(pos, timeout, showArrow); |
431 | } |
432 | |
433 | void QBalloonTip::hideBalloon() |
434 | { |
435 | if (!theSolitaryBalloonTip) |
436 | return; |
437 | theSolitaryBalloonTip->hide(); |
438 | delete theSolitaryBalloonTip; |
439 | theSolitaryBalloonTip = nullptr; |
440 | } |
441 | |
442 | void QBalloonTip::updateBalloonPosition(const QPoint& pos) |
443 | { |
444 | if (!theSolitaryBalloonTip) |
445 | return; |
446 | theSolitaryBalloonTip->hide(); |
447 | theSolitaryBalloonTip->balloon(pos, 0, theSolitaryBalloonTip->showArrow); |
448 | } |
449 | |
450 | bool QBalloonTip::isBalloonVisible() |
451 | { |
452 | return theSolitaryBalloonTip; |
453 | } |
454 | |
455 | QBalloonTip::QBalloonTip(const QIcon &icon, const QString &title, |
456 | const QString &message, QSystemTrayIcon *ti) |
457 | : QWidget(nullptr, Qt::ToolTip), |
458 | trayIcon(ti), |
459 | timerId(-1), |
460 | showArrow(true) |
461 | { |
462 | setAttribute(Qt::WA_DeleteOnClose); |
463 | QObject::connect(sender: ti, SIGNAL(destroyed()), receiver: this, SLOT(close())); |
464 | |
465 | #if QT_CONFIG(label) |
466 | QLabel *titleLabel = new QLabel; |
467 | titleLabel->installEventFilter(filterObj: this); |
468 | titleLabel->setText(title); |
469 | QFont f = titleLabel->font(); |
470 | f.setBold(true); |
471 | titleLabel->setFont(f); |
472 | titleLabel->setTextFormat(Qt::PlainText); // to maintain compat with windows |
473 | #endif |
474 | |
475 | const int iconSize = 18; |
476 | const int closeButtonSize = 15; |
477 | |
478 | #if QT_CONFIG(pushbutton) |
479 | QPushButton *closeButton = new QPushButton; |
480 | closeButton->setIcon(style()->standardIcon(standardIcon: QStyle::SP_TitleBarCloseButton)); |
481 | closeButton->setIconSize(QSize(closeButtonSize, closeButtonSize)); |
482 | closeButton->setSizePolicy(hor: QSizePolicy::Fixed, ver: QSizePolicy::Fixed); |
483 | closeButton->setFixedSize(w: closeButtonSize, h: closeButtonSize); |
484 | QObject::connect(sender: closeButton, SIGNAL(clicked()), receiver: this, SLOT(close())); |
485 | #else |
486 | Q_UNUSED(closeButtonSize); |
487 | #endif |
488 | |
489 | #if QT_CONFIG(label) |
490 | QLabel *msgLabel = new QLabel; |
491 | msgLabel->installEventFilter(filterObj: this); |
492 | msgLabel->setText(message); |
493 | msgLabel->setTextFormat(Qt::PlainText); |
494 | msgLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); |
495 | |
496 | // smart size for the message label |
497 | int limit = QWidgetPrivate::availableScreenGeometry(widget: msgLabel).width() / 3; |
498 | if (msgLabel->sizeHint().width() > limit) { |
499 | msgLabel->setWordWrap(true); |
500 | if (msgLabel->sizeHint().width() > limit) { |
501 | msgLabel->d_func()->ensureTextControl(); |
502 | if (QWidgetTextControl *control = msgLabel->d_func()->control) { |
503 | QTextOption opt = control->document()->defaultTextOption(); |
504 | opt.setWrapMode(QTextOption::WrapAnywhere); |
505 | control->document()->setDefaultTextOption(opt); |
506 | } |
507 | } |
508 | // Here we allow the text being much smaller than the balloon widget |
509 | // to emulate the weird standard windows behavior. |
510 | msgLabel->setFixedSize(w: limit, h: msgLabel->heightForWidth(limit)); |
511 | } |
512 | #endif |
513 | |
514 | QGridLayout *layout = new QGridLayout; |
515 | #if QT_CONFIG(label) |
516 | if (!icon.isNull()) { |
517 | QLabel *iconLabel = new QLabel; |
518 | iconLabel->setPixmap(icon.pixmap(w: iconSize, h: iconSize)); |
519 | iconLabel->setSizePolicy(hor: QSizePolicy::Fixed, ver: QSizePolicy::Fixed); |
520 | iconLabel->setMargin(2); |
521 | layout->addWidget(iconLabel, row: 0, column: 0); |
522 | layout->addWidget(titleLabel, row: 0, column: 1); |
523 | } else { |
524 | layout->addWidget(titleLabel, row: 0, column: 0, rowSpan: 1, columnSpan: 2); |
525 | } |
526 | #endif |
527 | |
528 | #if QT_CONFIG(pushbutton) |
529 | layout->addWidget(closeButton, row: 0, column: 2); |
530 | #endif |
531 | |
532 | #if QT_CONFIG(label) |
533 | layout->addWidget(msgLabel, row: 1, column: 0, rowSpan: 1, columnSpan: 3); |
534 | #endif |
535 | layout->setSizeConstraint(QLayout::SetFixedSize); |
536 | layout->setContentsMargins(left: 3, top: 3, right: 3, bottom: 3); |
537 | setLayout(layout); |
538 | |
539 | QPalette pal = palette(); |
540 | pal.setColor(acr: QPalette::Window, acolor: QColor(0xff, 0xff, 0xe1)); |
541 | pal.setColor(acr: QPalette::WindowText, acolor: Qt::black); |
542 | setPalette(pal); |
543 | } |
544 | |
545 | QBalloonTip::~QBalloonTip() |
546 | { |
547 | theSolitaryBalloonTip = nullptr; |
548 | } |
549 | |
550 | void QBalloonTip::paintEvent(QPaintEvent *) |
551 | { |
552 | QPainter painter(this); |
553 | painter.drawPixmap(r: rect(), pm: pixmap); |
554 | } |
555 | |
556 | void QBalloonTip::resizeEvent(QResizeEvent *ev) |
557 | { |
558 | QWidget::resizeEvent(event: ev); |
559 | } |
560 | |
561 | void QBalloonTip::balloon(const QPoint& pos, int msecs, bool showArrow) |
562 | { |
563 | this->showArrow = showArrow; |
564 | QScreen *screen = QGuiApplication::screenAt(point: pos); |
565 | if (!screen) |
566 | screen = QGuiApplication::primaryScreen(); |
567 | QRect screenRect = screen->geometry(); |
568 | QSize sh = sizeHint(); |
569 | const int border = 1; |
570 | const int ah = 18, ao = 18, aw = 18, rc = 7; |
571 | bool arrowAtTop = (pos.y() + sh.height() + ah < screenRect.height()); |
572 | bool arrowAtLeft = (pos.x() + sh.width() - ao < screenRect.width()); |
573 | setContentsMargins(left: border + 3, top: border + (arrowAtTop ? ah : 0) + 2, right: border + 3, bottom: border + (arrowAtTop ? 0 : ah) + 2); |
574 | updateGeometry(); |
575 | sh = sizeHint(); |
576 | |
577 | int ml, mr, mt, mb; |
578 | QSize sz = sizeHint(); |
579 | if (!arrowAtTop) { |
580 | ml = mt = 0; |
581 | mr = sz.width() - 1; |
582 | mb = sz.height() - ah - 1; |
583 | } else { |
584 | ml = 0; |
585 | mt = ah; |
586 | mr = sz.width() - 1; |
587 | mb = sz.height() - 1; |
588 | } |
589 | |
590 | QPainterPath path; |
591 | path.moveTo(x: ml + rc, y: mt); |
592 | if (arrowAtTop && arrowAtLeft) { |
593 | if (showArrow) { |
594 | path.lineTo(x: ml + ao, y: mt); |
595 | path.lineTo(x: ml + ao, y: mt - ah); |
596 | path.lineTo(x: ml + ao + aw, y: mt); |
597 | } |
598 | move(ax: qMax(a: pos.x() - ao, b: screenRect.left() + 2), ay: pos.y()); |
599 | } else if (arrowAtTop && !arrowAtLeft) { |
600 | if (showArrow) { |
601 | path.lineTo(x: mr - ao - aw, y: mt); |
602 | path.lineTo(x: mr - ao, y: mt - ah); |
603 | path.lineTo(x: mr - ao, y: mt); |
604 | } |
605 | move(ax: qMin(a: pos.x() - sh.width() + ao, b: screenRect.right() - sh.width() - 2), ay: pos.y()); |
606 | } |
607 | path.lineTo(x: mr - rc, y: mt); |
608 | path.arcTo(rect: QRect(mr - rc*2, mt, rc*2, rc*2), startAngle: 90, arcLength: -90); |
609 | path.lineTo(x: mr, y: mb - rc); |
610 | path.arcTo(rect: QRect(mr - rc*2, mb - rc*2, rc*2, rc*2), startAngle: 0, arcLength: -90); |
611 | if (!arrowAtTop && !arrowAtLeft) { |
612 | if (showArrow) { |
613 | path.lineTo(x: mr - ao, y: mb); |
614 | path.lineTo(x: mr - ao, y: mb + ah); |
615 | path.lineTo(x: mr - ao - aw, y: mb); |
616 | } |
617 | move(ax: qMin(a: pos.x() - sh.width() + ao, b: screenRect.right() - sh.width() - 2), |
618 | ay: pos.y() - sh.height()); |
619 | } else if (!arrowAtTop && arrowAtLeft) { |
620 | if (showArrow) { |
621 | path.lineTo(x: ao + aw, y: mb); |
622 | path.lineTo(x: ao, y: mb + ah); |
623 | path.lineTo(x: ao, y: mb); |
624 | } |
625 | move(ax: qMax(a: pos.x() - ao, b: screenRect.x() + 2), ay: pos.y() - sh.height()); |
626 | } |
627 | path.lineTo(x: ml + rc, y: mb); |
628 | path.arcTo(rect: QRect(ml, mb - rc*2, rc*2, rc*2), startAngle: -90, arcLength: -90); |
629 | path.lineTo(x: ml, y: mt + rc); |
630 | path.arcTo(rect: QRect(ml, mt, rc*2, rc*2), startAngle: 180, arcLength: -90); |
631 | |
632 | // Set the mask |
633 | QBitmap bitmap = QBitmap(sizeHint()); |
634 | bitmap.fill(fillColor: Qt::color0); |
635 | QPainter painter1(&bitmap); |
636 | painter1.setPen(QPen(Qt::color1, border)); |
637 | painter1.setBrush(QBrush(Qt::color1)); |
638 | painter1.drawPath(path); |
639 | setMask(bitmap); |
640 | |
641 | // Draw the border |
642 | pixmap = QPixmap(sz); |
643 | QPainter painter2(&pixmap); |
644 | painter2.setPen(QPen(palette().color(cr: QPalette::Window).darker(f: 160), border)); |
645 | painter2.setBrush(palette().color(cr: QPalette::Window)); |
646 | painter2.drawPath(path); |
647 | |
648 | if (msecs > 0) |
649 | timerId = startTimer(interval: msecs); |
650 | show(); |
651 | } |
652 | |
653 | void QBalloonTip::mousePressEvent(QMouseEvent *e) |
654 | { |
655 | close(); |
656 | if (e->button() == Qt::LeftButton) |
657 | emit trayIcon->messageClicked(); |
658 | } |
659 | |
660 | void QBalloonTip::timerEvent(QTimerEvent *e) |
661 | { |
662 | if (e->timerId() == timerId) { |
663 | killTimer(id: timerId); |
664 | if (!underMouse()) |
665 | close(); |
666 | return; |
667 | } |
668 | QWidget::timerEvent(event: e); |
669 | } |
670 | |
671 | ////////////////////////////////////////////////////////////////////// |
672 | void QSystemTrayIconPrivate::install_sys_qpa() |
673 | { |
674 | qpa_sys->init(); |
675 | QObject::connect(sender: qpa_sys, SIGNAL(activated(QPlatformSystemTrayIcon::ActivationReason)), |
676 | receiver: q_func(), SLOT(_q_emitActivated(QPlatformSystemTrayIcon::ActivationReason))); |
677 | QObject::connect(sender: qpa_sys, signal: &QPlatformSystemTrayIcon::messageClicked, |
678 | context: q_func(), slot: &QSystemTrayIcon::messageClicked); |
679 | updateMenu_sys(); |
680 | updateIcon_sys(); |
681 | updateToolTip_sys(); |
682 | } |
683 | |
684 | void QSystemTrayIconPrivate::remove_sys_qpa() |
685 | { |
686 | QObject::disconnect(sender: qpa_sys, SIGNAL(activated(QPlatformSystemTrayIcon::ActivationReason)), |
687 | receiver: q_func(), SLOT(_q_emitActivated(QPlatformSystemTrayIcon::ActivationReason))); |
688 | QObject::disconnect(sender: qpa_sys, signal: &QPlatformSystemTrayIcon::messageClicked, |
689 | receiver: q_func(), slot: &QSystemTrayIcon::messageClicked); |
690 | qpa_sys->cleanup(); |
691 | } |
692 | |
693 | void QSystemTrayIconPrivate::(QMenu *) const |
694 | { |
695 | #if QT_CONFIG(menu) |
696 | if (menu->platformMenu()) |
697 | return; // The platform menu already exists. |
698 | |
699 | // The recursion depth is the same as menu depth, so should not |
700 | // be higher than 3 levels. |
701 | const auto actions = menu->actions(); |
702 | for (QAction *action : actions) { |
703 | if (action->menu()) |
704 | addPlatformMenu(menu: action->menu()); |
705 | } |
706 | |
707 | // This menu should be processed *after* its children, otherwise |
708 | // setMenu() is not called on respective QPlatformMenuItems. |
709 | QPlatformMenu * = qpa_sys->createMenu(); |
710 | if (platformMenu) |
711 | menu->setPlatformMenu(platformMenu); |
712 | #else |
713 | Q_UNUSED(menu); |
714 | #endif // QT_CONFIG(menu) |
715 | } |
716 | |
717 | QT_END_NAMESPACE |
718 | |
719 | #endif // QT_NO_SYSTEMTRAYICON |
720 | |
721 | #include "moc_qsystemtrayicon.cpp" |
722 | #include "moc_qsystemtrayicon_p.cpp" |
723 | |