File indexing completed on 2024-04-28 03:59: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 QIcon KTitleWidget::icon() const
0221 {
0222     return d->icon;
0223 }
0224 
0225 QSize KTitleWidget::iconSize() const
0226 {
0227     if (d->iconSize.isValid()) {
0228         return d->iconSize;
0229     }
0230     const int iconSizeExtent = style()->pixelMetric(QStyle::PM_MessageBoxIconSize);
0231     return QSize(iconSizeExtent, iconSizeExtent);
0232 }
0233 
0234 void KTitleWidget::setBuddy(QWidget *buddy)
0235 {
0236     d->textLabel->setBuddy(buddy);
0237 }
0238 
0239 void KTitleWidget::changeEvent(QEvent *e)
0240 {
0241     QWidget::changeEvent(e);
0242     if (e->type() == QEvent::PaletteChange || e->type() == QEvent::FontChange || e->type() == QEvent::ApplicationFontChange) {
0243         d->textLabel->setStyleSheet(d->textStyleSheet());
0244         d->commentLabel->setStyleSheet(d->commentStyleSheet());
0245         d->updatePixmap();
0246     } else if (e->type() == QEvent::StyleChange) {
0247         if (!d->iconSize.isValid()) {
0248             // relies on style's PM_MessageBoxIconSize
0249             d->updatePixmap();
0250         }
0251     }
0252 }
0253 
0254 void KTitleWidget::setText(const QString &text, Qt::Alignment alignment)
0255 {
0256     d->textLabel->setVisible(!text.isNull());
0257 
0258     if (!Qt::mightBeRichText(text)) {
0259         d->textLabel->setStyleSheet(d->textStyleSheet());
0260     }
0261 
0262     d->textLabel->setText(text);
0263     d->textLabel->setAlignment(alignment);
0264     show();
0265 }
0266 
0267 void KTitleWidget::setLevel(int level)
0268 {
0269     if (d->level == level) {
0270         return;
0271     }
0272 
0273     d->level = level;
0274 
0275     d->textLabel->setStyleSheet(d->textStyleSheet());
0276 }
0277 
0278 int KTitleWidget::level()
0279 {
0280     return d->level;
0281 }
0282 
0283 void KTitleWidget::setText(const QString &text, MessageType type)
0284 {
0285     setIcon(type);
0286     setText(text);
0287 }
0288 
0289 void KTitleWidget::setComment(const QString &comment, MessageType type)
0290 {
0291     d->commentLabel->setVisible(!comment.isNull());
0292 
0293     // TODO: should we override the current icon with the corresponding MessageType icon?
0294     d->messageType = type;
0295     d->commentLabel->setStyleSheet(d->commentStyleSheet());
0296     d->commentLabel->setText(comment);
0297     show();
0298 }
0299 
0300 void KTitleWidget::setIcon(const QIcon &icon, KTitleWidget::ImageAlignment alignment)
0301 {
0302     d->icon = icon;
0303 
0304     d->imageLabel->setVisible(!icon.isNull());
0305 
0306     d->updateIconAlignment(alignment);
0307 
0308     d->updatePixmap();
0309 }
0310 
0311 void KTitleWidget::setIconSize(const QSize &iconSize)
0312 {
0313     if (d->iconSize == iconSize) {
0314         return;
0315     }
0316 
0317     const QSize oldEffectiveIconSize = this->iconSize();
0318 
0319     d->iconSize = iconSize;
0320 
0321     if (oldEffectiveIconSize != this->iconSize()) {
0322         d->updatePixmap();
0323     }
0324 }
0325 
0326 void KTitleWidget::setIcon(MessageType type, ImageAlignment alignment)
0327 {
0328     setIcon(QIcon::fromTheme(d->iconTypeToIconName(type)), alignment);
0329 }
0330 
0331 int KTitleWidget::autoHideTimeout() const
0332 {
0333     return d->autoHideTimeout;
0334 }
0335 
0336 void KTitleWidget::setAutoHideTimeout(int msecs)
0337 {
0338     d->autoHideTimeout = msecs;
0339 
0340     if (msecs > 0) {
0341         installEventFilter(this);
0342     } else {
0343         removeEventFilter(this);
0344     }
0345 }
0346 
0347 void KTitleWidget::showEvent(QShowEvent *event)
0348 {
0349     Q_UNUSED(event)
0350     if (d->autoHideTimeout > 0) {
0351         QTimer::singleShot(d->autoHideTimeout, this, [this] {
0352             d->timeoutFinished();
0353         });
0354     }
0355 }
0356 
0357 #include "moc_ktitlewidget.cpp"