diff options
author | Tomaz Canabrava <tcanabrava@kde.org> | 2013-05-17 21:58:49 -0300 |
---|---|---|
committer | Tomaz Canabrava <tcanabrava@kde.org> | 2013-05-17 21:58:49 -0300 |
commit | 86021b24d98c385b10ccc2fefab5cf22f3a03b54 (patch) | |
tree | 39a603640b2882cec9040c59f1a51bfe8e88bae6 /qt-ui/kmessagewidget.cpp | |
parent | 56dbb7c2ff697a393f5051e2b5363bd4c0f2bb6e (diff) | |
download | subsurface-86021b24d98c385b10ccc2fefab5cf22f3a03b54.tar.gz |
Borrowed the KMessageWidget code to better show passive information.
Borrowed the code from KMessageWidget from Aurelian Gateau, Kdelibs,
to better show passive information and notifications. instead of a
popup blowing in the user's face, a nice, animated and well designed
widget will gracefully fade-in, show the notes, and fade out when
not needed anymore.
Signed-off-by: Tomaz Canabrava <tcanabrava@kde.org>
Diffstat (limited to 'qt-ui/kmessagewidget.cpp')
-rw-r--r-- | qt-ui/kmessagewidget.cpp | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/qt-ui/kmessagewidget.cpp b/qt-ui/kmessagewidget.cpp new file mode 100644 index 000000000..653d52f4f --- /dev/null +++ b/qt-ui/kmessagewidget.cpp @@ -0,0 +1,421 @@ +/* This file is part of the KDE libraries + * + * Copyright (c) 2011 Aurélien Gâteau <agateau@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include "kmessagewidget.h" + +#include <QEvent> +#include <QGridLayout> +#include <QHBoxLayout> +#include <QLabel> +#include <QPainter> +#include <QShowEvent> +#include <QTimeLine> +#include <QToolButton> +#include <QStyle> +#include <QAction> + +void KMessageWidgetPrivate::init(KMessageWidget* q_ptr) +{ + q = q_ptr; + + q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + + timeLine = new QTimeLine(500, q); + QObject::connect(timeLine, SIGNAL(valueChanged(qreal)), q, SLOT(slotTimeLineChanged(qreal))); + QObject::connect(timeLine, SIGNAL(finished()), q, SLOT(slotTimeLineFinished())); + + content = new QFrame(q); + content->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + wordWrap = false; + + iconLabel = new QLabel(content); + iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + iconLabel->hide(); + + textLabel = new QLabel(content); + textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + QObject::connect(textLabel, SIGNAL(linkActivated(const QString&)), q, SIGNAL(linkActivated(const QString&))); + QObject::connect(textLabel, SIGNAL(linkHovered(const QString&)), q, SIGNAL(linkHovered(const QString&))); + + QAction* closeAction = new QAction(QObject::tr("Close"), q); + q->connect(closeAction, SIGNAL(toggled(bool)), q, SLOT(animatedHide())); + + closeButton = new QToolButton(content); + closeButton->setAutoRaise(true); + closeButton->setDefaultAction(closeAction); + + q->setMessageType(KMessageWidget::Information); +} + +void KMessageWidgetPrivate::createLayout() +{ + delete content->layout(); + + content->resize(q->size()); + + qDeleteAll(buttons); + buttons.clear(); + + Q_FOREACH(QAction* action, q->actions()) { + QToolButton* button = new QToolButton(content); + button->setDefaultAction(action); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + buttons.append(button); + } + + // AutoRaise reduces visual clutter, but we don't want to turn it on if + // there are other buttons, otherwise the close button will look different + // from the others. + closeButton->setAutoRaise(buttons.isEmpty()); + + if (wordWrap) { + QGridLayout* layout = new QGridLayout(content); + // Set alignment to make sure icon does not move down if text wraps + layout->addWidget(iconLabel, 0, 0, 1, 1, Qt::AlignHCenter | Qt::AlignTop); + layout->addWidget(textLabel, 0, 1); + + QHBoxLayout* buttonLayout = new QHBoxLayout; + buttonLayout->addStretch(); + Q_FOREACH(QToolButton* button, buttons) { + // For some reason, calling show() is necessary if wordwrap is true, + // otherwise the buttons do not show up. It is not needed if + // wordwrap is false. + button->show(); + buttonLayout->addWidget(button); + } + buttonLayout->addWidget(closeButton); + layout->addItem(buttonLayout, 1, 0, 1, 2); + } else { + QHBoxLayout* layout = new QHBoxLayout(content); + layout->addWidget(iconLabel); + layout->addWidget(textLabel); + + Q_FOREACH(QToolButton* button, buttons) { + layout->addWidget(button); + } + + layout->addWidget(closeButton); + }; + + if (q->isVisible()) { + q->setFixedHeight(content->sizeHint().height()); + } + + q->updateGeometry(); +} + +void KMessageWidgetPrivate::updateLayout() +{ + if (content->layout()) { + createLayout(); + } +} + +void KMessageWidgetPrivate::updateSnapShot() +{ + // Attention: updateSnapShot calls QWidget::render(), which causes the whole + // window layouts to be activated. Calling this method from resizeEvent() + // can lead to infinite recursion, see: + // https://bugs.kde.org/show_bug.cgi?id=311336 + contentSnapShot = QPixmap(content->size()); + contentSnapShot.fill(Qt::transparent); + content->render(&contentSnapShot, QPoint(), QRegion(), QWidget::DrawChildren); +} + +void KMessageWidgetPrivate::slotTimeLineChanged(qreal value) +{ + q->setFixedHeight(qMin(value * 2, qreal(1.0)) * content->height()); + q->update(); +} + +void KMessageWidgetPrivate::slotTimeLineFinished() +{ + if (timeLine->direction() == QTimeLine::Forward) { + // Show + // We set the whole geometry here, because it may be wrong if a + // KMessageWidget is shown right when the toplevel window is created. + content->setGeometry(0, 0, q->width(), bestContentHeight()); + } else { + // Hide + q->hide(); + } +} + +int KMessageWidgetPrivate::bestContentHeight() const +{ + int height = content->heightForWidth(q->width()); + + if (height == -1) { + height = content->sizeHint().height(); + } + + return height; +} + + +//--------------------------------------------------------------------- +// KMessageWidget +//--------------------------------------------------------------------- +KMessageWidget::KMessageWidget(QWidget* parent) + : QFrame(parent) + , d(new KMessageWidgetPrivate) +{ + d->init(this); +} + +KMessageWidget::KMessageWidget(const QString& text, QWidget* parent) + : QFrame(parent) + , d(new KMessageWidgetPrivate) +{ + d->init(this); + setText(text); +} + +KMessageWidget::~KMessageWidget() +{ + delete d; +} + +QString KMessageWidget::text() const +{ + return d->textLabel->text(); +} + +void KMessageWidget::setText(const QString& text) +{ + d->textLabel->setText(text); + updateGeometry(); +} + +KMessageWidget::MessageType KMessageWidget::messageType() const +{ + return d->messageType; +} + +void KMessageWidget::setMessageType(KMessageWidget::MessageType type) +{ + d->messageType = type; + QColor bg0, bg1, bg2, border, fg; + + switch (type) { + case Positive: + bg1 = QColor(Qt::green); + fg = QColor(Qt::white); + break; + + case Information: + bg1 = QColor(Qt::blue); + fg = QColor(Qt::black); + break; + + case Warning: + bg1 = QColor(Qt::yellow); + fg = QColor(Qt::black); + break; + + case Error: + bg1 = QColor(Qt::red); + fg = QColor(Qt::black); + break; + } + + // Colors + bg0 = bg1.lighter(110); + bg2 = bg1.darker(110); + border = QColor(Qt::black); + d->content->setStyleSheet( + QString(".QFrame {" + "background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1," + " stop: 0 %1," + " stop: 0.1 %2," + " stop: 1.0 %3);" + "border-radius: 5px;" + "border: 1px solid %4;" + "margin: %5px;" + "}" + ".QLabel { color: %6; }" + ).arg(bg0.name()) + .arg(bg1.name()) + .arg(bg2.name()) + .arg(border.name()) + /* + DefaultFrameWidth returns the size of the external margin + border width. + We know our border is 1px, so we subtract this from the frame + normal QStyle FrameWidth to get our margin + */ + + .arg(style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this) -1) + .arg(fg.name()) + ); +} + +QSize KMessageWidget::sizeHint() const +{ + ensurePolished(); + return d->content->sizeHint(); +} + +QSize KMessageWidget::minimumSizeHint() const +{ + ensurePolished(); + return d->content->minimumSizeHint(); +} + +bool KMessageWidget::event(QEvent* event) +{ + if (event->type() == QEvent::Polish && !d->content->layout()) { + d->createLayout(); + } + + return QFrame::event(event); +} + +void KMessageWidget::resizeEvent(QResizeEvent* event) +{ + QFrame::resizeEvent(event); + + if (d->timeLine->state() == QTimeLine::NotRunning) { + d->content->resize(width(), d->bestContentHeight()); + } +} + +int KMessageWidget::heightForWidth(int width) const +{ + ensurePolished(); + return d->content->heightForWidth(width); +} + +void KMessageWidget::paintEvent(QPaintEvent* event) +{ + QFrame::paintEvent(event); + + if (d->timeLine->state() == QTimeLine::Running) { + QPainter painter(this); + painter.setOpacity(d->timeLine->currentValue() * d->timeLine->currentValue()); + painter.drawPixmap(0, 0, d->contentSnapShot); + } +} + +void KMessageWidget::showEvent(QShowEvent* event) +{ + // Keep this method here to avoid breaking binary compatibility: + // QFrame::showEvent() used to be reimplemented. + QFrame::showEvent(event); +} + +bool KMessageWidget::wordWrap() const +{ + return d->wordWrap; +} + +void KMessageWidget::setWordWrap(bool wordWrap) +{ + d->wordWrap = wordWrap; + d->textLabel->setWordWrap(wordWrap); + QSizePolicy policy = sizePolicy(); + policy.setHeightForWidth(wordWrap); + setSizePolicy(policy); + d->updateLayout(); + + // Without this, when user does wordWrap -> !wordWrap -> wordWrap, a minimum + // height is set, causing the widget to be too high. + // Mostly visible in test programs. + if (wordWrap) { + setMinimumHeight(0); + } +} + +bool KMessageWidget::isCloseButtonVisible() const +{ + return d->closeButton->isVisible(); +} + +void KMessageWidget::setCloseButtonVisible(bool show) +{ + d->closeButton->setVisible(show); + updateGeometry(); +} + +void KMessageWidget::addAction(QAction* action) +{ + QFrame::addAction(action); + d->updateLayout(); +} + +void KMessageWidget::removeAction(QAction* action) +{ + QFrame::removeAction(action); + d->updateLayout(); +} + +void KMessageWidget::animatedShow() +{ + if (isVisible()) { + return; + } + + QFrame::show(); + setFixedHeight(0); + int wantedHeight = d->bestContentHeight(); + d->content->setGeometry(0, -wantedHeight, width(), wantedHeight); + + d->updateSnapShot(); + + d->timeLine->setDirection(QTimeLine::Forward); + + if (d->timeLine->state() == QTimeLine::NotRunning) { + d->timeLine->start(); + } +} + +void KMessageWidget::animatedHide() +{ + if (!isVisible()) { + return; + } + + d->content->move(0, -d->content->height()); + d->updateSnapShot(); + + d->timeLine->setDirection(QTimeLine::Backward); + + if (d->timeLine->state() == QTimeLine::NotRunning) { + d->timeLine->start(); + } +} + +QIcon KMessageWidget::icon() const +{ + return d->icon; +} + +void KMessageWidget::setIcon(const QIcon& icon) +{ + d->icon = icon; + + if (d->icon.isNull()) { + d->iconLabel->hide(); + } else { + d->iconLabel->setPixmap(d->icon.pixmap(QSize(16,16))); + d->iconLabel->show(); + } +} + |