diff --git a/autotests/kmessagewidgetautotest.cpp b/autotests/kmessagewidgetautotest.cpp index ec30f01..e8d6ee0 100644 --- a/autotests/kmessagewidgetautotest.cpp +++ b/autotests/kmessagewidgetautotest.cpp @@ -1,252 +1,250 @@ /* Copyright 2014 Dominik Haumann Copyright 2017 Friedrich W. H. Kossebau This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kmessagewidgetautotest.h" #include #include #include QTEST_MAIN(KMessageWidgetTest) // KMessageWidget is currently hardcoded to a 500 ms timeline and default QTimeLine 40 ms update interval // let's have 7 updates for now, should be save const int overlappingWaitingTime = 280; #define CHECK_FULLY_VISIBLE \ QVERIFY(w.isVisible()); \ QCOMPARE(w.height(), w.sizeHint().height()); \ - QCOMPARE(w.findChild(QStringLiteral("contentWidget")) ->pos(), QPoint(0, 0)); #define CHECK_FULLY_NOT_VISIBLE \ QCOMPARE(w.height(), 0); \ - QCOMPARE(w.findChild(QStringLiteral("contentWidget")) ->pos(), QPoint(0, -w.sizeHint().height())); \ QVERIFY(!w.isVisible()); void KMessageWidgetTest::testAnimationSignals() { KMessageWidget w(QStringLiteral("Hello world")); QSignalSpy showSignalsSpy(&w, &KMessageWidget::showAnimationFinished); QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); QCOMPARE(showSignalsSpy.count(), 0); // // test: showing the message widget should emit showAnimationFinished() // exactly once after the show animation is finished // w.animatedShow(); QTRY_VERIFY(!w.isShowAnimationRunning()); QCOMPARE(showSignalsSpy.count(), 1); CHECK_FULLY_VISIBLE // // test: hiding the message widget should emit hideAnimationFinished() // exactly once after the show animation is finished // w.animatedHide(); QTRY_VERIFY(!w.isHideAnimationRunning()); QCOMPARE(hideSignalsSpy.count(), 1); CHECK_FULLY_NOT_VISIBLE } void KMessageWidgetTest::testShowOnVisible() { KMessageWidget w(QStringLiteral("Hello world")); QSignalSpy showSignalsSpy(&w, &KMessageWidget::showAnimationFinished); w.show(); CHECK_FULLY_VISIBLE // test: call show on visible w.animatedShow(); QTRY_VERIFY(!w.isShowAnimationRunning()); QCOMPARE(showSignalsSpy.count(), 1); CHECK_FULLY_VISIBLE } void KMessageWidgetTest::testHideOnInvisible() { KMessageWidget w(QStringLiteral("Hello world")); QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); // test: call hide on invisible w.animatedHide(); QTRY_VERIFY(!w.isHideAnimationRunning()); QCOMPARE(hideSignalsSpy.count(), 1); QVERIFY(! w.isVisible()); // Not CHECK_FULLY_NOT_VISIBLE because since it was never shown height is not what it would be otherwise } void KMessageWidgetTest::testOverlappingShowAndHide_data() { QTest::addColumn("delay"); QTest::newRow("instant") << false; QTest::newRow("delay") << true; } void KMessageWidgetTest::testOverlappingShowAndHide() { QFETCH(bool, delay); KMessageWidget w(QStringLiteral("Hello world")); QSignalSpy showSignalsSpy(&w, &KMessageWidget::showAnimationFinished); QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); // test: call hide while show animation still running w.animatedShow(); QVERIFY(w.isVisible()); // Not CHECK_FULLY_VISIBLE because we're not waiting for it to finish if (delay) { QTest::qWait(overlappingWaitingTime); } w.animatedHide(); QVERIFY(! w.isShowAnimationRunning()); QCOMPARE(showSignalsSpy.count(), 1); QTRY_VERIFY(!w.isHideAnimationRunning()); QCOMPARE(hideSignalsSpy.count(), 1); CHECK_FULLY_NOT_VISIBLE } void KMessageWidgetTest::testOverlappingHideAndShow_data() { QTest::addColumn("delay"); QTest::newRow("instant") << false; QTest::newRow("delay") << true; } void KMessageWidgetTest::testOverlappingHideAndShow() { QFETCH(bool, delay); KMessageWidget w(QStringLiteral("Hello world")); QSignalSpy showSignalsSpy(&w, &KMessageWidget::showAnimationFinished); QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); w.show(); CHECK_FULLY_VISIBLE // test: call show while hide animation still running w.animatedHide(); if (delay) { QTest::qWait(overlappingWaitingTime); } w.animatedShow(); QVERIFY(! w.isHideAnimationRunning()); QCOMPARE(hideSignalsSpy.count(), 1); QTRY_VERIFY(!w.isShowAnimationRunning()); QCOMPARE(showSignalsSpy.count(), 1); CHECK_FULLY_VISIBLE } void KMessageWidgetTest::testOverlappingDoubleShow_data() { QTest::addColumn("delay"); QTest::newRow("instant") << false; QTest::newRow("delay") << true; } void KMessageWidgetTest::testOverlappingDoubleShow() { QFETCH(bool, delay); KMessageWidget w(QStringLiteral("Hello world")); QSignalSpy showSignalsSpy(&w, &KMessageWidget::showAnimationFinished); // test: call show while show animation still running w.animatedShow(); if (delay) { QTest::qWait(overlappingWaitingTime); } w.animatedShow(); QTRY_VERIFY(!w.isShowAnimationRunning()); QCOMPARE(showSignalsSpy.count(), 1); CHECK_FULLY_VISIBLE } void KMessageWidgetTest::testOverlappingDoubleHide_data() { QTest::addColumn("delay"); QTest::newRow("instant") << false; QTest::newRow("delay") << true; } void KMessageWidgetTest::testOverlappingDoubleHide() { QFETCH(bool, delay); KMessageWidget w(QStringLiteral("Hello world")); QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); w.show(); // test: call hide while hide animation still running w.animatedHide(); if (delay) { QTest::qWait(overlappingWaitingTime); } w.animatedHide(); QTRY_VERIFY(!w.isHideAnimationRunning()); QCOMPARE(hideSignalsSpy.count(), 1); CHECK_FULLY_NOT_VISIBLE } void KMessageWidgetTest::testHideWithNotYetShownParent() { QWidget parent; KMessageWidget w(QStringLiteral("Hello world"), &parent); QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); // test: call hide while not yet visible w.animatedHide(); QTRY_VERIFY(!w.isHideAnimationRunning()); parent.show(); QCOMPARE(hideSignalsSpy.count(), 1); QVERIFY(! w.isVisible()); // Not CHECK_FULLY_NOT_VISIBLE because since it was never shown height is not what it would be otherwise } void KMessageWidgetTest::testNonAnimatedShowAfterAnimatedHide() { KMessageWidget w(QStringLiteral("Hello world")); QSignalSpy hideSignalsSpy(&w, &KMessageWidget::hideAnimationFinished); w.show(); CHECK_FULLY_VISIBLE w.animatedHide(); QTRY_VERIFY(!w.isHideAnimationRunning()); QCOMPARE(hideSignalsSpy.count(), 1); CHECK_FULLY_NOT_VISIBLE w.show(); CHECK_FULLY_VISIBLE } diff --git a/src/kmessagewidget.cpp b/src/kmessagewidget.cpp index 4e2e5a7..28aaa3b 100644 --- a/src/kmessagewidget.cpp +++ b/src/kmessagewidget.cpp @@ -1,516 +1,472 @@ /* This file is part of the KDE libraries * * Copyright (c) 2011 Aurélien Gâteau * Copyright (c) 2014 Dominik Haumann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include "kmessagewidget.h" #include #include #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------- // KMessageWidgetPrivate //--------------------------------------------------------------------- + +constexpr int borderSize = 2; + class KMessageWidgetPrivate { public: void init(KMessageWidget *); KMessageWidget *q; - QFrame *content = nullptr; QLabel *iconLabel = nullptr; QLabel *textLabel = nullptr; QToolButton *closeButton = nullptr; QTimeLine *timeLine = nullptr; QIcon icon; bool ignoreShowEventDoingAnimatedShow = false; KMessageWidget::MessageType messageType; bool wordWrap; QList buttons; - QPixmap contentSnapShot; void createLayout(); - void applyStyleSheet(); - void updateSnapShot(); + void setPalette(); void updateLayout(); void slotTimeLineChanged(qreal); void slotTimeLineFinished(); - int bestContentHeight() const; }; void KMessageWidgetPrivate::init(KMessageWidget *q_ptr) { q = q_ptr; - - q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); - // Note: when changing the value 500, also update KMessageWidgetTest timeLine = new QTimeLine(500, q); QObject::connect(timeLine, SIGNAL(valueChanged(qreal)), q, SLOT(slotTimeLineChanged(qreal))); QObject::connect(timeLine, SIGNAL(finished()), q, SLOT(slotTimeLineFinished())); - content = new QFrame(q); - content->setObjectName(QStringLiteral("contentWidget")); - content->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - wordWrap = false; - iconLabel = new QLabel(content); + iconLabel = new QLabel(q); iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); iconLabel->hide(); - textLabel = new QLabel(content); + textLabel = new QLabel(q); textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); QObject::connect(textLabel, &QLabel::linkActivated, q, &KMessageWidget::linkActivated); QObject::connect(textLabel, &QLabel::linkHovered, q, &KMessageWidget::linkHovered); QAction *closeAction = new QAction(q); closeAction->setText(KMessageWidget::tr("&Close")); closeAction->setToolTip(KMessageWidget::tr("Close message")); QStyleOptionFrame opt; opt.initFrom(q); closeAction->setIcon(q->style()->standardIcon(QStyle::SP_DialogCloseButton, &opt, q)); QObject::connect(closeAction, &QAction::triggered, q, &KMessageWidget::animatedHide); - closeButton = new QToolButton(content); + closeButton = new QToolButton(q); closeButton->setAutoRaise(true); closeButton->setDefaultAction(closeAction); q->setMessageType(KMessageWidget::Information); } void KMessageWidgetPrivate::createLayout() { - delete content->layout(); - - content->resize(q->size()); + delete q->layout(); qDeleteAll(buttons); buttons.clear(); const auto actions = q->actions(); buttons.reserve(actions.size()); for (QAction *action : actions) { - QToolButton *button = new QToolButton(content); + QToolButton *button = new QToolButton(q); button->setDefaultAction(action); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); buttons.append(button); } // AutoRaise reduces visual clutter, but we don't want to turn it on if // there are other buttons, otherwise the close button will look different // from the others. closeButton->setAutoRaise(buttons.isEmpty()); - if (wordWrap) { - QGridLayout *layout = new QGridLayout(content); + QGridLayout *layout = new QGridLayout(q); // Set alignment to make sure icon does not move down if text wraps layout->addWidget(iconLabel, 0, 0, 1, 1, Qt::AlignHCenter | Qt::AlignTop); layout->addWidget(textLabel, 0, 1); if (buttons.isEmpty()) { // Use top-vertical alignment like the icon does. layout->addWidget(closeButton, 0, 2, 1, 1, Qt::AlignHCenter | Qt::AlignTop); } else { // Use an additional layout in row 1 for the buttons. QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addStretch(); for (QToolButton *button : qAsConst(buttons)) { // For some reason, calling show() is necessary if wordwrap is true, // otherwise the buttons do not show up. It is not needed if // wordwrap is false. button->show(); buttonLayout->addWidget(button); } buttonLayout->addWidget(closeButton); layout->addItem(buttonLayout, 1, 0, 1, 2); } } else { - QHBoxLayout *layout = new QHBoxLayout(content); + QHBoxLayout *layout = new QHBoxLayout(q); layout->addWidget(iconLabel); layout->addWidget(textLabel); for (QToolButton *button : qAsConst(buttons)) { layout->addWidget(button); } - layout->addWidget(closeButton); }; - + // Add bordersize to the margin so it starts from the inner border and doesn't look too cramped + q->layout()->setContentsMargins(q->layout()->contentsMargins() + borderSize); if (q->isVisible()) { - q->setFixedHeight(content->sizeHint().height()); + q->setFixedHeight(q->sizeHint().height()); } q->updateGeometry(); } -void KMessageWidgetPrivate::applyStyleSheet() +void KMessageWidgetPrivate::setPalette() { QColor bgBaseColor; // We have to hardcode colors here because KWidgetsAddons is a tier 1 framework // and therefore can't depend on any other KDE Frameworks // The following RGB color values come from the "default" scheme in kcolorscheme.cpp switch (messageType) { case KMessageWidget::Positive: bgBaseColor.setRgb(39, 174, 96); // Window: ForegroundPositive break; case KMessageWidget::Information: bgBaseColor.setRgb(61, 174, 233); // Window: ForegroundActive break; case KMessageWidget::Warning: bgBaseColor.setRgb(246, 116, 0); // Window: ForegroundNeutral break; case KMessageWidget::Error: bgBaseColor.setRgb(218, 68, 83); // Window: ForegroundNegative break; } - const qreal bgBaseColorAlpha = 0.2; - bgBaseColor.setAlphaF(bgBaseColorAlpha); - - const QPalette palette = QGuiApplication::palette(); - const QColor windowColor = palette.window().color(); - const QColor textColor = palette.text().color(); - const QColor border = bgBaseColor; - - // Generate a final background color from overlaying bgBaseColor over windowColor - const int newRed = (bgBaseColor.red() * bgBaseColorAlpha) + (windowColor.red() * (1 - bgBaseColorAlpha)); - const int newGreen = (bgBaseColor.green() * bgBaseColorAlpha) + (windowColor.green() * (1 - bgBaseColorAlpha)); - const int newBlue = (bgBaseColor.blue() * bgBaseColorAlpha) + (windowColor.blue() * (1 - bgBaseColorAlpha)); - - const QColor bgFinalColor = QColor(newRed, newGreen, newBlue); - - content->setStyleSheet( - QString::fromLatin1(".QFrame {" - "background-color: %1;" - "border-radius: 4px;" - "border: 2px solid %2;" - "margin: %3px;" - "}" - ".QLabel { color: %4; }" - ) - .arg(bgFinalColor.name()) - .arg(border.name()) - // DefaultFrameWidth returns the size of the external margin + border width. We know our border is 2px, so we subtract this from the frame normal QStyle FrameWidth to get our margin - .arg(q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, q) - 2) - .arg(textColor.name()) - ); + QPalette palette = q->palette(); + palette.setColor(QPalette::Window, bgBaseColor); + const QColor parentTextColor = (q->parentWidget() ? q->parentWidget()->palette() : qApp->palette()).color(QPalette::WindowText); + palette.setColor(QPalette::WindowText, parentTextColor); + q->setPalette(palette); + // Explicitly set the palettes of the labels because some apps use stylesheets which break the + // palette propagation + iconLabel->setPalette(palette); + textLabel->setPalette(palette); + // update the Icon in case it is recolorable + q->setIcon(icon); + q->update(); } void KMessageWidgetPrivate::updateLayout() { - if (content->layout()) { - createLayout(); - } -} - -void KMessageWidgetPrivate::updateSnapShot() -{ - // Attention: updateSnapShot calls QWidget::render(), which causes the whole - // window layouts to be activated. Calling this method from resizeEvent() - // can lead to infinite recursion, see: - // https://bugs.kde.org/show_bug.cgi?id=311336 - contentSnapShot = QPixmap(content->size() * q->devicePixelRatioF()); - contentSnapShot.setDevicePixelRatio(q->devicePixelRatioF()); - contentSnapShot.fill(Qt::transparent); - content->render(&contentSnapShot, QPoint(), QRegion(), QWidget::DrawChildren); + createLayout(); } void KMessageWidgetPrivate::slotTimeLineChanged(qreal value) { - q->setFixedHeight(qMin(value * 2, qreal(1.0)) * content->height()); + q->setFixedHeight(qMin(value * 2, qreal(1.0)) * bestContentHeight()); q->update(); } void KMessageWidgetPrivate::slotTimeLineFinished() { if (timeLine->direction() == QTimeLine::Forward) { - // Show - // We set the whole geometry here, because it may be wrong if a - // KMessageWidget is shown right when the toplevel window is created. - content->setGeometry(0, 0, q->width(), bestContentHeight()); + q->resize(q->width(), bestContentHeight()); // notify about finished animation emit q->showAnimationFinished(); } else { // hide and notify about finished animation q->hide(); emit q->hideAnimationFinished(); } } int KMessageWidgetPrivate::bestContentHeight() const { - int height = content->heightForWidth(q->width()); + int height = q->heightForWidth(q->width()); if (height == -1) { - height = content->sizeHint().height(); + height = q->sizeHint().height(); } return height; } //--------------------------------------------------------------------- // KMessageWidget //--------------------------------------------------------------------- KMessageWidget::KMessageWidget(QWidget *parent) : QFrame(parent) , d(new KMessageWidgetPrivate) { d->init(this); } KMessageWidget::KMessageWidget(const QString &text, QWidget *parent) : QFrame(parent) , d(new KMessageWidgetPrivate) { d->init(this); setText(text); } KMessageWidget::~KMessageWidget() { delete d; } QString KMessageWidget::text() const { return d->textLabel->text(); } void KMessageWidget::setText(const QString &text) { d->textLabel->setText(text); updateGeometry(); } KMessageWidget::MessageType KMessageWidget::messageType() const { return d->messageType; } void KMessageWidget::setMessageType(KMessageWidget::MessageType type) { d->messageType = type; - d->applyStyleSheet(); + d->setPalette(); } QSize KMessageWidget::sizeHint() const { ensurePolished(); - return d->content->sizeHint(); + return QFrame::sizeHint(); } QSize KMessageWidget::minimumSizeHint() const { ensurePolished(); - return d->content->minimumSizeHint(); + return QFrame::minimumSizeHint(); } bool KMessageWidget::event(QEvent *event) { - if (event->type() == QEvent::Polish && !d->content->layout()) { + if (event->type() == QEvent::Polish && !layout()) { d->createLayout(); } else if (event->type() == QEvent::PaletteChange) { - d->applyStyleSheet(); + d->setPalette(); } else if (event->type() == QEvent::Show && !d->ignoreShowEventDoingAnimatedShow) { - if ((height() != d->content->height()) || (d->content->pos().y() != 0)) { - d->content->move(0, 0); - setFixedHeight(d->content->height()); - } + setFixedHeight(d->bestContentHeight()); + } else if (event->type() == QEvent::ParentChange) { + d->setPalette(); } return QFrame::event(event); } void KMessageWidget::resizeEvent(QResizeEvent *event) { QFrame::resizeEvent(event); - if (d->timeLine->state() == QTimeLine::NotRunning) { - d->content->resize(width(), d->bestContentHeight()); + setFixedHeight(d->bestContentHeight()); } } int KMessageWidget::heightForWidth(int width) const { ensurePolished(); - return d->content->heightForWidth(width); + return QFrame::heightForWidth(width); } - +#include void KMessageWidget::paintEvent(QPaintEvent *event) { - QFrame::paintEvent(event); + Q_UNUSED(event) + QPainter painter(this); if (d->timeLine->state() == QTimeLine::Running) { - QPainter painter(this); painter.setOpacity(d->timeLine->currentValue() * d->timeLine->currentValue()); - painter.drawPixmap(0, 0, d->contentSnapShot); } + constexpr float radius = 4 * 0.6; + const QRect innerRect = rect().marginsRemoved(QMargins() + borderSize / 2); + const QColor color = palette().color(QPalette::Window); + constexpr float alpha = 0.2; + const QColor parentWindowColor = (parentWidget() ? parentWidget()->palette() : qApp->palette()).color(QPalette::Window); + const int newRed = (color.red() * alpha) + (parentWindowColor.red() * (1 - alpha)); + const int newGreen = (color.green() * alpha) + (parentWindowColor.green() * (1 - alpha)); + const int newBlue = (color.blue() * alpha) + (parentWindowColor.blue() * (1 - alpha)); + painter.setPen(QPen(color, borderSize)); + painter.setBrush(QColor(newRed, newGreen, newBlue)); + painter.setRenderHint(QPainter::Antialiasing); + painter.drawRoundedRect(innerRect, radius, radius); } bool KMessageWidget::wordWrap() const { return d->wordWrap; } void KMessageWidget::setWordWrap(bool wordWrap) { d->wordWrap = wordWrap; d->textLabel->setWordWrap(wordWrap); QSizePolicy policy = sizePolicy(); policy.setHeightForWidth(wordWrap); setSizePolicy(policy); d->updateLayout(); // Without this, when user does wordWrap -> !wordWrap -> wordWrap, a minimum // height is set, causing the widget to be too high. // Mostly visible in test programs. if (wordWrap) { setMinimumHeight(0); } } bool KMessageWidget::isCloseButtonVisible() const { return d->closeButton->isVisible(); } void KMessageWidget::setCloseButtonVisible(bool show) { d->closeButton->setVisible(show); updateGeometry(); } void KMessageWidget::addAction(QAction *action) { QFrame::addAction(action); d->updateLayout(); } void KMessageWidget::removeAction(QAction *action) { QFrame::removeAction(action); d->updateLayout(); } void KMessageWidget::animatedShow() { // Test before styleHint, as there might have been a style change while animation was running if (isHideAnimationRunning()) { d->timeLine->stop(); emit hideAnimationFinished(); } if (!style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this) || (parentWidget() && !parentWidget()->isVisible())) { show(); emit showAnimationFinished(); return; } - if (isVisible() && (d->timeLine->state() == QTimeLine::NotRunning) && (height() == d->bestContentHeight()) && (d->content->pos().y() == 0)) { + if (isVisible() && (d->timeLine->state() == QTimeLine::NotRunning) && (height() == d->bestContentHeight())) { emit showAnimationFinished(); return; } d->ignoreShowEventDoingAnimatedShow = true; show(); d->ignoreShowEventDoingAnimatedShow = false; setFixedHeight(0); - int wantedHeight = d->bestContentHeight(); - d->content->setGeometry(0, -wantedHeight, width(), wantedHeight); - - d->updateSnapShot(); d->timeLine->setDirection(QTimeLine::Forward); if (d->timeLine->state() == QTimeLine::NotRunning) { d->timeLine->start(); } } void KMessageWidget::animatedHide() { // test this before isVisible, as animatedShow might have been called directly before, // so the first timeline event is not yet done and the widget is still hidden // And before styleHint, as there might have been a style change while animation was running if (isShowAnimationRunning()) { d->timeLine->stop(); emit showAnimationFinished(); } if (!style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this)) { hide(); emit hideAnimationFinished(); return; } if (!isVisible()) { // explicitly hide it, so it stays hidden in case it is only not visible due to the parents hide(); emit hideAnimationFinished(); return; } - d->content->move(0, -d->content->height()); - d->updateSnapShot(); - d->timeLine->setDirection(QTimeLine::Backward); if (d->timeLine->state() == QTimeLine::NotRunning) { d->timeLine->start(); } } bool KMessageWidget::isHideAnimationRunning() const { return (d->timeLine->direction() == QTimeLine::Backward) && (d->timeLine->state() == QTimeLine::Running); } bool KMessageWidget::isShowAnimationRunning() const { return (d->timeLine->direction() == QTimeLine::Forward) && (d->timeLine->state() == QTimeLine::Running); } QIcon KMessageWidget::icon() const { return d->icon; } void KMessageWidget::setIcon(const QIcon &icon) { d->icon = icon; if (d->icon.isNull()) { d->iconLabel->hide(); } else { const int size = style()->pixelMetric(QStyle::PM_ToolBarIconSize); d->iconLabel->setPixmap(d->icon.pixmap(size)); d->iconLabel->show(); } } #include "moc_kmessagewidget.cpp"