mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 18:32:49 +00:00
kdeui: essentially KMessageWidget rewrite
the fixed size policy causes all sort of trouble, no more tho. also the buttons will be centered now when word wrapping is on Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
55a923205a
commit
0ba71250e0
2 changed files with 67 additions and 161 deletions
|
@ -32,62 +32,47 @@
|
|||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QtGui/qevent.h>
|
||||
#include <QTimeLine>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QToolButton>
|
||||
#include <QStyle>
|
||||
|
||||
// NOTE: KateAnimation relies on this being 500ms, see:
|
||||
// kde-workspace/kate/part/view/kateanimation.cpp
|
||||
static const int s_kmessageanimationduration = 500;
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// KMessageWidgetPrivate
|
||||
//---------------------------------------------------------------------
|
||||
class KMessageWidgetPrivate
|
||||
{
|
||||
public:
|
||||
void init(KMessageWidget*);
|
||||
void init(KMessageWidget *q_ptr);
|
||||
|
||||
KMessageWidget* q;
|
||||
QFrame* content;
|
||||
QLabel* iconLabel;
|
||||
QLabel* textLabel;
|
||||
QToolButton* closeButton;
|
||||
QTimeLine* timeLine;
|
||||
QIcon icon;
|
||||
|
||||
KMessageWidget::MessageType messageType;
|
||||
bool wordWrap;
|
||||
QList<QToolButton*> buttons;
|
||||
QImage contentSnapShot;
|
||||
QPropertyAnimation* animation;
|
||||
|
||||
void createLayout();
|
||||
void updateSnapShot();
|
||||
void updateLayout();
|
||||
void slotTimeLineChanged(qreal);
|
||||
void slotTimeLineFinished();
|
||||
|
||||
int bestContentHeight() const;
|
||||
};
|
||||
|
||||
void KMessageWidgetPrivate::init(KMessageWidget *q_ptr)
|
||||
{
|
||||
q = q_ptr;
|
||||
|
||||
q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
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 = new QLabel(q);
|
||||
iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
iconLabel->hide();
|
||||
|
||||
textLabel = new QLabel(content);
|
||||
textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
textLabel = new QLabel(q);
|
||||
textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
QObject::connect(textLabel, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString)));
|
||||
QObject::connect(textLabel, SIGNAL(linkHovered(QString)), q, SIGNAL(linkHovered(QString)));
|
||||
|
@ -98,24 +83,25 @@ void KMessageWidgetPrivate::init(KMessageWidget *q_ptr)
|
|||
// which might conflict with application-specific shortcuts.
|
||||
closeAction->setShortcut(QKeySequence());
|
||||
|
||||
closeButton = new QToolButton(content);
|
||||
closeButton = new QToolButton(q);
|
||||
closeButton->setAutoRaise(true);
|
||||
closeButton->setDefaultAction(closeAction);
|
||||
|
||||
animation = nullptr;
|
||||
|
||||
q->setMessageType(KMessageWidget::Information);
|
||||
}
|
||||
|
||||
void KMessageWidgetPrivate::createLayout()
|
||||
void KMessageWidgetPrivate::updateLayout()
|
||||
{
|
||||
delete content->layout();
|
||||
|
||||
content->resize(q->size());
|
||||
|
||||
if (q->layout()) {
|
||||
delete q->layout();
|
||||
}
|
||||
qDeleteAll(buttons);
|
||||
buttons.clear();
|
||||
|
||||
Q_FOREACH(QAction* action, q->actions()) {
|
||||
QToolButton* button = new QToolButton(content);
|
||||
QToolButton* button = new QToolButton(q);
|
||||
button->setDefaultAction(action);
|
||||
button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
buttons.append(button);
|
||||
|
@ -126,13 +112,13 @@ void KMessageWidgetPrivate::createLayout()
|
|||
// from the others.
|
||||
closeButton->setAutoRaise(buttons.isEmpty());
|
||||
|
||||
if (wordWrap) {
|
||||
QGridLayout* layout = new QGridLayout(content);
|
||||
if (textLabel->wordWrap()) {
|
||||
QGridLayout* layout = new QGridLayout(q);
|
||||
// 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;
|
||||
QHBoxLayout* buttonLayout = new QHBoxLayout();
|
||||
buttonLayout->addStretch();
|
||||
Q_FOREACH(QToolButton* button, buttons) {
|
||||
// For some reason, calling show() is necessary if wordwrap is true,
|
||||
|
@ -142,9 +128,10 @@ void KMessageWidgetPrivate::createLayout()
|
|||
buttonLayout->addWidget(button);
|
||||
}
|
||||
buttonLayout->addWidget(closeButton);
|
||||
buttonLayout->addStretch();
|
||||
layout->addItem(buttonLayout, 1, 0, 1, 2);
|
||||
} else {
|
||||
QHBoxLayout* layout = new QHBoxLayout(content);
|
||||
QHBoxLayout* layout = new QHBoxLayout(q);
|
||||
layout->addWidget(iconLabel);
|
||||
layout->addWidget(textLabel);
|
||||
|
||||
|
@ -155,72 +142,23 @@ void KMessageWidgetPrivate::createLayout()
|
|||
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 = QImage(content->size(), QImage::Format_ARGB32_Premultiplied);
|
||||
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)
|
||||
KMessageWidget::KMessageWidget(QWidget *parent)
|
||||
: QFrame(parent),
|
||||
d(new KMessageWidgetPrivate())
|
||||
{
|
||||
d->init(this);
|
||||
}
|
||||
|
||||
KMessageWidget::KMessageWidget(const QString& text, QWidget* parent)
|
||||
: QFrame(parent)
|
||||
, d(new KMessageWidgetPrivate)
|
||||
KMessageWidget::KMessageWidget(const QString &text, QWidget *parent)
|
||||
: QFrame(parent),
|
||||
d(new KMessageWidgetPrivate())
|
||||
{
|
||||
d->init(this);
|
||||
setText(text);
|
||||
|
@ -281,8 +219,8 @@ void KMessageWidget::setMessageType(KMessageWidget::MessageType type)
|
|||
bg2 = bg1.darker(110);
|
||||
border = KColorScheme::shade(bg1, KColorScheme::DarkShade);
|
||||
|
||||
d->content->setStyleSheet(
|
||||
QString(".QFrame {"
|
||||
setStyleSheet(
|
||||
QString("QLabel {"
|
||||
"background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,"
|
||||
" stop: 0 %1,"
|
||||
" stop: 0.1 %2,"
|
||||
|
@ -290,8 +228,8 @@ void KMessageWidget::setMessageType(KMessageWidget::MessageType type)
|
|||
"border-radius: 5px;"
|
||||
"border: 1px solid %4;"
|
||||
"margin: %5px;"
|
||||
"color: %6;"
|
||||
"}"
|
||||
".QLabel { color: %6; }"
|
||||
)
|
||||
.arg(bg0.name())
|
||||
.arg(bg1.name())
|
||||
|
@ -300,74 +238,44 @@ void KMessageWidget::setMessageType(KMessageWidget::MessageType type)
|
|||
// 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();
|
||||
return QFrame::sizeHint();
|
||||
}
|
||||
|
||||
QSize KMessageWidget::minimumSizeHint() const
|
||||
{
|
||||
ensurePolished();
|
||||
return d->content->minimumSizeHint();
|
||||
return QFrame::minimumSizeHint();
|
||||
}
|
||||
|
||||
bool KMessageWidget::event(QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::Polish && !d->content->layout()) {
|
||||
d->createLayout();
|
||||
if (event->type() == QEvent::Polish && !layout()) {
|
||||
d->updateLayout();
|
||||
}
|
||||
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.drawImage(0, 0, d->contentSnapShot);
|
||||
painter.end();
|
||||
}
|
||||
return QFrame::heightForWidth(width);
|
||||
}
|
||||
|
||||
bool KMessageWidget::wordWrap() const
|
||||
{
|
||||
return d->wordWrap;
|
||||
return d->textLabel->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
|
||||
|
@ -395,6 +303,11 @@ void KMessageWidget::removeAction(QAction* action)
|
|||
|
||||
void KMessageWidget::animatedShow()
|
||||
{
|
||||
if (d->animation) {
|
||||
delete d->animation;
|
||||
d->animation = nullptr;
|
||||
}
|
||||
|
||||
if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) {
|
||||
show();
|
||||
return;
|
||||
|
@ -404,21 +317,22 @@ void KMessageWidget::animatedShow()
|
|||
return;
|
||||
}
|
||||
|
||||
d->animation = new QPropertyAnimation(this, "windowOpacity", this);
|
||||
d->animation->setStartValue(0.0);
|
||||
d->animation->setEndValue(1.0);
|
||||
d->animation->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
d->animation->setDuration(s_kmessageanimationduration);
|
||||
d->animation->start();
|
||||
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 (d->animation) {
|
||||
delete d->animation;
|
||||
d->animation = nullptr;
|
||||
}
|
||||
|
||||
if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) {
|
||||
hide();
|
||||
return;
|
||||
|
@ -428,13 +342,13 @@ void KMessageWidget::animatedHide()
|
|||
return;
|
||||
}
|
||||
|
||||
d->content->move(0, -d->content->height());
|
||||
d->updateSnapShot();
|
||||
|
||||
d->timeLine->setDirection(QTimeLine::Backward);
|
||||
if (d->timeLine->state() == QTimeLine::NotRunning) {
|
||||
d->timeLine->start();
|
||||
}
|
||||
d->animation = new QPropertyAnimation(this, "windowOpacity", this);
|
||||
d->animation->setStartValue(1.0);
|
||||
d->animation->setEndValue(0.0);
|
||||
d->animation->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
d->animation->setDuration(s_kmessageanimationduration);
|
||||
d->animation->start();
|
||||
QFrame::hide();
|
||||
}
|
||||
|
||||
QIcon KMessageWidget::icon() const
|
||||
|
@ -454,5 +368,4 @@ void KMessageWidget::setIcon(const QIcon& icon)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#include "moc_kmessagewidget.cpp"
|
||||
|
|
|
@ -110,9 +110,9 @@ public:
|
|||
/**
|
||||
* Constructs a KMessageWidget with the specified parent.
|
||||
*/
|
||||
explicit KMessageWidget(QWidget *parent = 0);
|
||||
explicit KMessageWidget(QWidget *parent = nullptr);
|
||||
|
||||
explicit KMessageWidget(const QString &text, QWidget *parent = 0);
|
||||
explicit KMessageWidget(const QString &text, QWidget *parent = nullptr);
|
||||
|
||||
~KMessageWidget();
|
||||
|
||||
|
@ -175,7 +175,7 @@ Q_SIGNALS:
|
|||
* @see QLabel::linkActivated()
|
||||
* @since 4.10
|
||||
*/
|
||||
void linkActivated(const QString& contents);
|
||||
void linkActivated(const QString &contents);
|
||||
|
||||
/**
|
||||
* This signal is emitted when the user hovers over a link in the text label.
|
||||
|
@ -184,21 +184,14 @@ Q_SIGNALS:
|
|||
* @see QLabel::linkHovered()
|
||||
* @since 4.11
|
||||
*/
|
||||
void linkHovered(const QString& contents);
|
||||
void linkHovered(const QString &contents);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event);
|
||||
|
||||
bool event(QEvent *event);
|
||||
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
|
||||
private:
|
||||
KMessageWidgetPrivate *const d;
|
||||
friend class KMessageWidgetPrivate;
|
||||
|
||||
Q_PRIVATE_SLOT(d, void slotTimeLineChanged(qreal))
|
||||
Q_PRIVATE_SLOT(d, void slotTimeLineFinished())
|
||||
};
|
||||
|
||||
#endif /* KMESSAGEWIDGET_H */
|
||||
|
|
Loading…
Add table
Reference in a new issue