QtCanpool 3.3.0
载入中...
搜索中...
未找到
qxtoastmanager.cpp
浏览该文件的文档.
1
5#include "qxtoastmanager.h"
6#include "qxtoastmanager_p.h"
7
8#include <QEvent>
9
11
12namespace
13{
14
20QSize outerSize(const QxToast *toast, int plate)
21{
22 const int width = plate + 2 * QxToast::ShadowMargin;
23 return QSize(width, toast->heightForWidth(width));
24}
25
26} // namespace
27
28QxToastManagerPrivate::QxToastManagerPrivate() = default;
29
30void QxToastManagerPrivate::adopt(QxToast *toast)
31{
32 Q_Q(QxToastManager);
33
34 // Both connections carry the toast, so they go away with it. dismissRequested
35 // is what keeps the stack honest when the toast decides on its own - the
36 // clock, a click, or a caller holding the pointer; dismissed is what frees
37 // the widget once it is off screen.
38 QObject::connect(toast, &QxToast::dismissRequested, q, [this, toast]() {
39 remove(toast);
40 });
41 QObject::connect(toast, &QxToast::dismissed, toast, &QObject::deleteLater);
42 QObject::connect(toast, &QxToast::sizeHintChanged, q, [this]() {
43 relayout();
44 });
45
46 m_toasts.append(toast);
47}
48
49void QxToastManagerPrivate::remove(QxToast *toast)
50{
51 Q_Q(QxToastManager);
52
53 const int index = m_toasts.indexOf(toast);
54 if (index < 0) {
55 // Already gone. A dismissal that started here comes back through
56 // dismissRequested, and it has to find this harmless.
57 return;
58 }
59
60 m_toasts.removeAt(index);
61 relayout();
62 Q_EMIT q->toastDismissed(toast);
63
64 // Whatever took the toast out of the stack, the widget still has to leave
65 // the screen. When the dismissal started in dismiss() this is a no-op.
66 toast->dismiss();
67}
68
69void QxToastManagerPrivate::relayout()
70{
71 if (!m_host) {
72 return;
73 }
74
75 const int shadow = QxToast::ShadowMargin;
76 // margin() and spacing() describe the plates. The widget of a toast is
77 // ShadowMargin larger on every side - that is where its shadow goes - so
78 // that room comes out of the spacing before anything is placed.
79 const int gap = qMax(0, m_spacing - 2 * shadow);
80 const QRect area = m_host->rect();
81 const bool atTop = m_position == QxToastManager::TopLeft || m_position == QxToastManager::TopRight;
82 const bool atLeft = m_position == QxToastManager::TopLeft || m_position == QxToastManager::BottomLeft;
83
84 const int plate = plateWidth();
85
86 // A column anchored to the bottom has to know how tall it is before it can
87 // be placed, so it is measured in plate coordinates first.
88 int stackHeight = 0;
89 for (int i = 0; i < m_toasts.count(); ++i) {
90 stackHeight += outerSize(m_toasts.at(i), plate).height() - 2 * shadow;
91 }
92 stackHeight += qMax(0, m_toasts.count() - 1) * m_spacing;
93
94 int top = atTop ? area.top() + m_margin : area.bottom() - m_margin - stackHeight + 1;
95
96 for (int i = 0; i < m_toasts.count(); ++i) {
97 QxToast *toast = m_toasts.at(i);
98 const QSize outer = outerSize(toast, plate);
99 const int left = atLeft ? area.left() + m_margin : area.right() - m_margin - plate + 1;
100 toast->setGeometry(left - shadow, top - shadow, outer.width(), outer.height());
101 top += outer.height() + gap;
102 }
103}
104
105int QxToastManagerPrivate::plateWidth() const
106{
107 if (!m_host) {
108 return m_maxWidth;
109 }
110 const int available = qMax(1, m_host->width() - 2 * m_margin);
111 return qMin(m_maxWidth, available);
112}
113
114QxToastManager::QxToastManager(QWidget *host, QObject *parent)
115 : QObject(parent)
116 , d_ptr(new QxToastManagerPrivate())
117{
118 Q_D(QxToastManager);
119 d->setPublic(this);
120 d->m_host = host;
121 if (host) {
122 // The stack is anchored to the host's edges, so a resize moves it.
123 host->installEventFilter(this);
124 }
125}
126
131
132QWidget *QxToastManager::host() const
133{
134 Q_D(const QxToastManager);
135 return d->m_host;
136}
137
139{
140 Q_D(const QxToastManager);
141 return d->m_maxVisible;
142}
143
145{
146 Q_D(QxToastManager);
147 d->m_maxVisible = qMax(1, count);
148
149 while (d->m_toasts.count() > d->m_maxVisible) {
150 d->remove(d->m_toasts.first());
151 }
152 d->relayout();
153}
154
156{
157 Q_D(const QxToastManager);
158 return d->m_position;
159}
160
162{
163 Q_D(QxToastManager);
164 if (d->m_position == position) {
165 return;
166 }
167 d->m_position = position;
168 d->relayout();
169}
170
172{
173 Q_D(const QxToastManager);
174 return d->m_margin;
175}
176
178{
179 Q_D(QxToastManager);
180 d->m_margin = qMax(0, pixels);
181 d->relayout();
182}
183
185{
186 Q_D(const QxToastManager);
187 return d->m_spacing;
188}
189
191{
192 Q_D(QxToastManager);
193 d->m_spacing = qMax(0, pixels);
194 d->relayout();
195}
196
198{
199 Q_D(const QxToastManager);
200 return d->m_maxWidth;
201}
202
204{
205 Q_D(QxToastManager);
206 // Clamped to the range a plate can actually take, so that the width the
207 // manager places at and the width the toast draws are never two different
208 // numbers.
209 d->m_maxWidth = qBound(QxToast::MinPlateWidth, pixels, QxToast::MaxPlateWidth);
210 d->relayout();
211}
212
214{
215 Q_D(const QxToastManager);
216 return d->m_fadeDuration;
217}
218
220{
221 Q_D(QxToastManager);
222 d->m_fadeDuration = qMax(0, ms);
223 for (QxToast *toast : d->m_toasts) {
224 toast->setFadeDuration(d->m_fadeDuration);
225 }
226}
227
229{
230 Q_D(const QxToastManager);
231 return d->m_toasts.count();
232}
233
234QList<QxToast *> QxToastManager::toasts() const
235{
236 Q_D(const QxToastManager);
237 return d->m_toasts;
238}
239
241{
242 Q_D(const QxToastManager);
243 if (index < 0 || index >= d->m_toasts.count()) {
244 return Q_NULLPTR;
245 }
246 return d->m_toasts.at(index);
247}
248
250{
251 Q_D(const QxToastManager);
252 return d->m_toasts.contains(toast);
253}
254
255QxToast *QxToastManager::show(const QString &text, QxToast::Level level, int timeoutMs)
256{
257 Q_D(QxToastManager);
258 if (!d->m_host) {
259 qWarning("QxToastManager: there is no host window to show a toast in");
260 return Q_NULLPTR;
261 }
262
263 // Making room before the new toast joins keeps the stack at its height: the
264 // column is never taller than maxVisible(), not even for the instant it
265 // takes to evict and add.
266 while (!d->m_toasts.isEmpty() && d->m_toasts.count() >= d->m_maxVisible) {
267 d->remove(d->m_toasts.first());
268 }
269
270 QxToast *toast = new QxToast(text, level, d->m_host);
271 toast->setTimeout(timeoutMs);
272 toast->setFadeDuration(d->m_fadeDuration);
273 d->adopt(toast);
274
275 d->relayout();
276 toast->start();
277 Q_EMIT toastShown(toast);
278 return toast;
279}
280
282{
283 Q_D(QxToastManager);
284 // remove() rewrites the stack, so it is walked over a copy of it.
285 const QList<QxToast *> toasts = d->m_toasts;
286 for (QxToast *toast : toasts) {
287 d->remove(toast);
288 }
289}
290
291bool QxToastManager::eventFilter(QObject *watched, QEvent *event)
292{
293 Q_D(QxToastManager);
294 if (watched == d->m_host && event->type() == QEvent::Resize) {
295 d->relayout();
296 }
297 return QObject::eventFilter(watched, event);
298}
299
The state behind QxToastManager: the host, the stack and the placement rules.
The stack of toasts of one window.
Position position() const
void setSpacing(int pixels)
void setMargin(int pixels)
int maxWidth() const
Widest a plate may get; a narrow host shrinks it further.
void setMaxVisible(int count)
QList< QxToast * > toasts() const
The toasts that are on screen, oldest first - the order they stack in.
void setPosition(Position position)
Position
The corner of the host the stack is anchored to.
int margin() const
Distance kept between the plate and the two edges it is anchored to.
QWidget * host() const
int fadeDuration() const
Milliseconds the fade in and the fade out take; zero turns them off.
void dismissAll()
Dismisses every toast of the stack, oldest first.
void setMaxWidth(int pixels)
bool contains(QxToast *toast) const
void toastShown(QxToast *toast)
A toast has joined the stack and is on its way in.
int spacing() const
Distance between two plates of the stack.
QxToast * show(const QString &text, QxToast::Level level=QxToast::Information, int timeoutMs=QxToast::DefaultTimeout)
Creates a toast, puts it at the end of the stack and starts it.
QxToast * toastAt(int index) const
The toast at index of the stack, null when it is out of range.
int maxVisible() const
How many toasts share the screen before the oldest one is evicted.
bool eventFilter(QObject *watched, QEvent *event) override
One short message, shown over the window it belongs to and gone a few seconds later.
Definition qxtoast.h:54
Level
What the message is about; it selects the icon and the accent colour.
Definition qxtoast.h:58
void dismiss()
Takes the toast off screen: the countdown stops, the fade out runs and dismissed() follows once the w...
Definition qxtoast.cpp:358
void setFadeDuration(int ms)
Zero turns the animation off, which makes the life cycle immediate.
Definition qxtoast.cpp:295
static constexpr int MaxPlateWidth
Definition qxtoast.h:82
void start()
Puts the toast on screen, fades it in and starts the countdown.
Definition qxtoast.cpp:331
static constexpr int MinPlateWidth
The narrowest and the widest plate a toast settles for, whatever width it is given.
Definition qxtoast.h:81
void setTimeout(int timeoutMs)
Definition qxtoast.cpp:283
#define QX_APP_END_NAMESPACE
#define Q_NULLPTR
Copyleft (C) 2025 maminjie canpool@163.com SPDX-License-Identifier: MulanPSL-2.0
#define QX_APP_BEGIN_NAMESPACE
#define QX_FINI_PRIVATE()
QColor text