KTextAddons

textmessageindicator.cpp
1/*
2 SPDX-FileCopyrightText: 2014-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "textmessageindicator.h"
8
9#include <QAbstractScrollArea>
10#include <QApplication>
11#include <QPainter>
12#include <QResizeEvent>
13#include <QStyle>
14#include <QTimer>
15
16using namespace TextCustomEditor;
17TextMessageIndicator::TextMessageIndicator(QWidget *parent)
18 : QWidget(parent)
19{
20 setObjectName(QStringLiteral("TextMessageIndicator"));
21 setFocusPolicy(Qt::NoFocus);
22 QPalette pal = palette();
24 setPalette(pal);
25 // if the layout is LTR, we can safely place it in the right position
26 if (layoutDirection() == Qt::LeftToRight) {
27 move(10, parentWidget()->height() - 10);
28 }
29 resize(0, 0);
30 hide();
31}
32
33void TextMessageIndicator::display(const QString &message, const QString &details, Icon icon, int durationMs)
34{
35 if (message.isEmpty()) {
36 return;
37 }
38 // set text
39 mMessage = message;
40 mDetails = details;
41 // reset vars
42 mLineSpacing = 0;
43 // load icon (if set)
44 mSymbol = QPixmap();
45 const auto iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize);
46 if (icon != None) {
47 switch (icon) {
48 case Error:
49 mSymbol = QIcon::fromTheme(QStringLiteral("dialog-error")).pixmap(iconSize);
50 break;
51 case Warning:
52 mSymbol = QIcon::fromTheme(QStringLiteral("dialog-warning")).pixmap(iconSize);
53 break;
54 default:
55 mSymbol = QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(iconSize);
56 break;
57 }
58 }
59
60 computeSizeAndResize();
61 // show widget and schedule a repaint
62 show();
63 update();
64
65 // close the message window after given mS
66 if (durationMs > 0) {
67 if (!mTimer) {
68 mTimer = new QTimer(this);
69 mTimer->setSingleShot(true);
71 }
72 mTimer->start(durationMs);
73 } else if (mTimer) {
74 mTimer->stop();
75 }
76
77 qobject_cast<QAbstractScrollArea *>(parentWidget())->viewport()->installEventFilter(this);
78}
79
80QRect TextMessageIndicator::computeTextRect(const QString &message, int extra_width) const
81// Return the QRect which embeds the text
82{
83 int charSize = fontMetrics().averageCharWidth();
84 /* width of the viewport, minus 20 (~ size removed by further resizing),
85 minus the extra size (usually the icon width), minus (a bit empirical)
86 twice the mean width of a character to ensure that the bounding box is
87 really smaller than the container.
88 */
89 const int boundingWidth =
90 qobject_cast<QAbstractScrollArea *>(parentWidget())->viewport()->width() - 20 - (extra_width > 0 ? 2 + extra_width : 0) - 2 * charSize;
91 QRect textRect = fontMetrics().boundingRect(0, 0, boundingWidth, 0, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
92 textRect.translate(-textRect.left(), -textRect.top());
93 textRect.adjust(0, 0, 2, 2);
94
95 return textRect;
96}
97
98void TextMessageIndicator::computeSizeAndResize()
99{
100 // determine text rectangle
101 const QRect textRect = computeTextRect(mMessage, mSymbol.width());
102 int width = textRect.width();
103 int height = textRect.height();
104
105 if (!mDetails.isEmpty()) {
106 // determine details text rectangle
107 const QRect detailsRect = computeTextRect(mDetails, mSymbol.width());
108 width = qMax(width, detailsRect.width());
109 height += detailsRect.height();
110
111 // plus add a ~60% line spacing
112 mLineSpacing = static_cast<int>(fontMetrics().height() * 0.6);
113 height += mLineSpacing;
114 }
115
116 // update geometry with icon information
117 if (!mSymbol.isNull()) {
118 width += 2 + mSymbol.width();
119 height = qMax(height, mSymbol.height());
120 }
121
122 // resize widget
123 resize(QRect(0, 0, width + 10, height + 8).size());
124
125 // if the layout is RtL, we can move it to the right place only after we
126 // know how much size it will take
127 int posX = parentWidget()->width() - geometry().width() - 20 - 1;
129 posX = 10;
130 }
131 move(posX, parentWidget()->height() - geometry().height() - 20);
132}
133
134bool TextMessageIndicator::eventFilter(QObject *obj, QEvent *event)
135{
136 /* if the parent object (scroll area) resizes, the message should
137 resize as well */
138 if (event->type() == QEvent::Resize) {
139 auto resizeEvent = static_cast<QResizeEvent *>(event);
140 if (resizeEvent->oldSize() != resizeEvent->size()) {
141 computeSizeAndResize();
142 }
143 }
144 // standard event processing
145 return QObject::eventFilter(obj, event);
146}
147
148void TextMessageIndicator::paintEvent(QPaintEvent * /* e */)
149{
150 const QRect textRect = computeTextRect(mMessage, mSymbol.width());
151
152 QRect detailsRect;
153 if (!mDetails.isEmpty()) {
154 detailsRect = computeTextRect(mDetails, mSymbol.width());
155 }
156
157 int textXOffset = 0;
158 // add 2 to account for the reduced drawRoundRect later
159 int textYOffset = (geometry().height() - textRect.height() - detailsRect.height() - mLineSpacing + 2) / 2;
160 int iconXOffset = 0;
161 int iconYOffset = !mSymbol.isNull() ? (geometry().height() - mSymbol.height()) / 2 : 0;
162 int shadowOffset = 1;
163
165 iconXOffset = 2 + textRect.width();
166 } else {
167 textXOffset = 2 + mSymbol.width();
168 }
169
170 // draw background
171 QPainter painter(this);
172 painter.setRenderHint(QPainter::Antialiasing, true);
173 painter.setPen(Qt::black);
174 painter.setBrush(palette().color(QPalette::Window));
175 painter.translate(0.5, 0.5);
176 painter.drawRoundedRect(1, 1, width() - 2, height() - 2, 1600 / width(), 1600 / height(), Qt::RelativeSize);
177
178 // draw icon if present
179 if (!mSymbol.isNull()) {
180 painter.drawPixmap(5 + iconXOffset, iconYOffset, mSymbol, 0, 0, mSymbol.width(), mSymbol.height());
181 }
182
183 const int xStartPoint = 5 + textXOffset;
184 const int yStartPoint = textYOffset;
185 const int textDrawingFlags = Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap;
186
187 // draw shadow and text
188 painter.setPen(palette().color(QPalette::Window).darker(115));
189 painter.drawText(xStartPoint + shadowOffset, yStartPoint + shadowOffset, textRect.width(), textRect.height(), textDrawingFlags, mMessage);
190 if (!mDetails.isEmpty()) {
191 painter.drawText(xStartPoint + shadowOffset,
192 yStartPoint + textRect.height() + mLineSpacing + shadowOffset,
193 textRect.width(),
194 detailsRect.height(),
195 textDrawingFlags,
196 mDetails);
197 }
198 painter.setPen(palette().color(QPalette::WindowText));
199 painter.drawText(xStartPoint, yStartPoint, textRect.width(), textRect.height(), textDrawingFlags, mMessage);
200 if (!mDetails.isEmpty()) {
201 painter.drawText(xStartPoint + shadowOffset,
202 yStartPoint + textRect.height() + mLineSpacing,
203 textRect.width(),
204 detailsRect.height(),
205 textDrawingFlags,
206 mDetails);
207 }
208}
209
210void TextMessageIndicator::mousePressEvent(QMouseEvent * /*e*/)
211{
212 if (mTimer) {
213 mTimer->stop();
214 }
215 hide();
216}
217
218#include "moc_textmessageindicator.cpp"
KIOCORE_EXPORT CopyJob * move(const QList< QUrl > &src, const QUrl &dest, JobFlags flags=DefaultFlags)
int height() const const
QPalette palette()
QPixmap pixmap(QWindow *window, const QSize &size, Mode mode, State state) const const
QIcon fromTheme(const QString &name)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool eventFilter(QObject *watched, QEvent *event)
T qobject_cast(QObject *object)
void setColor(ColorGroup group, ColorRole role, const QColor &color)
int height() const const
bool isNull() const const
int width() const const
void adjust(int dx1, int dy1, int dx2, int dy2)
int height() const const
int left() const const
int top() const const
void translate(const QPoint &offset)
int width() const const
bool isEmpty() const const
PM_SmallIconSize
virtual int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const=0
AlignLeft
LeftToRight
RelativeSize
TextWordWrap
void setSingleShot(bool singleShot)
void start()
void stop()
void timeout()
virtual bool event(QEvent *event) override
QFontMetrics fontMetrics() const const
void hide()
QWidget * parentWidget() const const
void move(const QPoint &)
virtual void resizeEvent(QResizeEvent *event)
void show()
void resize(const QSize &)
QStyle * style() const const
void update()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:51:28 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.