[90288] | 1 | /* $Id: UINotificationCenter.cpp 104245 2024-04-09 08:03:20Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * VBox Qt GUI - UINotificationCenter class implementation.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2021-2023 Oracle and/or its affiliates.
|
---|
[90288] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[90288] | 26 | */
|
---|
| 27 |
|
---|
| 28 | /* Qt includes: */
|
---|
| 29 | #include <QApplication>
|
---|
| 30 | #include <QHBoxLayout>
|
---|
[93414] | 31 | #include <QMenu>
|
---|
[90288] | 32 | #include <QPainter>
|
---|
| 33 | #include <QPaintEvent>
|
---|
| 34 | #include <QPropertyAnimation>
|
---|
[90464] | 35 | #include <QScrollArea>
|
---|
[90288] | 36 | #include <QSignalTransition>
|
---|
| 37 | #include <QState>
|
---|
| 38 | #include <QStateMachine>
|
---|
| 39 | #include <QStyle>
|
---|
[91225] | 40 | #include <QTimer>
|
---|
[90288] | 41 | #include <QVBoxLayout>
|
---|
| 42 |
|
---|
| 43 | /* GUI includes: */
|
---|
| 44 | #include "QIToolButton.h"
|
---|
[91236] | 45 | #include "UIExtraDataManager.h"
|
---|
[90288] | 46 | #include "UIIconPool.h"
|
---|
| 47 | #include "UINotificationCenter.h"
|
---|
| 48 | #include "UINotificationObjectItem.h"
|
---|
| 49 | #include "UINotificationModel.h"
|
---|
[104245] | 50 | #include "UITranslationEventListener.h"
|
---|
[90288] | 51 |
|
---|
| 52 | /* Other VBox includes: */
|
---|
| 53 | #include "iprt/assert.h"
|
---|
| 54 |
|
---|
| 55 |
|
---|
[90484] | 56 | /** QScrollArea extension to make notification scroll-area more versatile. */
|
---|
| 57 | class UINotificationScrollArea : public QScrollArea
|
---|
| 58 | {
|
---|
| 59 | Q_OBJECT;
|
---|
| 60 |
|
---|
| 61 | public:
|
---|
| 62 |
|
---|
| 63 | /** Creates notification scroll-area passing @a pParent to the base-class. */
|
---|
| 64 | UINotificationScrollArea(QWidget *pParent = 0);
|
---|
| 65 |
|
---|
| 66 | /** Returns minimum size-hint. */
|
---|
[103982] | 67 | virtual QSize minimumSizeHint() const RT_OVERRIDE RT_FINAL;
|
---|
[90484] | 68 |
|
---|
| 69 | /** Assigns scrollable @a pWidget.
|
---|
| 70 | * @note Keep in mind that's an override, but NOT a virtual method. */
|
---|
| 71 | void setWidget(QWidget *pWidget);
|
---|
| 72 |
|
---|
| 73 | protected:
|
---|
| 74 |
|
---|
| 75 | /** Preprocesses @a pEvent for registered @a pWatched object. */
|
---|
[103982] | 76 | virtual bool eventFilter(QObject *pWatched, QEvent *pEvent) RT_OVERRIDE RT_FINAL;
|
---|
[90484] | 77 | };
|
---|
| 78 |
|
---|
| 79 |
|
---|
| 80 | /*********************************************************************************************************************************
|
---|
| 81 | * Class UINotificationScrollArea implementation. *
|
---|
| 82 | *********************************************************************************************************************************/
|
---|
| 83 |
|
---|
| 84 | UINotificationScrollArea::UINotificationScrollArea(QWidget *pParent /* = 0 */)
|
---|
| 85 | : QScrollArea(pParent)
|
---|
| 86 | {
|
---|
| 87 | setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
---|
| 88 | }
|
---|
| 89 |
|
---|
| 90 | QSize UINotificationScrollArea::minimumSizeHint() const
|
---|
| 91 | {
|
---|
| 92 | /* So, here is the logic,
|
---|
| 93 | * we are taking width from widget if it's present,
|
---|
| 94 | * while keeping height calculated by the base-class. */
|
---|
| 95 | const QSize msh = QScrollArea::minimumSizeHint();
|
---|
| 96 | return widget() ? QSize(widget()->minimumSizeHint().width(), msh.height()) : msh;
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | void UINotificationScrollArea::setWidget(QWidget *pWidget)
|
---|
| 100 | {
|
---|
| 101 | /* We'd like to listen for a new widget's events: */
|
---|
| 102 | if (widget())
|
---|
| 103 | widget()->removeEventFilter(this);
|
---|
| 104 | pWidget->installEventFilter(this);
|
---|
| 105 |
|
---|
| 106 | /* Call to base-class: */
|
---|
| 107 | QScrollArea::setWidget(pWidget);
|
---|
| 108 | }
|
---|
| 109 |
|
---|
| 110 | bool UINotificationScrollArea::eventFilter(QObject *pWatched, QEvent *pEvent)
|
---|
| 111 | {
|
---|
| 112 | /* For listened widget: */
|
---|
| 113 | if (pWatched == widget())
|
---|
| 114 | {
|
---|
| 115 | switch (pEvent->type())
|
---|
| 116 | {
|
---|
| 117 | /* We'd like to handle layout-request events: */
|
---|
| 118 | case QEvent::LayoutRequest:
|
---|
| 119 | updateGeometry();
|
---|
| 120 | break;
|
---|
| 121 | default:
|
---|
| 122 | break;
|
---|
| 123 | }
|
---|
| 124 | }
|
---|
| 125 |
|
---|
| 126 | /* Call to base-class: */
|
---|
| 127 | return QScrollArea::eventFilter(pWatched, pEvent);
|
---|
| 128 | }
|
---|
| 129 |
|
---|
| 130 |
|
---|
| 131 | /*********************************************************************************************************************************
|
---|
| 132 | * Class UINotificationCenter implementation. *
|
---|
| 133 | *********************************************************************************************************************************/
|
---|
| 134 |
|
---|
[90288] | 135 | /* static */
|
---|
| 136 | UINotificationCenter *UINotificationCenter::s_pInstance = 0;
|
---|
| 137 |
|
---|
| 138 | /* static */
|
---|
[90563] | 139 | void UINotificationCenter::create(QWidget *pParent /* = 0 */)
|
---|
[90288] | 140 | {
|
---|
| 141 | AssertReturnVoid(!s_pInstance);
|
---|
[91753] | 142 | s_pInstance = new UINotificationCenter(pParent);
|
---|
[90288] | 143 | }
|
---|
| 144 |
|
---|
| 145 | /* static */
|
---|
| 146 | void UINotificationCenter::destroy()
|
---|
| 147 | {
|
---|
| 148 | AssertPtrReturnVoid(s_pInstance);
|
---|
| 149 | delete s_pInstance;
|
---|
[91753] | 150 | s_pInstance = 0;
|
---|
[90288] | 151 | }
|
---|
| 152 |
|
---|
| 153 | /* static */
|
---|
| 154 | UINotificationCenter *UINotificationCenter::instance()
|
---|
| 155 | {
|
---|
| 156 | return s_pInstance;
|
---|
| 157 | }
|
---|
| 158 |
|
---|
[91753] | 159 | UINotificationCenter::UINotificationCenter(QWidget *pParent)
|
---|
[104245] | 160 | : QWidget(pParent)
|
---|
[91753] | 161 | , m_pModel(0)
|
---|
[93408] | 162 | , m_enmAlignment(Qt::AlignTop)
|
---|
[92400] | 163 | , m_enmOrder(Qt::AscendingOrder)
|
---|
[91753] | 164 | , m_pLayoutMain(0)
|
---|
| 165 | , m_pLayoutButtons(0)
|
---|
[92429] | 166 | , m_pButtonOpen(0)
|
---|
[92431] | 167 | , m_pButtonToggleSorting(0)
|
---|
[99184] | 168 | #ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
|
---|
[92429] | 169 | , m_pButtonKeepFinished(0)
|
---|
[99184] | 170 | #endif
|
---|
[92429] | 171 | , m_pButtonRemoveFinished(0)
|
---|
[91753] | 172 | , m_pLayoutItems(0)
|
---|
| 173 | , m_pStateMachineSliding(0)
|
---|
| 174 | , m_iAnimatedValue(0)
|
---|
| 175 | , m_pTimerOpen(0)
|
---|
[91877] | 176 | , m_fLastResult(false)
|
---|
[91753] | 177 | {
|
---|
| 178 | prepare();
|
---|
| 179 | }
|
---|
| 180 |
|
---|
| 181 | UINotificationCenter::~UINotificationCenter()
|
---|
| 182 | {
|
---|
[92646] | 183 | cleanup();
|
---|
[91753] | 184 | }
|
---|
| 185 |
|
---|
[90563] | 186 | void UINotificationCenter::setParent(QWidget *pParent)
|
---|
| 187 | {
|
---|
[90566] | 188 | /* Additionally hide if parent unset: */
|
---|
[90563] | 189 | if (!pParent)
|
---|
[90566] | 190 | setHidden(true);
|
---|
| 191 |
|
---|
| 192 | /* Uninstall filter from previous parent: */
|
---|
[90563] | 193 | if (parent())
|
---|
| 194 | parent()->removeEventFilter(this);
|
---|
[90566] | 195 |
|
---|
| 196 | /* Reparent: */
|
---|
[104245] | 197 | QWidget::setParent(pParent);
|
---|
[90566] | 198 |
|
---|
| 199 | /* Install filter to new parent: */
|
---|
[90563] | 200 | if (parent())
|
---|
| 201 | parent()->installEventFilter(this);
|
---|
[90566] | 202 |
|
---|
| 203 | /* Show only if there is something to show: */
|
---|
[90563] | 204 | if (parent())
|
---|
[90566] | 205 | setHidden(m_pModel->ids().isEmpty());
|
---|
[90563] | 206 | }
|
---|
| 207 |
|
---|
[90552] | 208 | void UINotificationCenter::invoke()
|
---|
| 209 | {
|
---|
[90627] | 210 | /* Open if center isn't opened yet: */
|
---|
[92429] | 211 | if (!m_pButtonOpen->isChecked())
|
---|
| 212 | m_pButtonOpen->animateClick();
|
---|
[90552] | 213 | }
|
---|
| 214 |
|
---|
[90288] | 215 | QUuid UINotificationCenter::append(UINotificationObject *pObject)
|
---|
| 216 | {
|
---|
[91501] | 217 | /* Sanity check: */
|
---|
[90600] | 218 | AssertPtrReturn(m_pModel, QUuid());
|
---|
[91501] | 219 | AssertPtrReturn(pObject, QUuid());
|
---|
| 220 |
|
---|
| 221 | /* Is object critical? */
|
---|
| 222 | const bool fCritical = pObject->isCritical();
|
---|
| 223 | /* Is object progress? */
|
---|
[94785] | 224 | const bool fProgress = pObject->inherits("UINotificationProgress");
|
---|
[91501] | 225 |
|
---|
| 226 | /* Handle object. Be aware it can be deleted during handling! */
|
---|
[90600] | 227 | const QUuid uId = m_pModel->appendObject(pObject);
|
---|
| 228 |
|
---|
[91225] | 229 | /* If object is critical and center isn't opened yet: */
|
---|
[92429] | 230 | if (!m_pButtonOpen->isChecked() && fCritical)
|
---|
[91225] | 231 | {
|
---|
| 232 | /* We should delay progresses for a bit: */
|
---|
[91501] | 233 | const int iDelay = fProgress ? 2000 : 0;
|
---|
[91225] | 234 | /* We should issue an open request: */
|
---|
| 235 | AssertPtrReturn(m_pTimerOpen, uId);
|
---|
| 236 | m_uOpenObjectId = uId;
|
---|
| 237 | m_pTimerOpen->start(iDelay);
|
---|
| 238 | }
|
---|
[90288] | 239 |
|
---|
[90600] | 240 | return uId;
|
---|
[90288] | 241 | }
|
---|
| 242 |
|
---|
| 243 | void UINotificationCenter::revoke(const QUuid &uId)
|
---|
| 244 | {
|
---|
| 245 | AssertReturnVoid(!uId.isNull());
|
---|
| 246 | return m_pModel->revokeObject(uId);
|
---|
| 247 | }
|
---|
| 248 |
|
---|
[91877] | 249 | bool UINotificationCenter::handleNow(UINotificationProgress *pProgress)
|
---|
[91755] | 250 | {
|
---|
| 251 | /* Check for the recursive run: */
|
---|
[91877] | 252 | AssertMsgReturn(!m_pEventLoop, ("UINotificationCenter::handleNow() is called recursively!\n"), false);
|
---|
[91755] | 253 |
|
---|
[91877] | 254 | /* Reset the result: */
|
---|
| 255 | m_fLastResult = false;
|
---|
| 256 |
|
---|
[91755] | 257 | /* Guard progress for the case
|
---|
| 258 | * it destroyed itself in his append call: */
|
---|
| 259 | QPointer<UINotificationProgress> guardProgress = pProgress;
|
---|
| 260 | connect(pProgress, &UINotificationProgress::sigProgressFinished,
|
---|
| 261 | this, &UINotificationCenter::sltHandleProgressFinished);
|
---|
| 262 | append(pProgress);
|
---|
| 263 |
|
---|
| 264 | /* Is progress still valid? */
|
---|
| 265 | if (guardProgress.isNull())
|
---|
[91877] | 266 | return m_fLastResult;
|
---|
[91844] | 267 | /* Is progress still running? */
|
---|
[92294] | 268 | if (guardProgress->isDone())
|
---|
[91877] | 269 | return m_fLastResult;
|
---|
[91755] | 270 |
|
---|
| 271 | /* Create a local event-loop: */
|
---|
| 272 | QEventLoop eventLoop;
|
---|
| 273 | m_pEventLoop = &eventLoop;
|
---|
| 274 |
|
---|
| 275 | /* Guard ourself for the case
|
---|
| 276 | * we destroyed ourself in our event-loop: */
|
---|
| 277 | QPointer<UINotificationCenter> guardThis = this;
|
---|
| 278 |
|
---|
| 279 | /* Start the blocking event-loop: */
|
---|
| 280 | eventLoop.exec();
|
---|
| 281 |
|
---|
| 282 | /* Are we still valid? */
|
---|
| 283 | if (guardThis.isNull())
|
---|
[91877] | 284 | return false;
|
---|
[91755] | 285 |
|
---|
| 286 | /* Cleanup event-loop: */
|
---|
| 287 | m_pEventLoop = 0;
|
---|
[91877] | 288 |
|
---|
| 289 | /* Return actual result: */
|
---|
| 290 | return m_fLastResult;
|
---|
[91755] | 291 | }
|
---|
| 292 |
|
---|
[99265] | 293 | bool UINotificationCenter::hasOperationsPending() const
|
---|
| 294 | {
|
---|
| 295 | return m_pEventLoop;
|
---|
| 296 | }
|
---|
| 297 |
|
---|
| 298 | void UINotificationCenter::abortOperations()
|
---|
| 299 | {
|
---|
| 300 | m_pEventLoop->exit();
|
---|
| 301 | emit sigOperationsAborted();
|
---|
| 302 | }
|
---|
| 303 |
|
---|
[104245] | 304 | void UINotificationCenter::sltRetranslateUI()
|
---|
[91236] | 305 | {
|
---|
[92429] | 306 | if (m_pButtonOpen)
|
---|
| 307 | m_pButtonOpen->setToolTip(tr("Open notification center"));
|
---|
[92431] | 308 | if (m_pButtonToggleSorting)
|
---|
| 309 | m_pButtonToggleSorting->setToolTip(tr("Toggle ascending/descending order"));
|
---|
[99184] | 310 | #ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
|
---|
[92429] | 311 | if (m_pButtonKeepFinished)
|
---|
| 312 | m_pButtonKeepFinished->setToolTip(tr("Keep finished progresses"));
|
---|
[99184] | 313 | #endif
|
---|
[92429] | 314 | if (m_pButtonRemoveFinished)
|
---|
| 315 | m_pButtonRemoveFinished->setToolTip(tr("Delete finished notifications"));
|
---|
[91236] | 316 | }
|
---|
| 317 |
|
---|
[90288] | 318 | bool UINotificationCenter::eventFilter(QObject *pObject, QEvent *pEvent)
|
---|
| 319 | {
|
---|
| 320 | /* For parent object only: */
|
---|
| 321 | if (pObject == parent())
|
---|
| 322 | {
|
---|
| 323 | /* Handle required event types: */
|
---|
| 324 | switch (pEvent->type())
|
---|
| 325 | {
|
---|
| 326 | case QEvent::Resize:
|
---|
| 327 | {
|
---|
| 328 | /* When parent being resized we want
|
---|
| 329 | * to adjust overlay accordingly. */
|
---|
| 330 | adjustGeometry();
|
---|
| 331 | break;
|
---|
| 332 | }
|
---|
| 333 | default:
|
---|
| 334 | break;
|
---|
| 335 | }
|
---|
| 336 | }
|
---|
| 337 |
|
---|
| 338 | /* Call to base-class: */
|
---|
[104245] | 339 | return QWidget::eventFilter(pObject, pEvent);
|
---|
[90288] | 340 | }
|
---|
| 341 |
|
---|
| 342 | bool UINotificationCenter::event(QEvent *pEvent)
|
---|
| 343 | {
|
---|
| 344 | /* Handle required event types: */
|
---|
| 345 | switch (pEvent->type())
|
---|
| 346 | {
|
---|
[90484] | 347 | /* When we are being asked to update layout
|
---|
| 348 | * we want to adjust overlay accordingly. */
|
---|
| 349 | case QEvent::LayoutRequest:
|
---|
| 350 | {
|
---|
| 351 | adjustGeometry();
|
---|
| 352 | break;
|
---|
| 353 | }
|
---|
[90288] | 354 | /* When we are being resized or moved we want
|
---|
| 355 | * to adjust transparency mask accordingly. */
|
---|
| 356 | case QEvent::Move:
|
---|
| 357 | case QEvent::Resize:
|
---|
| 358 | {
|
---|
| 359 | adjustMask();
|
---|
| 360 | break;
|
---|
| 361 | }
|
---|
| 362 | default:
|
---|
| 363 | break;
|
---|
| 364 | }
|
---|
| 365 |
|
---|
[90371] | 366 | /* Call to base-class: */
|
---|
[104245] | 367 | return QWidget::event(pEvent);
|
---|
[90288] | 368 | }
|
---|
| 369 |
|
---|
| 370 | void UINotificationCenter::paintEvent(QPaintEvent *pEvent)
|
---|
| 371 | {
|
---|
| 372 | /* Sanity check: */
|
---|
| 373 | AssertPtrReturnVoid(pEvent);
|
---|
| 374 |
|
---|
| 375 | /* Prepare painter: */
|
---|
| 376 | QPainter painter(this);
|
---|
| 377 |
|
---|
| 378 | /* Limit painting with incoming rectangle: */
|
---|
| 379 | painter.setClipRect(pEvent->rect());
|
---|
| 380 |
|
---|
| 381 | /* Paint background: */
|
---|
| 382 | paintBackground(&painter);
|
---|
| 383 | paintFrame(&painter);
|
---|
| 384 | }
|
---|
| 385 |
|
---|
[93408] | 386 | void UINotificationCenter::sltHandleAlignmentChange()
|
---|
| 387 | {
|
---|
| 388 | /* Update alignment: */
|
---|
| 389 | m_enmAlignment = gEDataManager->notificationCenterAlignment();
|
---|
| 390 |
|
---|
| 391 | /* Re-insert to layout: */
|
---|
| 392 | m_pLayoutMain->removeItem(m_pLayoutButtons);
|
---|
| 393 | m_pLayoutMain->insertLayout(m_enmAlignment == Qt::AlignTop ? 0 : -1, m_pLayoutButtons);
|
---|
[93413] | 394 |
|
---|
| 395 | /* Adjust mask to make sure button visible, layout should be finalized already: */
|
---|
| 396 | QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
|
---|
| 397 | adjustMask();
|
---|
[93408] | 398 | }
|
---|
| 399 |
|
---|
[92431] | 400 | void UINotificationCenter::sltIssueOrderChange()
|
---|
| 401 | {
|
---|
| 402 | const Qt::SortOrder enmSortOrder = m_pButtonToggleSorting->isChecked()
|
---|
| 403 | ? Qt::AscendingOrder
|
---|
| 404 | : Qt::DescendingOrder;
|
---|
| 405 | gEDataManager->setNotificationCenterOrder(enmSortOrder);
|
---|
| 406 | }
|
---|
| 407 |
|
---|
[92400] | 408 | void UINotificationCenter::sltHandleOrderChange()
|
---|
| 409 | {
|
---|
[92646] | 410 | /* Save new order: */
|
---|
[92400] | 411 | m_enmOrder = gEDataManager->notificationCenterOrder();
|
---|
[92646] | 412 |
|
---|
| 413 | /* Cleanup items first: */
|
---|
[101122] | 414 | cleanupItems();
|
---|
[92646] | 415 |
|
---|
| 416 | /* Populate model contents again: */
|
---|
| 417 | foreach (const QUuid &uId, m_pModel->ids())
|
---|
| 418 | {
|
---|
| 419 | UINotificationObjectItem *pItem = UINotificationItem::create(this, m_pModel->objectById(uId));
|
---|
| 420 | m_items[uId] = pItem;
|
---|
| 421 | m_pLayoutItems->insertWidget(m_enmOrder == Qt::AscendingOrder ? -1 : 0, pItem);
|
---|
| 422 | }
|
---|
| 423 |
|
---|
| 424 | /* Hide and slide away if there are no notifications to show: */
|
---|
| 425 | setHidden(m_pModel->ids().isEmpty());
|
---|
| 426 | if (m_pModel->ids().isEmpty() && m_pButtonOpen->isChecked())
|
---|
| 427 | m_pButtonOpen->toggle();
|
---|
[92400] | 428 | }
|
---|
| 429 |
|
---|
[90288] | 430 | void UINotificationCenter::sltHandleOpenButtonToggled(bool fToggled)
|
---|
| 431 | {
|
---|
| 432 | if (fToggled)
|
---|
| 433 | emit sigOpen();
|
---|
| 434 | else
|
---|
| 435 | emit sigClose();
|
---|
| 436 | }
|
---|
| 437 |
|
---|
[99184] | 438 | #ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
|
---|
[91236] | 439 | void UINotificationCenter::sltHandleKeepButtonToggled(bool fToggled)
|
---|
| 440 | {
|
---|
| 441 | gEDataManager->setKeepSuccessfullNotificationProgresses(fToggled);
|
---|
| 442 | }
|
---|
[99184] | 443 | #endif /* VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON */
|
---|
[91236] | 444 |
|
---|
[92295] | 445 | void UINotificationCenter::sltHandleRemoveFinishedButtonClicked()
|
---|
| 446 | {
|
---|
| 447 | m_pModel->revokeFinishedObjects();
|
---|
| 448 | }
|
---|
| 449 |
|
---|
[93414] | 450 | void UINotificationCenter::sltHandleOpenButtonContextMenuRequested(const QPoint &)
|
---|
| 451 | {
|
---|
| 452 | /* Create menu: */
|
---|
| 453 | QMenu menu(m_pButtonOpen);
|
---|
| 454 |
|
---|
| 455 | /* Create action: */
|
---|
| 456 | QAction action( m_enmAlignment == Qt::AlignTop
|
---|
| 457 | ? tr("Align Bottom")
|
---|
| 458 | : tr("Align Top"),
|
---|
| 459 | m_pButtonOpen);
|
---|
| 460 | menu.addAction(&action);
|
---|
| 461 |
|
---|
| 462 | /* Execute menu, check if any (single) action is clicked: */
|
---|
| 463 | QAction *pAction = menu.exec(m_pButtonOpen->mapToGlobal(QPoint(m_pButtonOpen->width(), 0)));
|
---|
| 464 | if (pAction)
|
---|
| 465 | {
|
---|
| 466 | const Qt::Alignment enmAlignment = m_enmAlignment == Qt::AlignTop
|
---|
| 467 | ? Qt::AlignBottom
|
---|
| 468 | : Qt::AlignTop;
|
---|
| 469 | gEDataManager->setNotificationCenterAlignment(enmAlignment);
|
---|
| 470 | }
|
---|
| 471 | }
|
---|
| 472 |
|
---|
[91225] | 473 | void UINotificationCenter::sltHandleOpenTimerTimeout()
|
---|
| 474 | {
|
---|
[91231] | 475 | /* Make sure it's invoked by corresponding timer only: */
|
---|
| 476 | QTimer *pTimer = qobject_cast<QTimer*>(sender());
|
---|
| 477 | AssertPtrReturnVoid(pTimer);
|
---|
| 478 | AssertReturnVoid(pTimer == m_pTimerOpen);
|
---|
| 479 |
|
---|
| 480 | /* Stop corresponding timer: */
|
---|
| 481 | m_pTimerOpen->stop();
|
---|
| 482 |
|
---|
[91225] | 483 | /* Check whether we really closed: */
|
---|
[92429] | 484 | if (m_pButtonOpen->isChecked())
|
---|
[91225] | 485 | return;
|
---|
| 486 |
|
---|
| 487 | /* Check whether message with particular ID exists: */
|
---|
| 488 | if (!m_pModel->hasObject(m_uOpenObjectId))
|
---|
| 489 | return;
|
---|
| 490 |
|
---|
[92429] | 491 | /* Toggle open button: */
|
---|
| 492 | m_pButtonOpen->animateClick();
|
---|
[91225] | 493 | }
|
---|
| 494 |
|
---|
[92646] | 495 | void UINotificationCenter::sltHandleModelItemAdded(const QUuid &uId)
|
---|
[90288] | 496 | {
|
---|
[92646] | 497 | /* Add corresponding model item representation: */
|
---|
| 498 | AssertReturnVoid(!m_items.contains(uId));
|
---|
| 499 | UINotificationObjectItem *pItem = UINotificationItem::create(this, m_pModel->objectById(uId));
|
---|
| 500 | m_items[uId] = pItem;
|
---|
| 501 | m_pLayoutItems->insertWidget(m_enmOrder == Qt::AscendingOrder ? -1 : 0, pItem);
|
---|
[90288] | 502 |
|
---|
[92646] | 503 | /* Show if there are notifications to show: */
|
---|
| 504 | setHidden(m_pModel->ids().isEmpty());
|
---|
| 505 | }
|
---|
[90464] | 506 |
|
---|
[92646] | 507 | void UINotificationCenter::sltHandleModelItemRemoved(const QUuid &uId)
|
---|
| 508 | {
|
---|
[99263] | 509 | /* Remove corresponding model item representation if present: */
|
---|
| 510 | if (m_items.contains(uId))
|
---|
| 511 | delete m_items.take(uId);
|
---|
[92646] | 512 |
|
---|
[90565] | 513 | /* Hide and slide away if there are no notifications to show: */
|
---|
| 514 | setHidden(m_pModel->ids().isEmpty());
|
---|
[92429] | 515 | if (m_pModel->ids().isEmpty() && m_pButtonOpen->isChecked())
|
---|
| 516 | m_pButtonOpen->toggle();
|
---|
[90288] | 517 | }
|
---|
| 518 |
|
---|
[91755] | 519 | void UINotificationCenter::sltHandleProgressFinished()
|
---|
| 520 | {
|
---|
[91877] | 521 | /* Acquire the sender: */
|
---|
| 522 | UINotificationProgress *pProgress = qobject_cast<UINotificationProgress*>(sender());
|
---|
| 523 | AssertPtrReturnVoid(pProgress);
|
---|
| 524 |
|
---|
| 525 | /* Set the result: */
|
---|
| 526 | m_fLastResult = pProgress->error().isNull();
|
---|
| 527 |
|
---|
[91844] | 528 | /* Break the loop if exists: */
|
---|
| 529 | if (m_pEventLoop)
|
---|
| 530 | m_pEventLoop->exit();
|
---|
[91755] | 531 | }
|
---|
| 532 |
|
---|
[90288] | 533 | void UINotificationCenter::prepare()
|
---|
| 534 | {
|
---|
[90565] | 535 | /* Hide initially: */
|
---|
| 536 | setHidden(true);
|
---|
| 537 |
|
---|
[90288] | 538 | /* Listen for parent events: */
|
---|
[90563] | 539 | if (parent())
|
---|
| 540 | parent()->installEventFilter(this);
|
---|
[90288] | 541 |
|
---|
[98928] | 542 | /* Prepare the rest of stuff: */
|
---|
| 543 | prepareModel();
|
---|
| 544 | prepareWidgets();
|
---|
| 545 | prepareStateMachineSliding();
|
---|
| 546 | prepareOpenTimer();
|
---|
| 547 |
|
---|
[93408] | 548 | /* Prepare alignment: */
|
---|
| 549 | m_enmAlignment = gEDataManager->notificationCenterAlignment();
|
---|
| 550 | connect(gEDataManager, &UIExtraDataManager::sigNotificationCenterAlignmentChange,
|
---|
| 551 | this, &UINotificationCenter::sltHandleAlignmentChange);
|
---|
[98928] | 552 | sltHandleAlignmentChange();
|
---|
[93407] | 553 | /* Prepare order: */
|
---|
| 554 | m_enmOrder = gEDataManager->notificationCenterOrder();
|
---|
| 555 | connect(gEDataManager, &UIExtraDataManager::sigNotificationCenterOrderChange,
|
---|
| 556 | this, &UINotificationCenter::sltHandleOrderChange);
|
---|
[98928] | 557 | sltHandleOrderChange();
|
---|
[93407] | 558 |
|
---|
[91236] | 559 | /* Apply language settings: */
|
---|
[104245] | 560 | sltRetranslateUI();
|
---|
| 561 |
|
---|
| 562 | connect(&translationEventListener(), &UITranslationEventListener::sigRetranslateUI,
|
---|
| 563 | this, &UINotificationCenter::sltRetranslateUI);
|
---|
[90288] | 564 | }
|
---|
| 565 |
|
---|
| 566 | void UINotificationCenter::prepareModel()
|
---|
| 567 | {
|
---|
| 568 | m_pModel = new UINotificationModel(this);
|
---|
| 569 | if (m_pModel)
|
---|
[92646] | 570 | {
|
---|
| 571 | connect(m_pModel, &UINotificationModel::sigItemAdded,
|
---|
| 572 | this, &UINotificationCenter::sltHandleModelItemAdded);
|
---|
| 573 | connect(m_pModel, &UINotificationModel::sigItemRemoved,
|
---|
| 574 | this, &UINotificationCenter::sltHandleModelItemRemoved);
|
---|
| 575 | }
|
---|
[90288] | 576 | }
|
---|
| 577 |
|
---|
| 578 | void UINotificationCenter::prepareWidgets()
|
---|
| 579 | {
|
---|
| 580 | /* Prepare main layout: */
|
---|
| 581 | m_pLayoutMain = new QVBoxLayout(this);
|
---|
| 582 | if (m_pLayoutMain)
|
---|
| 583 | {
|
---|
[93407] | 584 | /* Create container scroll-area: */
|
---|
| 585 | UINotificationScrollArea *pScrollAreaContainer = new UINotificationScrollArea(this);
|
---|
| 586 | if (pScrollAreaContainer)
|
---|
| 587 | {
|
---|
| 588 | /* Prepare container widget: */
|
---|
| 589 | QWidget *pWidgetContainer = new QWidget(pScrollAreaContainer);
|
---|
| 590 | if (pWidgetContainer)
|
---|
| 591 | {
|
---|
| 592 | /* Prepare container layout: */
|
---|
| 593 | QVBoxLayout *pLayoutContainer = new QVBoxLayout(pWidgetContainer);
|
---|
| 594 | if (pLayoutContainer)
|
---|
| 595 | {
|
---|
| 596 | pLayoutContainer->setContentsMargins(0, 0, 0, 0);
|
---|
| 597 |
|
---|
| 598 | /* Prepare items layout: */
|
---|
| 599 | m_pLayoutItems = new QVBoxLayout;
|
---|
| 600 | if (m_pLayoutItems)
|
---|
| 601 | pLayoutContainer->addLayout(m_pLayoutItems);
|
---|
| 602 |
|
---|
| 603 | pLayoutContainer->addStretch();
|
---|
| 604 | }
|
---|
| 605 |
|
---|
| 606 | /* Add to scroll-area: */
|
---|
| 607 | pScrollAreaContainer->setWidget(pWidgetContainer);
|
---|
| 608 | }
|
---|
| 609 |
|
---|
| 610 | /* Configure container scroll-area: */
|
---|
| 611 | pScrollAreaContainer->setWidgetResizable(true);
|
---|
| 612 | pScrollAreaContainer->setFrameShape(QFrame::NoFrame);
|
---|
| 613 | pScrollAreaContainer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
---|
| 614 | pScrollAreaContainer->viewport()->setAutoFillBackground(false);
|
---|
| 615 | pScrollAreaContainer->widget()->setAutoFillBackground(false);
|
---|
| 616 |
|
---|
| 617 | /* Add to layout: */
|
---|
| 618 | m_pLayoutMain->addWidget(pScrollAreaContainer);
|
---|
| 619 | }
|
---|
| 620 |
|
---|
[91236] | 621 | /* Prepare buttons layout: */
|
---|
| 622 | m_pLayoutButtons = new QHBoxLayout;
|
---|
| 623 | if (m_pLayoutButtons)
|
---|
[90288] | 624 | {
|
---|
[91236] | 625 | m_pLayoutButtons->setContentsMargins(0, 0, 0, 0);
|
---|
[90288] | 626 |
|
---|
[92429] | 627 | /* Prepare open button: */
|
---|
| 628 | m_pButtonOpen = new QIToolButton(this);
|
---|
| 629 | if (m_pButtonOpen)
|
---|
[90288] | 630 | {
|
---|
[93196] | 631 | m_pButtonOpen->setIcon(UIIconPool::iconSet(":/notification_center_16px.png"));
|
---|
[92429] | 632 | m_pButtonOpen->setCheckable(true);
|
---|
[93414] | 633 | m_pButtonOpen->setContextMenuPolicy(Qt::CustomContextMenu);
|
---|
[93413] | 634 | connect(m_pButtonOpen, &QIToolButton::toggled,
|
---|
| 635 | this, &UINotificationCenter::sltHandleOpenButtonToggled);
|
---|
[93414] | 636 | connect(m_pButtonOpen, &QIToolButton::customContextMenuRequested,
|
---|
| 637 | this, &UINotificationCenter::sltHandleOpenButtonContextMenuRequested);
|
---|
[92429] | 638 | m_pLayoutButtons->addWidget(m_pButtonOpen);
|
---|
[90288] | 639 | }
|
---|
| 640 |
|
---|
[92295] | 641 | /* Add stretch: */
|
---|
| 642 | m_pLayoutButtons->addStretch(1);
|
---|
| 643 |
|
---|
[92431] | 644 | /* Prepare toggle-sorting button: */
|
---|
| 645 | m_pButtonToggleSorting = new QIToolButton(this);
|
---|
| 646 | if (m_pButtonToggleSorting)
|
---|
| 647 | {
|
---|
[93196] | 648 | m_pButtonToggleSorting->setIcon(UIIconPool::iconSet(":/notification_center_sort_16px.png"));
|
---|
[92431] | 649 | m_pButtonToggleSorting->setCheckable(true);
|
---|
| 650 | m_pButtonToggleSorting->setChecked(gEDataManager->notificationCenterOrder() == Qt::AscendingOrder);
|
---|
| 651 | connect(m_pButtonToggleSorting, &QIToolButton::toggled, this, &UINotificationCenter::sltIssueOrderChange);
|
---|
| 652 | m_pLayoutButtons->addWidget(m_pButtonToggleSorting);
|
---|
| 653 | }
|
---|
| 654 |
|
---|
[99184] | 655 | #ifdef VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON
|
---|
[92429] | 656 | /* Prepare keep-finished button: */
|
---|
| 657 | m_pButtonKeepFinished = new QIToolButton(this);
|
---|
| 658 | if (m_pButtonKeepFinished)
|
---|
[91236] | 659 | {
|
---|
[93196] | 660 | m_pButtonKeepFinished->setIcon(UIIconPool::iconSet(":/notification_center_hold_progress_16px.png"));
|
---|
[92429] | 661 | m_pButtonKeepFinished->setCheckable(true);
|
---|
| 662 | m_pButtonKeepFinished->setChecked(gEDataManager->keepSuccessfullNotificationProgresses());
|
---|
| 663 | connect(m_pButtonKeepFinished, &QIToolButton::toggled, this, &UINotificationCenter::sltHandleKeepButtonToggled);
|
---|
| 664 | m_pLayoutButtons->addWidget(m_pButtonKeepFinished);
|
---|
[91236] | 665 | }
|
---|
[99184] | 666 | #endif /* VBOX_NOTIFICATION_CENTER_WITH_KEEP_BUTTON */
|
---|
[90288] | 667 |
|
---|
[92429] | 668 | /* Prepare remove-finished button: */
|
---|
| 669 | m_pButtonRemoveFinished = new QIToolButton(this);
|
---|
| 670 | if (m_pButtonRemoveFinished)
|
---|
[92295] | 671 | {
|
---|
[93196] | 672 | m_pButtonRemoveFinished->setIcon(UIIconPool::iconSet(":/notification_center_delete_progress_16px.png"));
|
---|
[92429] | 673 | connect(m_pButtonRemoveFinished, &QIToolButton::clicked, this, &UINotificationCenter::sltHandleRemoveFinishedButtonClicked);
|
---|
| 674 | m_pLayoutButtons->addWidget(m_pButtonRemoveFinished);
|
---|
[92295] | 675 | }
|
---|
[91236] | 676 |
|
---|
[90288] | 677 | /* Add to layout: */
|
---|
[93408] | 678 | m_pLayoutMain->insertLayout(m_enmAlignment == Qt::AlignTop ? 0 : -1, m_pLayoutButtons);
|
---|
[90288] | 679 | }
|
---|
| 680 | }
|
---|
| 681 | }
|
---|
| 682 |
|
---|
| 683 | void UINotificationCenter::prepareStateMachineSliding()
|
---|
| 684 | {
|
---|
| 685 | /* Create sliding animation state-machine: */
|
---|
| 686 | m_pStateMachineSliding = new QStateMachine(this);
|
---|
| 687 | if (m_pStateMachineSliding)
|
---|
| 688 | {
|
---|
| 689 | /* Create 'closed' state: */
|
---|
| 690 | QState *pStateClosed = new QState(m_pStateMachineSliding);
|
---|
| 691 | /* Create 'opened' state: */
|
---|
| 692 | QState *pStateOpened = new QState(m_pStateMachineSliding);
|
---|
| 693 |
|
---|
| 694 | /* Configure 'closed' state: */
|
---|
| 695 | if (pStateClosed)
|
---|
| 696 | {
|
---|
| 697 | /* When we entering closed state => we assigning animatedValue to 0: */
|
---|
| 698 | pStateClosed->assignProperty(this, "animatedValue", 0);
|
---|
| 699 |
|
---|
| 700 | /* Add state transitions: */
|
---|
| 701 | QSignalTransition *pClosedToOpened = pStateClosed->addTransition(this, SIGNAL(sigOpen()), pStateOpened);
|
---|
| 702 | if (pClosedToOpened)
|
---|
| 703 | {
|
---|
| 704 | /* Create forward animation: */
|
---|
| 705 | QPropertyAnimation *pAnimationForward = new QPropertyAnimation(this, "animatedValue", this);
|
---|
| 706 | if (pAnimationForward)
|
---|
| 707 | {
|
---|
| 708 | pAnimationForward->setEasingCurve(QEasingCurve::InCubic);
|
---|
| 709 | pAnimationForward->setDuration(300);
|
---|
| 710 | pAnimationForward->setStartValue(0);
|
---|
| 711 | pAnimationForward->setEndValue(100);
|
---|
| 712 |
|
---|
| 713 | /* Add to transition: */
|
---|
| 714 | pClosedToOpened->addAnimation(pAnimationForward);
|
---|
| 715 | }
|
---|
| 716 | }
|
---|
| 717 | }
|
---|
| 718 |
|
---|
| 719 | /* Configure 'opened' state: */
|
---|
| 720 | if (pStateOpened)
|
---|
| 721 | {
|
---|
| 722 | /* When we entering opened state => we assigning animatedValue to 100: */
|
---|
| 723 | pStateOpened->assignProperty(this, "animatedValue", 100);
|
---|
| 724 |
|
---|
| 725 | /* Add state transitions: */
|
---|
| 726 | QSignalTransition *pOpenedToClosed = pStateOpened->addTransition(this, SIGNAL(sigClose()), pStateClosed);
|
---|
| 727 | if (pOpenedToClosed)
|
---|
| 728 | {
|
---|
| 729 | /* Create backward animation: */
|
---|
| 730 | QPropertyAnimation *pAnimationBackward = new QPropertyAnimation(this, "animatedValue", this);
|
---|
| 731 | if (pAnimationBackward)
|
---|
| 732 | {
|
---|
| 733 | pAnimationBackward->setEasingCurve(QEasingCurve::InCubic);
|
---|
| 734 | pAnimationBackward->setDuration(300);
|
---|
| 735 | pAnimationBackward->setStartValue(100);
|
---|
| 736 | pAnimationBackward->setEndValue(0);
|
---|
| 737 |
|
---|
| 738 | /* Add to transition: */
|
---|
| 739 | pOpenedToClosed->addAnimation(pAnimationBackward);
|
---|
| 740 | }
|
---|
| 741 | }
|
---|
| 742 | }
|
---|
| 743 |
|
---|
| 744 | /* Initial state is 'closed': */
|
---|
| 745 | m_pStateMachineSliding->setInitialState(pStateClosed);
|
---|
| 746 | /* Start state-machine: */
|
---|
| 747 | m_pStateMachineSliding->start();
|
---|
| 748 | }
|
---|
| 749 | }
|
---|
| 750 |
|
---|
[91225] | 751 | void UINotificationCenter::prepareOpenTimer()
|
---|
| 752 | {
|
---|
| 753 | m_pTimerOpen = new QTimer(this);
|
---|
| 754 | if (m_pTimerOpen)
|
---|
| 755 | connect(m_pTimerOpen, &QTimer::timeout,
|
---|
| 756 | this, &UINotificationCenter::sltHandleOpenTimerTimeout);
|
---|
| 757 | }
|
---|
| 758 |
|
---|
[101122] | 759 | void UINotificationCenter::cleanupModel()
|
---|
[92646] | 760 | {
|
---|
[101122] | 761 | delete m_pModel;
|
---|
| 762 | m_pModel = 0;
|
---|
| 763 | }
|
---|
| 764 |
|
---|
| 765 | void UINotificationCenter::cleanupItems()
|
---|
| 766 | {
|
---|
[92646] | 767 | qDeleteAll(m_items);
|
---|
| 768 | m_items.clear();
|
---|
| 769 | }
|
---|
| 770 |
|
---|
[101122] | 771 | void UINotificationCenter::cleanup()
|
---|
| 772 | {
|
---|
| 773 | cleanupModel();
|
---|
| 774 | cleanupItems();
|
---|
| 775 | }
|
---|
| 776 |
|
---|
[90288] | 777 | void UINotificationCenter::paintBackground(QPainter *pPainter)
|
---|
| 778 | {
|
---|
| 779 | /* Acquire palette: */
|
---|
| 780 | const bool fActive = parentWidget() && parentWidget()->isActiveWindow();
|
---|
| 781 | const QPalette pal = QApplication::palette();
|
---|
| 782 |
|
---|
| 783 | /* Gather suitable color: */
|
---|
| 784 | QColor backgroundColor = pal.color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window).darker(120);
|
---|
| 785 | backgroundColor.setAlpha((double)animatedValue() / 100 * 220);
|
---|
| 786 |
|
---|
| 787 | /* Acquire pixel metric: */
|
---|
| 788 | const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
|
---|
| 789 |
|
---|
| 790 | /* Adjust rectangle: */
|
---|
| 791 | QRect rectAdjusted = rect();
|
---|
| 792 | rectAdjusted.adjust(iMetric, iMetric, 0, -iMetric);
|
---|
| 793 |
|
---|
| 794 | /* Paint background: */
|
---|
| 795 | pPainter->fillRect(rectAdjusted, backgroundColor);
|
---|
| 796 | }
|
---|
| 797 |
|
---|
| 798 | void UINotificationCenter::paintFrame(QPainter *pPainter)
|
---|
| 799 | {
|
---|
| 800 | /* Acquire palette: */
|
---|
| 801 | const bool fActive = parentWidget() && parentWidget()->isActiveWindow();
|
---|
| 802 | QPalette pal = QApplication::palette();
|
---|
| 803 |
|
---|
| 804 | /* Gather suitable colors: */
|
---|
| 805 | QColor color1 = pal.color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window).lighter(110);
|
---|
| 806 | color1.setAlpha(0);
|
---|
| 807 | QColor color2 = pal.color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window).darker(200);
|
---|
| 808 |
|
---|
| 809 | /* Acquire pixel metric: */
|
---|
| 810 | const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4;
|
---|
| 811 |
|
---|
| 812 | /* Top-left corner: */
|
---|
| 813 | QRadialGradient grad1(QPointF(iMetric, iMetric), iMetric);
|
---|
| 814 | {
|
---|
| 815 | grad1.setColorAt(0, color2);
|
---|
| 816 | grad1.setColorAt(1, color1);
|
---|
| 817 | }
|
---|
| 818 | /* Bottom-left corner: */
|
---|
| 819 | QRadialGradient grad2(QPointF(iMetric, height() - iMetric), iMetric);
|
---|
| 820 | {
|
---|
| 821 | grad2.setColorAt(0, color2);
|
---|
| 822 | grad2.setColorAt(1, color1);
|
---|
| 823 | }
|
---|
| 824 |
|
---|
| 825 | /* Top line: */
|
---|
| 826 | QLinearGradient grad3(QPointF(iMetric, 0), QPointF(iMetric, iMetric));
|
---|
| 827 | {
|
---|
| 828 | grad3.setColorAt(0, color1);
|
---|
| 829 | grad3.setColorAt(1, color2);
|
---|
| 830 | }
|
---|
| 831 | /* Bottom line: */
|
---|
| 832 | QLinearGradient grad4(QPointF(iMetric, height()), QPointF(iMetric, height() - iMetric));
|
---|
| 833 | {
|
---|
| 834 | grad4.setColorAt(0, color1);
|
---|
| 835 | grad4.setColorAt(1, color2);
|
---|
| 836 | }
|
---|
| 837 | /* Left line: */
|
---|
| 838 | QLinearGradient grad5(QPointF(0, height() - iMetric), QPointF(iMetric, height() - iMetric));
|
---|
| 839 | {
|
---|
| 840 | grad5.setColorAt(0, color1);
|
---|
| 841 | grad5.setColorAt(1, color2);
|
---|
| 842 | }
|
---|
| 843 |
|
---|
| 844 | /* Paint shape/shadow: */
|
---|
| 845 | pPainter->fillRect(QRect(0, 0, iMetric, iMetric), grad1);
|
---|
| 846 | pPainter->fillRect(QRect(0, height() - iMetric, iMetric, iMetric), grad2);
|
---|
| 847 | pPainter->fillRect(QRect(iMetric, 0, width() - iMetric, iMetric), grad3);
|
---|
| 848 | pPainter->fillRect(QRect(iMetric, height() - iMetric, width() - iMetric, iMetric), grad4);
|
---|
| 849 | pPainter->fillRect(QRect(0, iMetric, iMetric, height() - iMetric * 2), grad5);
|
---|
| 850 | }
|
---|
| 851 |
|
---|
| 852 | void UINotificationCenter::setAnimatedValue(int iValue)
|
---|
| 853 | {
|
---|
[93368] | 854 | /* Store recent value: */
|
---|
[90288] | 855 | m_iAnimatedValue = iValue;
|
---|
[93368] | 856 |
|
---|
| 857 | // WORKAROUND:
|
---|
| 858 | // Hide items if they are masked anyway.
|
---|
| 859 | // This actually shouldn't be necessary but
|
---|
| 860 | // *is* required to avoid painting artifacts.
|
---|
| 861 | foreach (QWidget *pItem, m_items.values())
|
---|
| 862 | pItem->setVisible(animatedValue());
|
---|
| 863 |
|
---|
| 864 | /* Adjust geometry: */
|
---|
[90288] | 865 | adjustGeometry();
|
---|
| 866 | }
|
---|
| 867 |
|
---|
| 868 | int UINotificationCenter::animatedValue() const
|
---|
| 869 | {
|
---|
| 870 | return m_iAnimatedValue;
|
---|
| 871 | }
|
---|
| 872 |
|
---|
| 873 | void UINotificationCenter::adjustGeometry()
|
---|
| 874 | {
|
---|
[90562] | 875 | /* Make sure parent exists: */
|
---|
| 876 | QWidget *pParent = parentWidget();
|
---|
| 877 | if (!pParent)
|
---|
| 878 | return;
|
---|
[90288] | 879 | /* Acquire parent width and height: */
|
---|
| 880 | const int iParentWidth = pParent->width();
|
---|
| 881 | const int iParentHeight = pParent->height();
|
---|
| 882 |
|
---|
| 883 | /* Acquire minimum width (includes margins by default): */
|
---|
| 884 | int iMinimumWidth = minimumSizeHint().width();
|
---|
| 885 | /* Acquire minimum button width (including margins manually): */
|
---|
| 886 | int iL, iT, iR, iB;
|
---|
| 887 | m_pLayoutMain->getContentsMargins(&iL, &iT, &iR, &iB);
|
---|
[92429] | 888 | const int iMinimumButtonWidth = m_pButtonOpen->minimumSizeHint().width() + iL + iR;
|
---|
[90288] | 889 |
|
---|
| 890 | /* Make sure we have some default width if there is no contents: */
|
---|
| 891 | iMinimumWidth = qMax(iMinimumWidth, 200);
|
---|
| 892 |
|
---|
| 893 | /* Move and resize notification-center finally: */
|
---|
| 894 | move(iParentWidth - (iMinimumButtonWidth + (double)animatedValue() / 100 * (iMinimumWidth - iMinimumButtonWidth)), 0);
|
---|
| 895 | resize(iMinimumWidth, iParentHeight);
|
---|
| 896 | }
|
---|
| 897 |
|
---|
| 898 | void UINotificationCenter::adjustMask()
|
---|
| 899 | {
|
---|
| 900 | /* We do include open-button mask only if center is opened or animated to be: */
|
---|
| 901 | QRegion region;
|
---|
[93342] | 902 | if (!animatedValue())
|
---|
[92429] | 903 | region += QRect(m_pButtonOpen->mapToParent(QPoint(0, 0)), m_pButtonOpen->size());
|
---|
[90288] | 904 | setMask(region);
|
---|
| 905 | }
|
---|
[90484] | 906 |
|
---|
| 907 |
|
---|
[92012] | 908 | /*********************************************************************************************************************************
|
---|
| 909 | * Class UINotificationReceiver implementation. *
|
---|
| 910 | *********************************************************************************************************************************/
|
---|
| 911 |
|
---|
| 912 | void UINotificationReceiver::setReceiverProperty(const QVariant &value)
|
---|
| 913 | {
|
---|
| 914 | setProperty("received_value", value);
|
---|
| 915 | }
|
---|
| 916 |
|
---|
| 917 |
|
---|
[90484] | 918 | #include "UINotificationCenter.moc"
|
---|