File indexing completed on 2024-04-28 15:32:15

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2007 Urs Wolfer <uwolfer@kde.org>
0004     SPDX-FileCopyrightText: 2007 Michaƫl Larouche <larouche@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "ktitlewidget.h"
0010 
0011 #include <QApplication>
0012 #include <QFrame>
0013 #include <QIcon>
0014 #include <QLabel>
0015 #include <QLayout>
0016 #include <QMouseEvent>
0017 #include <QStyle>
0018 #include <QTextDocument>
0019 #include <QTimer>
0020 
0021 class KTitleWidgetPrivate
0022 {
0023 public:
0024     KTitleWidgetPrivate(KTitleWidget *parent)
0025         : q(parent)
0026         // use Left so updateIconAlignment(ImageRight) as called by constructor triggers the default layout
0027         , iconAlignment(KTitleWidget::ImageLeft)
0028         , autoHideTimeout(0)
0029         , messageType(KTitleWidget::InfoMessage)
0030     {
0031     }
0032 
0033     QString textStyleSheet() const
0034     {
0035         qreal factor;
0036         switch (level) {
0037         case 1:
0038             factor = 1.35;
0039             break;
0040         case 2:
0041             factor = 1.20;
0042             break;
0043         case 3:
0044             factor = 1.15;
0045             break;
0046         case 4:
0047             factor = 1.10;
0048             break;
0049         default:
0050             factor = 1;
0051         }
0052         const double fontSize = QApplication::font().pointSize() * factor;
0053         return QStringLiteral("QLabel { font-size: %1pt; color: %2 }").arg(QString::number(fontSize), q->palette().color(QPalette::WindowText).name());
0054     }
0055 
0056     QString commentStyleSheet() const
0057     {
0058         QString styleSheet;
0059         switch (messageType) {
0060         // FIXME: we need the usability color styles to implement different
0061         //       yet palette appropriate colours for the different use cases!
0062         //       also .. should we include an icon here,
0063         //       perhaps using the imageLabel?
0064         case KTitleWidget::InfoMessage:
0065         case KTitleWidget::WarningMessage:
0066         case KTitleWidget::ErrorMessage:
0067             styleSheet = QStringLiteral("QLabel { color: palette(%1); background: palette(%2); }")
0068                              .arg(q->palette().color(QPalette::HighlightedText).name(), q->palette().color(QPalette::Highlight).name());
0069             break;
0070         case KTitleWidget::PlainMessage:
0071         default:
0072             break;
0073         }
0074         return styleSheet;
0075     }
0076 
0077     void updateIconAlignment(KTitleWidget::ImageAlignment newIconAlignment)
0078     {
0079         if (iconAlignment == newIconAlignment) {
0080             return;
0081         }
0082 
0083         iconAlignment = newIconAlignment;
0084 
0085         headerLayout->removeWidget(textLabel);
0086         headerLayout->removeWidget(commentLabel);
0087         headerLayout->removeWidget(imageLabel);
0088 
0089         if (iconAlignment == KTitleWidget::ImageLeft) {
0090             // swap the text and image labels around
0091             headerLayout->addWidget(imageLabel, 0, 0, 2, 1);
0092             headerLayout->addWidget(textLabel, 0, 1);
0093             headerLayout->addWidget(commentLabel, 1, 1);
0094             headerLayout->setColumnStretch(0, 0);
0095             headerLayout->setColumnStretch(1, 1);
0096         } else {
0097             headerLayout->addWidget(textLabel, 0, 0);
0098             headerLayout->addWidget(commentLabel, 1, 0);
0099             headerLayout->addWidget(imageLabel, 0, 1, 2, 1);
0100             headerLayout->setColumnStretch(1, 0);
0101             headerLayout->setColumnStretch(0, 1);
0102         }
0103     }
0104 
0105     void updatePixmap()
0106     {
0107         const QPixmap pixmap = icon.pixmap(q->iconSize());
0108         imageLabel->setPixmap(pixmap);
0109     }
0110 
0111     int level = 1;
0112     KTitleWidget *const q;
0113     QGridLayout *headerLayout;
0114     QLabel *imageLabel;
0115     QLabel *textLabel;
0116     QLabel *commentLabel;
0117     QIcon icon;
0118     QSize iconSize;
0119     KTitleWidget::ImageAlignment iconAlignment;
0120     int autoHideTimeout;
0121     KTitleWidget::MessageType messageType;
0122 
0123     /**
0124      * @brief Get the icon name from the icon type
0125      * @param type icon type from the enum
0126      * @return named icon as QString
0127      */
0128     QString iconTypeToIconName(KTitleWidget::MessageType type);
0129 
0130     void timeoutFinished()
0131     {
0132         q->setVisible(false);
0133     }
0134 };
0135 
0136 QString KTitleWidgetPrivate::iconTypeToIconName(KTitleWidget::MessageType type)
0137 {
0138     switch (type) {
0139     case KTitleWidget::InfoMessage:
0140         return QStringLiteral("dialog-information");
0141     case KTitleWidget::ErrorMessage:
0142         return QStringLiteral("dialog-error");
0143     case KTitleWidget::WarningMessage:
0144         return QStringLiteral("dialog-warning");
0145     case KTitleWidget::PlainMessage:
0146         break;
0147     }
0148 
0149     return QString();
0150 }
0151 
0152 KTitleWidget::KTitleWidget(QWidget *parent)
0153     : QWidget(parent)
0154     , d(new KTitleWidgetPrivate(this))
0155 {
0156     QFrame *titleFrame = new QFrame(this);
0157     titleFrame->setAutoFillBackground(true);
0158     titleFrame->setFrameShape(QFrame::StyledPanel);
0159     titleFrame->setFrameShadow(QFrame::Plain);
0160     titleFrame->setBackgroundRole(QPalette::Window);
0161     titleFrame->setContentsMargins(0, 0, 0, 0);
0162 
0163     // default image / text part start
0164     d->headerLayout = new QGridLayout(titleFrame);
0165     d->headerLayout->setContentsMargins(0, 0, 0, 0);
0166     d->headerLayout->setSizeConstraint(QLayout::SetFixedSize);
0167 
0168     d->textLabel = new QLabel(titleFrame);
0169     d->textLabel->setVisible(false);
0170     d->textLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
0171 
0172     d->imageLabel = new QLabel(titleFrame);
0173     d->imageLabel->setVisible(false);
0174 
0175     d->commentLabel = new QLabel(titleFrame);
0176     d->commentLabel->setVisible(false);
0177     d->commentLabel->setOpenExternalLinks(true);
0178     d->commentLabel->setWordWrap(true);
0179     d->commentLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
0180 
0181     d->updateIconAlignment(ImageRight); // make sure d->iconAlignment is left, to trigger initial layout
0182     // default image / text part end
0183 
0184     QVBoxLayout *mainLayout = new QVBoxLayout(this);
0185     mainLayout->addWidget(titleFrame);
0186     mainLayout->setContentsMargins(0, 0, 0, 0);
0187 }
0188 
0189 KTitleWidget::~KTitleWidget() = default;
0190 
0191 bool KTitleWidget::eventFilter(QObject *object, QEvent *event)
0192 {
0193     // Hide message label on click
0194     if (d->autoHideTimeout > 0 && event->type() == QEvent::MouseButtonPress) {
0195         QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
0196         if (mouseEvent && mouseEvent->button() == Qt::LeftButton) {
0197             setVisible(false);
0198             return true;
0199         }
0200     }
0201 
0202     return QWidget::eventFilter(object, event);
0203 }
0204 
0205 void KTitleWidget::setWidget(QWidget *widget)
0206 {
0207     d->headerLayout->addWidget(widget, 2, 0, 1, 2);
0208 }
0209 
0210 QString KTitleWidget::text() const
0211 {
0212     return d->textLabel->text();
0213 }
0214 
0215 QString KTitleWidget::comment() const
0216 {
0217     return d->commentLabel->text();
0218 }
0219 
0220 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 72)
0221 const QPixmap *KTitleWidget::pixmap() const
0222 {
0223     QT_WARNING_PUSH
0224     QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
0225     QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
0226     return d->imageLabel->pixmap();
0227     QT_WARNING_POP
0228 }
0229 #endif
0230 
0231 QIcon KTitleWidget::icon() const
0232 {
0233     return d->icon;
0234 }
0235 
0236 QSize KTitleWidget::iconSize() const
0237 {
0238     if (d->iconSize.isValid()) {
0239         return d->iconSize;
0240     }
0241     const int iconSizeExtent = style()->pixelMetric(QStyle::PM_MessageBoxIconSize);
0242     return QSize(iconSizeExtent, iconSizeExtent);
0243 }
0244 
0245 void KTitleWidget::setBuddy(QWidget *buddy)
0246 {
0247     d->textLabel->setBuddy(buddy);
0248 }
0249 
0250 void KTitleWidget::changeEvent(QEvent *e)
0251 {
0252     QWidget::changeEvent(e);
0253     if (e->type() == QEvent::PaletteChange || e->type() == QEvent::FontChange || e->type() == QEvent::ApplicationFontChange) {
0254         d->textLabel->setStyleSheet(d->textStyleSheet());
0255         d->commentLabel->setStyleSheet(d->commentStyleSheet());
0256         d->updatePixmap();
0257     } else if (e->type() == QEvent::StyleChange) {
0258         if (!d->iconSize.isValid()) {
0259             // relies on style's PM_MessageBoxIconSize
0260             d->updatePixmap();
0261         }
0262     }
0263 }
0264 
0265 void KTitleWidget::setText(const QString &text, Qt::Alignment alignment)
0266 {
0267     d->textLabel->setVisible(!text.isNull());
0268 
0269     if (!Qt::mightBeRichText(text)) {
0270         d->textLabel->setStyleSheet(d->textStyleSheet());
0271     }
0272 
0273     d->textLabel->setText(text);
0274     d->textLabel->setAlignment(alignment);
0275     show();
0276 }
0277 
0278 void KTitleWidget::setLevel(int level)
0279 {
0280     if (d->level == level) {
0281         return;
0282     }
0283 
0284     d->level = level;
0285 
0286     d->textLabel->setStyleSheet(d->textStyleSheet());
0287 }
0288 
0289 int KTitleWidget::level()
0290 {
0291     return d->level;
0292 }
0293 
0294 void KTitleWidget::setText(const QString &text, MessageType type)
0295 {
0296     setIcon(type);
0297     setText(text);
0298 }
0299 
0300 void KTitleWidget::setComment(const QString &comment, MessageType type)
0301 {
0302     d->commentLabel->setVisible(!comment.isNull());
0303 
0304     // TODO: should we override the current icon with the corresponding MessageType icon?
0305     d->messageType = type;
0306     d->commentLabel->setStyleSheet(d->commentStyleSheet());
0307     d->commentLabel->setText(comment);
0308     show();
0309 }
0310 
0311 void KTitleWidget::setIcon(const QIcon &icon, KTitleWidget::ImageAlignment alignment)
0312 {
0313     d->icon = icon;
0314 
0315     d->imageLabel->setVisible(!icon.isNull());
0316 
0317     d->updateIconAlignment(alignment);
0318 
0319     d->updatePixmap();
0320 }
0321 
0322 void KTitleWidget::setIconSize(const QSize &iconSize)
0323 {
0324     if (d->iconSize == iconSize) {
0325         return;
0326     }
0327 
0328     const QSize oldEffectiveIconSize = this->iconSize();
0329 
0330     d->iconSize = iconSize;
0331 
0332     if (oldEffectiveIconSize != this->iconSize()) {
0333         d->updatePixmap();
0334     }
0335 }
0336 
0337 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 72)
0338 void KTitleWidget::setPixmap(const QPixmap &pixmap, ImageAlignment alignment)
0339 {
0340     d->icon = QIcon(pixmap);
0341     d->iconSize = pixmap.size();
0342 
0343     d->imageLabel->setVisible(!d->icon.isNull());
0344 
0345     d->updateIconAlignment(alignment);
0346 
0347     d->updatePixmap();
0348 }
0349 #endif
0350 
0351 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 63)
0352 void KTitleWidget::setPixmap(const QString &icon, ImageAlignment alignment)
0353 {
0354     setIcon(QIcon::fromTheme(icon), alignment);
0355 }
0356 #endif
0357 
0358 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 63)
0359 void KTitleWidget::setPixmap(const QIcon &icon, ImageAlignment alignment)
0360 {
0361     setIcon(icon, alignment);
0362 }
0363 #endif
0364 
0365 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 72)
0366 void KTitleWidget::setPixmap(MessageType type, ImageAlignment alignment)
0367 {
0368     setIcon(type, alignment);
0369 }
0370 #endif
0371 
0372 void KTitleWidget::setIcon(MessageType type, ImageAlignment alignment)
0373 {
0374     setIcon(QIcon::fromTheme(d->iconTypeToIconName(type)), alignment);
0375 }
0376 
0377 int KTitleWidget::autoHideTimeout() const
0378 {
0379     return d->autoHideTimeout;
0380 }
0381 
0382 void KTitleWidget::setAutoHideTimeout(int msecs)
0383 {
0384     d->autoHideTimeout = msecs;
0385 
0386     if (msecs > 0) {
0387         installEventFilter(this);
0388     } else {
0389         removeEventFilter(this);
0390     }
0391 }
0392 
0393 void KTitleWidget::showEvent(QShowEvent *event)
0394 {
0395     Q_UNUSED(event)
0396     if (d->autoHideTimeout > 0) {
0397         QTimer::singleShot(d->autoHideTimeout, this, [this] {
0398             d->timeoutFinished();
0399         });
0400     }
0401 }
0402 
0403 #include "moc_ktitlewidget.cpp"