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