File indexing completed on 2025-01-19 03:56:07

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2011-07-03
0007  * Description : A widget to provide feedback or propose opportunistic interactions
0008  *
0009  * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2011      by Aurelien Gateau <agateau at kde dot org>
0011  * SPDX-FileCopyrightText: 2014      by Dominik Haumann <dhaumann at kde dot org>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "dnotificationwidget_p.h"
0018 
0019 // Qt includes
0020 
0021 #include <QAction>
0022 #include <QEvent>
0023 #include <QGridLayout>
0024 #include <QHBoxLayout>
0025 #include <QPainter>
0026 #include <QShowEvent>
0027 #include <QStyle>
0028 
0029 // KDE includes
0030 
0031 #include <klocalizedstring.h>
0032 
0033 namespace Digikam
0034 {
0035 
0036 DNotificationWidget::Private::Private(DNotificationWidget* const q_ptr)
0037     : QObject       (q_ptr),
0038       q             (q_ptr),
0039       content       (nullptr),
0040       iconLabel     (nullptr),
0041       textLabel     (nullptr),
0042       closeButton   (nullptr),
0043       timeLine      (nullptr),
0044       timer         (nullptr),
0045       messageType   (DNotificationWidget::Information),
0046       wordWrap      (false),
0047       delay         (-1)
0048 {
0049 }
0050 
0051 DNotificationWidget::Private::~Private()
0052 {
0053 }
0054 
0055 void DNotificationWidget::Private::init()
0056 {
0057     q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
0058 
0059     timeLine  = new QTimeLine(500, q);
0060     timer     = new QTimer(this);
0061     timer->setInterval(1000);
0062 
0063     connect(timeLine, SIGNAL(valueChanged(qreal)),
0064             this, SLOT(slotTimeLineChanged(qreal)));
0065 
0066     connect(timeLine, SIGNAL(finished()),
0067             this, SLOT(slotTimeLineFinished()));
0068 
0069     content   = new QFrame(q);
0070     content->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
0071 
0072     wordWrap  = false;
0073 
0074     iconLabel = new QLabel(content);
0075     iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0076     iconLabel->hide();
0077 
0078     textLabel = new QLabel(content);
0079     textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
0080     textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
0081 
0082     connect(textLabel, &QLabel::linkActivated, q,
0083             &DNotificationWidget::linkActivated);
0084 
0085     connect(textLabel, &QLabel::linkHovered, q,
0086             &DNotificationWidget::linkHovered);
0087 
0088     QAction* const closeAction = new QAction(q);
0089     closeAction->setText(i18n("&Close"));
0090     closeAction->setToolTip(i18n("Close message"));
0091     closeAction->setIcon(q->style()->standardIcon(QStyle::SP_DialogCloseButton));
0092 
0093     connect(closeAction, &QAction::triggered, q,
0094             &DNotificationWidget::animatedHide);
0095 
0096     closeButton = new QToolButton(content);
0097     closeButton->setAutoRaise(true);
0098     closeButton->setDefaultAction(closeAction);
0099 
0100     q->setMessageType(DNotificationWidget::Information);
0101 }
0102 
0103 void DNotificationWidget::Private::createLayout()
0104 {
0105     delete content->layout();
0106 
0107     content->resize(q->size());
0108 
0109     qDeleteAll(buttons);
0110     buttons.clear();
0111 
0112     Q_FOREACH (QAction* const action, q->actions())
0113     {
0114         QToolButton* const button = new QToolButton(content);
0115         button->setDefaultAction(action);
0116         button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0117         buttons.append(button);
0118     }
0119 
0120     // AutoRaise reduces visual clutter, but we don't want to turn it on if
0121     // there are other buttons, otherwise the close button will look different
0122     // from the others.
0123 
0124     closeButton->setAutoRaise(buttons.isEmpty());
0125 
0126     if (wordWrap)
0127     {
0128         QGridLayout* const layout = new QGridLayout(content);
0129 
0130         // Set alignment to make sure icon does not move down if text wraps
0131 
0132         layout->addWidget(iconLabel,  0, 0, 1, 1, Qt::AlignHCenter | Qt::AlignTop);
0133         layout->addWidget(textLabel,  0, 1);
0134 
0135         QHBoxLayout* const buttonLayout = new QHBoxLayout;
0136         buttonLayout->addStretch();
0137 
0138         Q_FOREACH (QToolButton* const button, buttons)
0139         {
0140             // For some reason, calling show() is necessary if wordwrap is true,
0141             // otherwise the buttons do not show up. It is not needed if
0142             // wordwrap is false.
0143 
0144             button->show();
0145             buttonLayout->addWidget(button);
0146         }
0147 
0148         buttonLayout->addWidget(closeButton);
0149         layout->addItem(buttonLayout, 1, 0, 1, 2);
0150     }
0151     else
0152     {
0153         QHBoxLayout* const layout = new QHBoxLayout(content);
0154         layout->addWidget(iconLabel);
0155         layout->addWidget(textLabel);
0156 
0157         Q_FOREACH (QToolButton* const button, buttons)
0158         {
0159             layout->addWidget(button);
0160         }
0161 
0162         layout->addWidget(closeButton);
0163     };
0164 
0165     if (q->isVisible())
0166     {
0167         q->setFixedHeight(content->sizeHint().height());
0168     }
0169 
0170     q->updateGeometry();
0171 }
0172 
0173 void DNotificationWidget::Private::updateLayout()
0174 {
0175     if (content->layout())
0176     {
0177         createLayout();
0178     }
0179 }
0180 
0181 void DNotificationWidget::Private::updateSnapShot()
0182 {
0183     // Attention: updateSnapShot calls QWidget::render(), which causes the whole
0184     // window layouts to be activated. Calling this method from resizeEvent()
0185     // can lead to infinite recursion, see:
0186     // https://bugs.kde.org/show_bug.cgi?id=311336
0187 
0188     contentSnapShot = QPixmap(content->size() * q->devicePixelRatio());
0189     contentSnapShot.setDevicePixelRatio(q->devicePixelRatio());
0190     contentSnapShot.fill(Qt::transparent);
0191     content->render(&contentSnapShot, QPoint(), QRegion(), QWidget::DrawChildren);
0192 }
0193 
0194 void DNotificationWidget::Private::slotTimeLineChanged(qreal value)
0195 {
0196     q->setFixedHeight(qMin(value * 2, qreal(1.0)) * content->height());
0197     q->update();
0198 }
0199 
0200 void DNotificationWidget::Private::slotTimeLineFinished()
0201 {
0202     if (timeLine->direction() == QTimeLine::Forward)
0203     {
0204         // Show
0205         // We set the whole geometry here, because it may be wrong if a
0206         // DNotificationWidget is shown right when the toplevel window is created.
0207 
0208         content->setGeometry(0, 0, q->width(), bestContentHeight());
0209 
0210         // notify about finished animation
0211 
0212         Q_EMIT q->showAnimationFinished();
0213     }
0214     else
0215     {
0216         // hide and notify about finished animation
0217 
0218         q->hide();
0219         timer->stop();
0220         Q_EMIT q->hideAnimationFinished();
0221     }
0222 }
0223 
0224 int DNotificationWidget::Private::bestContentHeight() const
0225 {
0226     int height = content->heightForWidth(q->width());
0227 
0228     if (height == -1)
0229     {
0230         height = content->sizeHint().height();
0231     }
0232 
0233     return height;
0234 }
0235 
0236 } // namespace Digikam
0237 
0238 #include "moc_dnotificationwidget_p.cpp"