diff --git a/interfaces/ktexteditor/messageinterface.h b/interfaces/ktexteditor/messageinterface.h index 8d82d6e2..cf2dba33 100644 --- a/interfaces/ktexteditor/messageinterface.h +++ b/interfaces/ktexteditor/messageinterface.h @@ -102,10 +102,9 @@ class KTEXTEDITOR_EXPORT Message : public QObject * For simple notifications either use Positive or Information. */ enum MessageType { - Positive = 0, ///< positive information message - Information, ///< information message type - Warning, ///< warning message type - Error ///< error message type + Information = 0, ///< information message type + Warning, ///< warning message type + Error ///< error message type }; /** diff --git a/kdeui/widgets/kmessagewidget.cpp b/kdeui/widgets/kmessagewidget.cpp index 4cac533b..2085f663 100644 --- a/kdeui/widgets/kmessagewidget.cpp +++ b/kdeui/widgets/kmessagewidget.cpp @@ -19,19 +19,53 @@ */ #include "kmessagewidget.h" #include "kaction.h" -#include "kcolorscheme.h" #include "kicon.h" #include "kiconloader.h" +#include "kcolorscheme.h" #include "kstandardaction.h" #include "kpixmapwidget.h" #include "kdebug.h" -#include -#include #include #include #include -#include +#include + +static const qreal s_roundness = 4.0; +static const qreal s_bordersize = 0.5; +static const qreal s_margin = 5; + +class KMessageLabel : public QLabel +{ + Q_OBJECT +public: + KMessageLabel(QWidget *parent); + + QColor bg; + QColor border; + +protected: + void paintEvent(QPaintEvent *event) final; +}; + +KMessageLabel::KMessageLabel(QWidget *parent) + : QLabel(parent) +{ + setContentsMargins(s_margin, s_margin, s_margin, s_margin); +} + +void KMessageLabel::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.setBrush(border); + QRectF widgetrect = rect(); + painter.drawRoundedRect(widgetrect, s_roundness, s_roundness); + painter.setBrush(bg); + widgetrect = widgetrect.adjusted(s_bordersize, s_bordersize, -s_bordersize, -s_bordersize); + painter.drawRoundedRect(widgetrect, s_roundness, s_roundness); + QLabel::paintEvent(event); +} //--------------------------------------------------------------------- // KMessageWidgetPrivate @@ -39,118 +73,106 @@ class KMessageWidgetPrivate { public: - void init(KMessageWidget *q_ptr); + KMessageWidgetPrivate(); + ~KMessageWidgetPrivate(); - KMessageWidget* q; - KPixmapWidget* iconWidget; - QLabel* textLabel; - QToolButton* closeButton; + void updateColors(); + + QVBoxLayout* mainlayout; + QHBoxLayout* messagelayout; + KPixmapWidget* iconwidget; + KMessageLabel* textlabel; + QToolButton* closebutton; QIcon icon; - KMessageWidget::MessageType messageType; + KMessageWidget::MessageType messagetype; + QHBoxLayout* buttonslayout; QList buttons; - - void updateLayout(); }; -void KMessageWidgetPrivate::init(KMessageWidget *q_ptr) +KMessageWidgetPrivate::KMessageWidgetPrivate() + : mainlayout(nullptr), + messagelayout(nullptr), + iconwidget(nullptr), + textlabel(nullptr), + closebutton(nullptr), + buttonslayout(nullptr), + messagetype(KMessageWidget::Information) { - q = q_ptr; - - q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - iconWidget = new KPixmapWidget(q); - iconWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - iconWidget->hide(); - - 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))); - - KAction* closeAction = KStandardAction::close(q, SLOT(animatedHide()), q); - - // The default shortcut assigned by KStandardAction is Ctrl+W, - // which might conflict with application-specific shortcuts. - closeAction->setShortcut(QKeySequence()); - - closeButton = new QToolButton(q); - closeButton->setAutoRaise(true); - closeButton->setDefaultAction(closeAction); - - q->setMessageType(KMessageWidget::Information); } -void KMessageWidgetPrivate::updateLayout() +KMessageWidgetPrivate::~KMessageWidgetPrivate() { - if (q->layout()) { - delete q->layout(); - } qDeleteAll(buttons); buttons.clear(); - - Q_FOREACH(QAction* action, q->actions()) { - QToolButton* button = new QToolButton(q); - 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 (textLabel->wordWrap()) { - QGridLayout* layout = new QGridLayout(q); - // Set alignment to make sure icon does not move down if text wraps - layout->addWidget(iconWidget, 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); - buttonLayout->addStretch(); - layout->addItem(buttonLayout, 1, 0, 1, 2); - } else { - QHBoxLayout* layout = new QHBoxLayout(q); - layout->addWidget(iconWidget); - layout->addWidget(textLabel); - - Q_FOREACH(QToolButton* button, buttons) { - layout->addWidget(button); - } - - layout->addWidget(closeButton); - }; - - q->updateGeometry(); + delete buttonslayout; + delete messagelayout; } +void KMessageWidgetPrivate::updateColors() +{ + const KColorScheme scheme(QPalette::Active, KColorScheme::Window); + switch (messagetype) { + case KMessageWidget::Information: { + // even tho the selection color may be more suitable for that it cannot be used because + // the text is selectable + textlabel->bg = scheme.background(KColorScheme::PositiveBackground).color(); + break; + } + case KMessageWidget::Warning: { + textlabel->bg = scheme.background(KColorScheme::NeutralBackground).color(); + break; + } + case KMessageWidget::Error: { + textlabel->bg = scheme.background(KColorScheme::NegativeBackground).color(); + break; + } + } + // textlabel->bg = textlabel->bg.darker(60); + textlabel->border = KColorScheme::shade(textlabel->bg, KColorScheme::DarkShade); +} //--------------------------------------------------------------------- // KMessageWidget //--------------------------------------------------------------------- KMessageWidget::KMessageWidget(QWidget *parent) - : QFrame(parent), + : QWidget(parent), d(new KMessageWidgetPrivate()) { - d->init(this); -} + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); -KMessageWidget::KMessageWidget(const QString &text, QWidget *parent) - : QFrame(parent), - d(new KMessageWidgetPrivate()) -{ - d->init(this); - setText(text); + d->mainlayout = new QVBoxLayout(this); + setLayout(d->mainlayout); + + d->messagelayout = new QHBoxLayout(); + + d->iconwidget = new KPixmapWidget(this); + d->iconwidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + d->iconwidget->hide(); + d->messagelayout->addWidget(d->iconwidget); + + d->textlabel = new KMessageLabel(this); + d->textlabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + d->textlabel->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::LinksAccessibleByMouse); + d->textlabel->setAlignment(Qt::AlignCenter); + connect(d->textlabel, SIGNAL(linkActivated(QString)), this, SIGNAL(linkActivated(QString))); + connect(d->textlabel, SIGNAL(linkHovered(QString)), this, SIGNAL(linkHovered(QString))); + d->messagelayout->addWidget(d->textlabel); + + KAction* closeAction = KStandardAction::close(this, SLOT(animatedHide()), this); + // The default shortcut assigned by KStandardAction is Ctrl+W, + // which might conflict with application-specific shortcuts. + closeAction->setShortcut(QKeySequence()); + d->closebutton = new QToolButton(this); + d->closebutton->setAutoRaise(true); + d->closebutton->setDefaultAction(closeAction); + d->messagelayout->addWidget(d->closebutton); + + d->mainlayout->addLayout(d->messagelayout); + + d->buttonslayout = new QHBoxLayout(); + d->mainlayout->addLayout(d->buttonslayout); + + d->updateColors(); } KMessageWidget::~KMessageWidget() @@ -160,133 +182,49 @@ KMessageWidget::~KMessageWidget() QString KMessageWidget::text() const { - return d->textLabel->text(); + return d->textlabel->text(); } void KMessageWidget::setText(const QString& text) { - d->textLabel->setText(text); + d->textlabel->setText(text); updateGeometry(); } KMessageWidget::MessageType KMessageWidget::messageType() const { - return d->messageType; -} - -static void getColorsFromColorScheme(KColorScheme::BackgroundRole bgRole, QColor* bg, QColor* fg) -{ - KColorScheme scheme(QPalette::Active, KColorScheme::Window); - *bg = scheme.background(bgRole).color(); - *fg = scheme.foreground().color(); + return d->messagetype; } void KMessageWidget::setMessageType(KMessageWidget::MessageType type) { - d->messageType = type; - QColor bg0, bg1, bg2, border, fg; - switch (type) { - case Positive: - getColorsFromColorScheme(KColorScheme::PositiveBackground, &bg1, &fg); - break; - case Information: - // There is no "information" background role in KColorScheme, use the - // colors of highlighted items instead - bg1 = palette().highlight().color(); - fg = palette().highlightedText().color(); - break; - case Warning: - getColorsFromColorScheme(KColorScheme::NeutralBackground, &bg1, &fg); - break; - case Error: - getColorsFromColorScheme(KColorScheme::NegativeBackground, &bg1, &fg); - break; - } - - // Colors - bg0 = bg1.lighter(110); - bg2 = bg1.darker(110); - border = KColorScheme::shade(bg1, KColorScheme::DarkShade); - - setStyleSheet( - QString("QLabel {" - "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;" - "color: %5;" - "}" - ) - .arg(bg0.name()) - .arg(bg1.name()) - .arg(bg2.name()) - .arg(border.name()) - .arg(fg.name()) - ); -} - -QSize KMessageWidget::sizeHint() const -{ - ensurePolished(); - return QFrame::sizeHint(); -} - -QSize KMessageWidget::minimumSizeHint() const -{ - ensurePolished(); - return QFrame::minimumSizeHint(); -} - -bool KMessageWidget::event(QEvent* event) -{ - if (event->type() == QEvent::Polish && !layout()) { - d->updateLayout(); - } - return QFrame::event(event); -} - -int KMessageWidget::heightForWidth(int width) const -{ - ensurePolished(); - return QFrame::heightForWidth(width); + d->messagetype = type; + d->updateColors(); + update(); } bool KMessageWidget::wordWrap() const { - return d->textLabel->wordWrap(); + return d->textlabel->wordWrap(); } void KMessageWidget::setWordWrap(bool wordWrap) { - d->textLabel->setWordWrap(wordWrap); - d->updateLayout(); + d->textlabel->setWordWrap(wordWrap); + adjustSize(); } bool KMessageWidget::isCloseButtonVisible() const { - return d->closeButton->isVisible(); + return d->closebutton->isVisible(); } void KMessageWidget::setCloseButtonVisible(bool show) { - d->closeButton->setVisible(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()) { @@ -295,7 +233,7 @@ void KMessageWidget::animatedShow() // yep, no animation. changing the geometry for 500ms looks exactly the same as showing the // widget without doing so - QFrame::show(); + QWidget::show(); } void KMessageWidget::animatedHide() @@ -304,7 +242,7 @@ void KMessageWidget::animatedHide() return; } - QFrame::hide(); + QWidget::hide(); } QIcon KMessageWidget::icon() const @@ -316,12 +254,45 @@ void KMessageWidget::setIcon(const QIcon& icon) { d->icon = icon; if (d->icon.isNull()) { - d->iconWidget->hide(); + d->iconwidget->hide(); } else { const int size = KIconLoader::global()->currentSize(KIconLoader::MainToolbar); - d->iconWidget->setPixmap(d->icon.pixmap(size)); - d->iconWidget->show(); + d->iconwidget->setPixmap(d->icon.pixmap(size)); + d->iconwidget->show(); } } +bool KMessageWidget::event(QEvent *event) +{ + const bool result = QWidget::event(event); + switch (event->type()) { + case QEvent::PaletteChange: { + d->updateColors(); + update(); + break; + } + case QEvent::ActionChanged: + case QEvent::ActionAdded: + case QEvent::ActionRemoved: { + qDeleteAll(d->buttons); + d->buttons.clear(); + delete d->buttonslayout; + d->buttonslayout = new QHBoxLayout(); + d->mainlayout->addLayout(d->buttonslayout); + d->buttonslayout->addStretch(); + foreach (QAction* action, actions()) { + QToolButton* button = new QToolButton(this); + button->setDefaultAction(action); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + d->buttons.append(button); + d->buttonslayout->addWidget(button, 1, Qt::AlignCenter); + } + d->buttonslayout->addStretch(); + break; + } + } + return result; +} + #include "moc_kmessagewidget.cpp" +#include "kmessagewidget.moc" diff --git a/kdeui/widgets/kmessagewidget.h b/kdeui/widgets/kmessagewidget.h index 8ab134be..b8b08e7e 100644 --- a/kdeui/widgets/kmessagewidget.h +++ b/kdeui/widgets/kmessagewidget.h @@ -22,7 +22,8 @@ #include -#include +#include +#include class KMessageWidgetPrivate; @@ -36,60 +37,10 @@ class KMessageWidgetPrivate; * to "OK Only" message boxes. If you do not need the modalness of KMessageBox, * consider using KMessageWidget instead. * - * Negative feedback - * - * The KMessageWidget can be used as a secondary indicator of failure: the - * first indicator is usually the fact the action the user expected to happen - * did not happen. - * - * Example: User fills a form, clicks "Submit". - * - * @li Expected feedback: form closes - * @li First indicator of failure: form stays there - * @li Second indicator of failure: a KMessageWidget appears on top of the - * form, explaining the error condition - * - * When used to provide negative feedback, KMessageWidget should be placed - * close to its context. In the case of a form, it should appear on top of the - * form entries. - * - * KMessageWidget should get inserted in the existing layout. Space should not - * be reserved for it, otherwise it becomes "dead space", ignored by the user. - * KMessageWidget should also not appear as an overlay to prevent blocking - * access to elements the user needs to interact with to fix the failure. - * - * Positive feedback - * - * KMessageWidget can be used for positive feedback but it shouldn't be - * overused. It is often enough to provide feedback by simply showing the - * results of an action. - * - * Examples of acceptable uses: - * - * @li Confirm success of "critical" transactions - * @li Indicate completion of background tasks - * - * Example of inadapted uses: - * - * @li Indicate successful saving of a file - * @li Indicate a file has been successfully removed - * - * Opportunistic interaction - * - * Opportunistic interaction is the situation where the application suggests to - * the user an action he could be interested in perform, either based on an - * action the user just triggered or an event which the application noticed. - * - * Example of acceptable uses: - * - * @li A browser can propose remembering a recently entered password - * @li A music collection can propose ripping a CD which just got inserted - * @li A chat application may notify the user a "special friend" just connected - * * @author Aurélien Gâteau * @since 4.7 */ -class KDEUI_EXPORT KMessageWidget : public QFrame +class KDEUI_EXPORT KMessageWidget : public QWidget { Q_OBJECT Q_ENUMS(MessageType) @@ -101,7 +52,6 @@ class KDEUI_EXPORT KMessageWidget : public QFrame Q_PROPERTY(QIcon icon READ icon WRITE setIcon) public: enum MessageType { - Positive, Information, Warning, Error @@ -111,29 +61,13 @@ public: * Constructs a KMessageWidget with the specified parent. */ explicit KMessageWidget(QWidget *parent = nullptr); - - explicit KMessageWidget(const QString &text, QWidget *parent = nullptr); - ~KMessageWidget(); QString text() const; - bool wordWrap() const; - bool isCloseButtonVisible() const; - MessageType messageType() const; - void addAction(QAction *action); - - void removeAction(QAction *action); - - QSize sizeHint() const; - - QSize minimumSizeHint() const; - - int heightForWidth(int width) const; - /** * The icon shown on the left of the text. By default, no icon is shown. * @since 4.11 @@ -142,11 +76,8 @@ public: public Q_SLOTS: void setText(const QString &text); - void setWordWrap(bool wordWrap); - void setCloseButtonVisible(bool visible); - void setMessageType(KMessageWidget::MessageType type); /**