| 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::(QMenu *) |
| 162 | { |
| 163 | Q_D(QSystemTrayIcon); |
| 164 | QMenu * = 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::() 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::(QMenu *) 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 * = 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 | |