KDEUI
kmessagewidget.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 * 00003 * Copyright (c) 2011 Aurélien Gâteau <agateau@kde.org> 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Lesser General Public 00007 * License as published by the Free Software Foundation; either 00008 * version 2.1 of the License, or (at your option) any later version. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Lesser General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public 00016 * License along with this library; if not, write to the Free Software 00017 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00018 * 02110-1301 USA 00019 */ 00020 #include "kmessagewidget.h" 00021 00022 #include <kaction.h> 00023 #include <kcolorscheme.h> 00024 #include <kdebug.h> 00025 #include <kglobalsettings.h> 00026 #include <kicon.h> 00027 #include <kiconloader.h> 00028 #include <kstandardaction.h> 00029 00030 #include <QEvent> 00031 #include <QGridLayout> 00032 #include <QHBoxLayout> 00033 #include <QLabel> 00034 #include <QPainter> 00035 #include <QShowEvent> 00036 #include <QTimeLine> 00037 #include <QToolButton> 00038 00039 //--------------------------------------------------------------------- 00040 // KMessageWidgetPrivate 00041 //--------------------------------------------------------------------- 00042 class KMessageWidgetPrivate 00043 { 00044 public: 00045 void init(KMessageWidget*); 00046 00047 KMessageWidget* q; 00048 QFrame* content; 00049 QLabel* iconLabel; 00050 QLabel* textLabel; 00051 QToolButton* closeButton; 00052 QTimeLine* timeLine; 00053 00054 KMessageWidget::MessageType messageType; 00055 bool wordWrap; 00056 QList<QToolButton*> buttons; 00057 QPixmap contentSnapShot; 00058 00059 void createLayout(); 00060 void updateSnapShot(); 00061 void updateLayout(); 00062 void slotTimeLineChanged(qreal); 00063 void slotTimeLineFinished(); 00064 00065 int bestContentHeight() const; 00066 }; 00067 00068 void KMessageWidgetPrivate::init(KMessageWidget *q_ptr) 00069 { 00070 q = q_ptr; 00071 00072 q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); 00073 00074 timeLine = new QTimeLine(500, q); 00075 QObject::connect(timeLine, SIGNAL(valueChanged(qreal)), q, SLOT(slotTimeLineChanged(qreal))); 00076 QObject::connect(timeLine, SIGNAL(finished()), q, SLOT(slotTimeLineFinished())); 00077 00078 content = new QFrame(q); 00079 content->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 00080 00081 wordWrap = false; 00082 00083 iconLabel = new QLabel(content); 00084 iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 00085 00086 textLabel = new QLabel(content); 00087 textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 00088 textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); 00089 00090 KAction* closeAction = KStandardAction::close(q, SLOT(animatedHide()), q); 00091 00092 closeButton = new QToolButton(content); 00093 closeButton->setAutoRaise(true); 00094 closeButton->setDefaultAction(closeAction); 00095 00096 q->setMessageType(KMessageWidget::Information); 00097 } 00098 00099 void KMessageWidgetPrivate::createLayout() 00100 { 00101 delete content->layout(); 00102 00103 content->resize(q->size()); 00104 00105 qDeleteAll(buttons); 00106 buttons.clear(); 00107 00108 Q_FOREACH(QAction* action, q->actions()) { 00109 QToolButton* button = new QToolButton(content); 00110 button->setDefaultAction(action); 00111 button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); 00112 buttons.append(button); 00113 } 00114 00115 // AutoRaise reduces visual clutter, but we don't want to turn it on if 00116 // there are other buttons, otherwise the close button will look different 00117 // from the others. 00118 closeButton->setAutoRaise(buttons.isEmpty()); 00119 00120 if (wordWrap) { 00121 QGridLayout* layout = new QGridLayout(content); 00122 // Set alignment to make sure icon does not move down if text wraps 00123 layout->addWidget(iconLabel, 0, 0, 1, 1, Qt::AlignHCenter | Qt::AlignTop); 00124 layout->addWidget(textLabel, 0, 1); 00125 00126 QHBoxLayout* buttonLayout = new QHBoxLayout; 00127 buttonLayout->addStretch(); 00128 Q_FOREACH(QToolButton* button, buttons) { 00129 // For some reason, calling show() is necessary if wordwrap is true, 00130 // otherwise the buttons do not show up. It is not needed if 00131 // wordwrap is false. 00132 button->show(); 00133 buttonLayout->addWidget(button); 00134 } 00135 buttonLayout->addWidget(closeButton); 00136 layout->addItem(buttonLayout, 1, 0, 1, 2); 00137 } else { 00138 QHBoxLayout* layout = new QHBoxLayout(content); 00139 layout->addWidget(iconLabel); 00140 layout->addWidget(textLabel); 00141 00142 Q_FOREACH(QToolButton* button, buttons) { 00143 layout->addWidget(button); 00144 } 00145 00146 layout->addWidget(closeButton); 00147 }; 00148 00149 if (q->isVisible()) { 00150 q->setFixedHeight(content->sizeHint().height()); 00151 } 00152 q->updateGeometry(); 00153 } 00154 00155 void KMessageWidgetPrivate::updateLayout() 00156 { 00157 if (content->layout()) { 00158 createLayout(); 00159 } 00160 } 00161 00162 void KMessageWidgetPrivate::updateSnapShot() 00163 { 00164 // Attention: updateSnapShot calls QWidget::render(), which causes the whole 00165 // window layouts to be activated. Calling this method from resizeEvent() 00166 // can lead to infinite recursion, see: 00167 // https://bugs.kde.org/show_bug.cgi?id=311336 00168 contentSnapShot = QPixmap(content->size()); 00169 contentSnapShot.fill(Qt::transparent); 00170 content->render(&contentSnapShot, QPoint(), QRegion(), QWidget::DrawChildren); 00171 } 00172 00173 void KMessageWidgetPrivate::slotTimeLineChanged(qreal value) 00174 { 00175 q->setFixedHeight(qMin(value * 2, qreal(1.0)) * content->height()); 00176 q->update(); 00177 } 00178 00179 void KMessageWidgetPrivate::slotTimeLineFinished() 00180 { 00181 if (timeLine->direction() == QTimeLine::Forward) { 00182 // Show 00183 // We set the whole geometry here, because it may be wrong if a 00184 // KMessageWidget is shown right when the toplevel window is created. 00185 content->setGeometry(0, 0, q->width(), bestContentHeight()); 00186 } else { 00187 // Hide 00188 q->hide(); 00189 } 00190 } 00191 00192 int KMessageWidgetPrivate::bestContentHeight() const 00193 { 00194 int height = content->heightForWidth(q->width()); 00195 if (height == -1) { 00196 height = content->sizeHint().height(); 00197 } 00198 return height; 00199 } 00200 00201 00202 //--------------------------------------------------------------------- 00203 // KMessageWidget 00204 //--------------------------------------------------------------------- 00205 KMessageWidget::KMessageWidget(QWidget* parent) 00206 : QFrame(parent) 00207 , d(new KMessageWidgetPrivate) 00208 { 00209 d->init(this); 00210 } 00211 00212 KMessageWidget::KMessageWidget(const QString& text, QWidget* parent) 00213 : QFrame(parent) 00214 , d(new KMessageWidgetPrivate) 00215 { 00216 d->init(this); 00217 setText(text); 00218 } 00219 00220 KMessageWidget::~KMessageWidget() 00221 { 00222 delete d; 00223 } 00224 00225 QString KMessageWidget::text() const 00226 { 00227 return d->textLabel->text(); 00228 } 00229 00230 void KMessageWidget::setText(const QString& text) 00231 { 00232 d->textLabel->setText(text); 00233 updateGeometry(); 00234 } 00235 00236 KMessageWidget::MessageType KMessageWidget::messageType() const 00237 { 00238 return d->messageType; 00239 } 00240 00241 static void getColorsFromColorScheme(KColorScheme::BackgroundRole bgRole, QColor* bg, QColor* fg) 00242 { 00243 KColorScheme scheme(QPalette::Active, KColorScheme::Window); 00244 *bg = scheme.background(bgRole).color(); 00245 *fg = scheme.foreground().color(); 00246 } 00247 00248 void KMessageWidget::setMessageType(KMessageWidget::MessageType type) 00249 { 00250 d->messageType = type; 00251 KIcon icon; 00252 QColor bg0, bg1, bg2, border, fg; 00253 switch (type) { 00254 case Positive: 00255 icon = KIcon("dialog-ok"); 00256 getColorsFromColorScheme(KColorScheme::PositiveBackground, &bg1, &fg); 00257 break; 00258 case Information: 00259 icon = KIcon("dialog-information"); 00260 // There is no "information" background role in KColorScheme, use the 00261 // colors of highlighted items instead 00262 bg1 = palette().highlight().color(); 00263 fg = palette().highlightedText().color(); 00264 break; 00265 case Warning: 00266 icon = KIcon("dialog-warning"); 00267 getColorsFromColorScheme(KColorScheme::NeutralBackground, &bg1, &fg); 00268 break; 00269 case Error: 00270 icon = KIcon("dialog-error"); 00271 getColorsFromColorScheme(KColorScheme::NegativeBackground, &bg1, &fg); 00272 break; 00273 } 00274 00275 // Colors 00276 bg0 = bg1.lighter(110); 00277 bg2 = bg1.darker(110); 00278 border = KColorScheme::shade(bg1, KColorScheme::DarkShade); 00279 00280 d->content->setStyleSheet( 00281 QString(".QFrame {" 00282 "background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1," 00283 " stop: 0 %1," 00284 " stop: 0.1 %2," 00285 " stop: 1.0 %3);" 00286 "border-radius: 5px;" 00287 "border: 1px solid %4;" 00288 "}" 00289 ".QLabel { color: %5; }" 00290 ) 00291 .arg(bg0.name()) 00292 .arg(bg1.name()) 00293 .arg(bg2.name()) 00294 .arg(border.name()) 00295 .arg(fg.name()) 00296 ); 00297 00298 // Icon 00299 const int size = KIconLoader::global()->currentSize(KIconLoader::MainToolbar); 00300 d->iconLabel->setPixmap(icon.pixmap(size)); 00301 } 00302 00303 QSize KMessageWidget::sizeHint() const 00304 { 00305 ensurePolished(); 00306 return d->content->sizeHint(); 00307 } 00308 00309 QSize KMessageWidget::minimumSizeHint() const 00310 { 00311 ensurePolished(); 00312 return d->content->minimumSizeHint(); 00313 } 00314 00315 bool KMessageWidget::event(QEvent* event) 00316 { 00317 if (event->type() == QEvent::Polish && !d->content->layout()) { 00318 d->createLayout(); 00319 } 00320 return QFrame::event(event); 00321 } 00322 00323 void KMessageWidget::resizeEvent(QResizeEvent* event) 00324 { 00325 QFrame::resizeEvent(event); 00326 00327 if (d->timeLine->state() == QTimeLine::NotRunning) { 00328 d->content->resize(width(), d->bestContentHeight()); 00329 } 00330 } 00331 00332 int KMessageWidget::heightForWidth(int width) const 00333 { 00334 ensurePolished(); 00335 return d->content->heightForWidth(width); 00336 } 00337 00338 void KMessageWidget::paintEvent(QPaintEvent* event) 00339 { 00340 QFrame::paintEvent(event); 00341 if (d->timeLine->state() == QTimeLine::Running) { 00342 QPainter painter(this); 00343 painter.setOpacity(d->timeLine->currentValue() * d->timeLine->currentValue()); 00344 painter.drawPixmap(0, 0, d->contentSnapShot); 00345 } 00346 } 00347 00348 void KMessageWidget::showEvent(QShowEvent* event) 00349 { 00350 // Keep this method here to avoid breaking binary compatibility: 00351 // QFrame::showEvent() used to be reimplemented. 00352 QFrame::showEvent(event); 00353 } 00354 00355 bool KMessageWidget::wordWrap() const 00356 { 00357 return d->wordWrap; 00358 } 00359 00360 void KMessageWidget::setWordWrap(bool wordWrap) 00361 { 00362 d->wordWrap = wordWrap; 00363 d->textLabel->setWordWrap(wordWrap); 00364 QSizePolicy policy = sizePolicy(); 00365 policy.setHeightForWidth(wordWrap); 00366 setSizePolicy(policy); 00367 d->updateLayout(); 00368 // Without this, when user does wordWrap -> !wordWrap -> wordWrap, a minimum 00369 // height is set, causing the widget to be too high. 00370 // Mostly visible in test programs. 00371 if (wordWrap) { 00372 setMinimumHeight(0); 00373 } 00374 } 00375 00376 bool KMessageWidget::isCloseButtonVisible() const 00377 { 00378 return d->closeButton->isVisible(); 00379 } 00380 00381 void KMessageWidget::setCloseButtonVisible(bool show) 00382 { 00383 d->closeButton->setVisible(show); 00384 updateGeometry(); 00385 } 00386 00387 void KMessageWidget::addAction(QAction* action) 00388 { 00389 QFrame::addAction(action); 00390 d->updateLayout(); 00391 } 00392 00393 void KMessageWidget::removeAction(QAction* action) 00394 { 00395 QFrame::removeAction(action); 00396 d->updateLayout(); 00397 } 00398 00399 void KMessageWidget::animatedShow() 00400 { 00401 if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) { 00402 show(); 00403 return; 00404 } 00405 00406 if (isVisible()) { 00407 return; 00408 } 00409 00410 QFrame::show(); 00411 setFixedHeight(0); 00412 int wantedHeight = d->bestContentHeight(); 00413 d->content->setGeometry(0, -wantedHeight, width(), wantedHeight); 00414 00415 d->updateSnapShot(); 00416 00417 d->timeLine->setDirection(QTimeLine::Forward); 00418 if (d->timeLine->state() == QTimeLine::NotRunning) { 00419 d->timeLine->start(); 00420 } 00421 } 00422 00423 void KMessageWidget::animatedHide() 00424 { 00425 if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) { 00426 hide(); 00427 return; 00428 } 00429 00430 if (!isVisible()) { 00431 return; 00432 } 00433 00434 d->content->move(0, -d->content->height()); 00435 d->updateSnapShot(); 00436 00437 d->timeLine->setDirection(QTimeLine::Backward); 00438 if (d->timeLine->state() == QTimeLine::NotRunning) { 00439 d->timeLine->start(); 00440 } 00441 } 00442 00443 #include "kmessagewidget.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:32:41 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:32:41 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.