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"