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