File indexing completed on 2025-03-16 06:55:16
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"