File indexing completed on 2025-02-02 14:20:12
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"