File indexing completed on 2024-04-21 04:58:21
0001 /* 0002 SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "konqstatusbarmessagelabel.h" 0008 #include <QStyle> 0009 #include <QTextDocument> 0010 0011 #include <kcolorscheme.h> 0012 #include <QIcon> 0013 #include <KLocalizedString> 0014 #include "konqdebug.h" 0015 0016 #include <QFontMetrics> 0017 #include <QPainter> 0018 #include <QPixmap> 0019 #include <QToolButton> 0020 #include <QTimer> 0021 0022 enum { GeometryTimeout = 100 }; 0023 enum { BorderGap = 2 }; 0024 0025 class KonqStatusBarMessageLabel::Private 0026 { 0027 public: 0028 Private() : 0029 m_type(Default), 0030 m_state(DefaultState), 0031 m_illumination(0), 0032 m_minTextHeight(-1), 0033 m_timer(nullptr), 0034 m_closeButton(nullptr) 0035 {} 0036 0037 bool isRichText() const 0038 { 0039 return m_text.startsWith(QLatin1String("<html>")) || m_text.startsWith(QLatin1String("<qt>")); 0040 } 0041 0042 KonqStatusBarMessageLabel::Type m_type; 0043 KonqStatusBarMessageLabel::State m_state; 0044 int m_illumination; 0045 int m_minTextHeight; 0046 QTimer *m_timer; 0047 QString m_text; 0048 QString m_defaultText; 0049 QTextDocument m_textDocument; 0050 QList<QString> m_pendingMessages; 0051 QPixmap m_pixmap; 0052 QToolButton *m_closeButton; 0053 }; 0054 0055 KonqStatusBarMessageLabel::KonqStatusBarMessageLabel(QWidget *parent) : 0056 QWidget(parent), d(new Private) 0057 { 0058 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum /*the sizeHint is the max*/); 0059 0060 d->m_timer = new QTimer(this); 0061 connect(d->m_timer, &QTimer::timeout, 0062 this, &KonqStatusBarMessageLabel::timerDone); 0063 0064 d->m_closeButton = new QToolButton(this); 0065 d->m_closeButton->setAutoRaise(true); 0066 d->m_closeButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close"))); 0067 d->m_closeButton->setToolTip(i18nc("@info", "Close")); 0068 d->m_closeButton->setAccessibleName(i18n("Close")); 0069 d->m_closeButton->hide(); 0070 connect(d->m_closeButton, &QToolButton::clicked, 0071 this, &KonqStatusBarMessageLabel::closeErrorMessage); 0072 } 0073 0074 KonqStatusBarMessageLabel::~KonqStatusBarMessageLabel() 0075 { 0076 delete d; 0077 } 0078 0079 void KonqStatusBarMessageLabel::setMessage(const QString &text, 0080 Type type) 0081 { 0082 if ((text == d->m_text) && (type == d->m_type)) { 0083 return; 0084 } 0085 0086 if (d->m_type == Error) { 0087 if (type == Error) { 0088 d->m_pendingMessages.insert(0, d->m_text); 0089 } else if ((d->m_state != DefaultState) || !d->m_pendingMessages.isEmpty()) { 0090 // a non-error message should not be shown, as there 0091 // are other pending error messages in the queue 0092 return; 0093 } 0094 } 0095 0096 d->m_text = text; 0097 d->m_type = type; 0098 0099 if (d->isRichText()) { 0100 d->m_textDocument.setTextWidth(-1); 0101 d->m_textDocument.setDefaultFont(font()); 0102 QString html = QStringLiteral("<html><font color=\""); 0103 html += palette().windowText().color().name(); 0104 html += QLatin1String("\">"); 0105 html += d->m_text; 0106 d->m_textDocument.setHtml(html); 0107 } 0108 0109 d->m_timer->stop(); 0110 d->m_illumination = 0; 0111 d->m_state = DefaultState; 0112 0113 const char *iconName = nullptr; 0114 QPixmap pixmap; 0115 switch (type) { 0116 case OperationCompleted: 0117 iconName = "dialog-ok"; 0118 // "ok" icon should probably be "dialog-success", but we don't have that icon in KDE 4.0 0119 d->m_closeButton->hide(); 0120 break; 0121 0122 case Information: 0123 iconName = "dialog-information"; 0124 d->m_closeButton->hide(); 0125 break; 0126 0127 case Error: 0128 d->m_timer->start(100); 0129 d->m_state = Illuminate; 0130 0131 updateCloseButtonPosition(); 0132 d->m_closeButton->show(); 0133 updateGeometry(); 0134 break; 0135 0136 case Default: 0137 default: 0138 d->m_closeButton->hide(); 0139 updateGeometry(); 0140 break; 0141 } 0142 0143 d->m_pixmap = (iconName == nullptr) ? QPixmap() : QIcon::fromTheme(iconName).pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize)); 0144 QTimer::singleShot(GeometryTimeout, this, SLOT(assureVisibleText())); 0145 0146 if (type == Error) { 0147 setAccessibleName(i18n("Error: %1", text)); 0148 } else { 0149 setAccessibleName(text); 0150 } 0151 0152 update(); 0153 } 0154 0155 KonqStatusBarMessageLabel::Type KonqStatusBarMessageLabel::type() const 0156 { 0157 return d->m_type; 0158 } 0159 0160 QString KonqStatusBarMessageLabel::text() const 0161 { 0162 return d->m_text; 0163 } 0164 0165 void KonqStatusBarMessageLabel::setDefaultText(const QString &text) 0166 { 0167 d->m_defaultText = text; 0168 } 0169 0170 QString KonqStatusBarMessageLabel::defaultText() const 0171 { 0172 return d->m_defaultText; 0173 } 0174 0175 void KonqStatusBarMessageLabel::setMinimumTextHeight(int min) 0176 { 0177 if (min != d->m_minTextHeight) { 0178 d->m_minTextHeight = min; 0179 setMinimumHeight(min); 0180 if (d->m_closeButton->height() > min) { 0181 d->m_closeButton->setFixedHeight(min); 0182 } 0183 } 0184 } 0185 0186 int KonqStatusBarMessageLabel::minimumTextHeight() const 0187 { 0188 return d->m_minTextHeight; 0189 } 0190 0191 void KonqStatusBarMessageLabel::paintEvent(QPaintEvent * /* event */) 0192 { 0193 QPainter painter(this); 0194 0195 if (d->m_illumination > 0) { 0196 // at this point, a: we are a second label being drawn over the already 0197 // painted status area, so we can be translucent, and b: our palette's 0198 // window color (bg only) seems to be wrong (always black) 0199 KColorScheme scheme(palette().currentColorGroup(), KColorScheme::Window); 0200 QColor backgroundColor = scheme.background(KColorScheme::NegativeBackground).color(); 0201 backgroundColor.setAlpha(qMin(255, d->m_illumination * 2)); 0202 painter.setBrush(backgroundColor); 0203 painter.setPen(Qt::NoPen); 0204 painter.drawRect(QRect(0, 0, width(), height())); 0205 } 0206 0207 // draw pixmap 0208 int x = BorderGap; 0209 const int y = (d->m_minTextHeight - d->m_pixmap.height()) / 2; 0210 0211 if (!d->m_pixmap.isNull()) { 0212 painter.drawPixmap(x, y, d->m_pixmap); 0213 x += d->m_pixmap.width() + BorderGap; 0214 } 0215 0216 // draw text 0217 0218 const QRect availTextRect(x, 0, availableTextWidth(), height()); 0219 0220 if (d->isRichText()) { 0221 const QSize sz = d->m_textDocument.size().toSize(); 0222 0223 // Vertical centering 0224 const QRect textRect = QStyle::alignedRect(Qt::LeftToRight, Qt::AlignLeft | Qt::AlignVCenter, sz, availTextRect); 0225 //qCDebug(KONQUEROR_LOG) << d->m_text << " sz=" << sz << textRect; 0226 0227 // What about wordwrap here? 0228 0229 painter.translate(textRect.left(), textRect.top()); 0230 d->m_textDocument.drawContents(&painter); 0231 } else { 0232 // plain text 0233 painter.setPen(palette().windowText().color()); 0234 int flags = Qt::AlignVCenter; 0235 if (height() > d->m_minTextHeight) { 0236 flags = flags | Qt::TextWordWrap; 0237 } 0238 painter.drawText(availTextRect, flags, d->m_text); 0239 } 0240 painter.end(); 0241 } 0242 0243 void KonqStatusBarMessageLabel::resizeEvent(QResizeEvent *event) 0244 { 0245 QWidget::resizeEvent(event); 0246 updateCloseButtonPosition(); 0247 QTimer::singleShot(GeometryTimeout, this, SLOT(assureVisibleText())); 0248 } 0249 0250 void KonqStatusBarMessageLabel::timerDone() 0251 { 0252 switch (d->m_state) { 0253 case Illuminate: { 0254 // increase the illumination 0255 const int illumination_max = 128; 0256 if (d->m_illumination < illumination_max) { 0257 d->m_illumination += 32; 0258 if (d->m_illumination > illumination_max) { 0259 d->m_illumination = illumination_max; 0260 } 0261 update(); 0262 } else { 0263 d->m_state = Illuminated; 0264 d->m_timer->start(5000); 0265 } 0266 break; 0267 } 0268 0269 case Illuminated: { 0270 // start desaturation 0271 d->m_state = Desaturate; 0272 d->m_timer->start(100); 0273 break; 0274 } 0275 0276 case Desaturate: { 0277 // desaturate 0278 if (d->m_illumination > 0) { 0279 d->m_illumination -= 5; 0280 update(); 0281 } else { 0282 d->m_state = DefaultState; 0283 d->m_timer->stop(); 0284 } 0285 break; 0286 } 0287 0288 default: 0289 break; 0290 } 0291 } 0292 0293 void KonqStatusBarMessageLabel::assureVisibleText() 0294 { 0295 if (d->m_text.isEmpty()) { 0296 return; 0297 } 0298 0299 int requiredHeight = d->m_minTextHeight; 0300 if (d->m_type != Default) { 0301 // Calculate the required height of the widget thats 0302 // needed for having a fully visible text. Note that for the default 0303 // statusbar type (e. g. hover information) increasing the text height 0304 // is not wanted, as this might rearrange the layout of items. 0305 0306 QFontMetrics fontMetrics(font()); 0307 const QRect bounds(fontMetrics.boundingRect(0, 0, availableTextWidth(), height(), 0308 Qt::AlignVCenter | Qt::TextWordWrap, d->m_text)); 0309 requiredHeight = bounds.height(); 0310 if (requiredHeight < d->m_minTextHeight) { 0311 requiredHeight = d->m_minTextHeight; 0312 } 0313 } 0314 0315 // Increase/decrease the current height of the widget to the 0316 // required height. The increasing/decreasing is done in several 0317 // steps to have an animation if the height is modified 0318 // (see KonqStatusBarMessageLabel::resizeEvent()) 0319 const int gap = d->m_minTextHeight / 2; 0320 int minHeight = minimumHeight(); 0321 if (minHeight < requiredHeight) { 0322 minHeight += gap; 0323 if (minHeight > requiredHeight) { 0324 minHeight = requiredHeight; 0325 } 0326 setMinimumHeight(minHeight); 0327 updateGeometry(); 0328 } else if (minHeight > requiredHeight) { 0329 minHeight -= gap; 0330 if (minHeight < requiredHeight) { 0331 minHeight = requiredHeight; 0332 } 0333 setMinimumHeight(minHeight); 0334 updateGeometry(); 0335 } 0336 0337 updateCloseButtonPosition(); 0338 } 0339 0340 int KonqStatusBarMessageLabel::availableTextWidth() const 0341 { 0342 const int buttonWidth = (d->m_type == Error) ? 0343 d->m_closeButton->width() + BorderGap : 0; 0344 return width() - d->m_pixmap.width() - (BorderGap * 4) - buttonWidth; 0345 } 0346 0347 void KonqStatusBarMessageLabel::updateCloseButtonPosition() 0348 { 0349 const int x = width() - d->m_closeButton->width() - BorderGap; 0350 d->m_closeButton->move(x, 0); 0351 } 0352 0353 void KonqStatusBarMessageLabel::closeErrorMessage() 0354 { 0355 if (!showPendingMessage()) { 0356 d->m_state = DefaultState; 0357 setMessage(d->m_defaultText, Default); 0358 } 0359 } 0360 0361 bool KonqStatusBarMessageLabel::showPendingMessage() 0362 { 0363 if (!d->m_pendingMessages.isEmpty()) { 0364 reset(); 0365 setMessage(d->m_pendingMessages.takeFirst(), Error); 0366 return true; 0367 } 0368 return false; 0369 } 0370 0371 void KonqStatusBarMessageLabel::reset() 0372 { 0373 d->m_text.clear(); 0374 d->m_type = Default; 0375 } 0376 0377 QSize KonqStatusBarMessageLabel::sizeHint() const 0378 { 0379 return minimumSizeHint(); 0380 } 0381 0382 QSize KonqStatusBarMessageLabel::minimumSizeHint() const 0383 { 0384 const int fontHeight = fontMetrics().height(); 0385 QSize sz(100, fontHeight); 0386 if (d->m_closeButton->isVisible()) { 0387 const QSize toolButtonSize = d->m_closeButton->sizeHint(); 0388 sz = toolButtonSize.expandedTo(sz); 0389 } 0390 return sz; 0391 }