Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylandquickshellsurfaceitem.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
8#include <QtWaylandCompositor/QWaylandShellSurface>
9#include <QGuiApplication>
10
12
13void QWaylandQuickShellSurfaceItemPrivate::setShellSurface(QWaylandShellSurface *shellSurface)
14{
15 Q_Q(QWaylandQuickShellSurfaceItem);
16
17 if (m_shellSurface) {
18 QObject::disconnect(m_shellSurface, &QObject::destroyed, q, nullptr);
19 QObject::disconnect(m_shellSurface, &QWaylandShellSurface::modalChanged, q, nullptr);
20 }
21
22 m_shellSurface = shellSurface;
23
24 if (m_shellIntegration) {
25 q->removeEventFilter(m_shellIntegration);
26 delete m_shellIntegration;
27 m_shellIntegration = nullptr;
28 }
29
30 if (m_shellSurface) {
31 m_shellIntegration = m_shellSurface->createIntegration(q);
32 q->installEventFilter(m_shellIntegration);
33
34 QObject::connect(m_shellSurface, &QObject::destroyed,
35 q, [this]() {
36 setShellSurface(nullptr);
37 emit q_func()->shellSurfaceChanged();
38 });
39 QObject::connect(m_shellSurface, &QWaylandShellSurface::modalChanged,
40 q, [this]() {
41 if (m_shellSurface && m_shellSurface->isModal())
42 raise();
43 });
44 }
45}
46
47QWaylandQuickShellSurfaceItem *QWaylandQuickShellSurfaceItemPrivate::maybeCreateAutoPopup(QWaylandShellSurface* shellSurface)
48{
49 if (!m_autoCreatePopupItems)
50 return nullptr;
51
52 Q_Q(QWaylandQuickShellSurfaceItem);
53 auto *popupItem = new QWaylandQuickShellSurfaceItem(q);
54 popupItem->setShellSurface(shellSurface);
55 popupItem->setAutoCreatePopupItems(true);
56 QObject::connect(popupItem, &QWaylandQuickShellSurfaceItem::surfaceDestroyed,
57 popupItem, &QObject::deleteLater);
58 return popupItem;
59}
60
61/*!
62 * \qmltype ShellSurfaceItem
63 * \nativetype QWaylandQuickShellSurfaceItem
64 * \inherits WaylandQuickItem
65 * \inqmlmodule QtWayland.Compositor
66 * \since 5.8
67 * \brief A Qt Quick item type for displaying and interacting with a ShellSurface.
68 *
69 * This type is used to render \c xdg_shell, \c ivi_application, \c wl_shell or \c qt_shell
70 * surfaces as part of a Qt Quick scene.
71 *
72 * \sa WaylandQuickItem, XdgSurface, IviSurface, QtShellSurface, WlShellSurface
73 */
74
75/*!
76 * \class QWaylandQuickShellSurfaceItem
77 * \inmodule QtWaylandCompositor
78 * \since 5.8
79 * \brief The QWaylandQuickShellSurfaceItem class provides a Qt Quick item that represents a QWaylandShellSurface.
80 *
81 * This type is used to render \c xdg_shell, \c ivi_application, \c wl_shell or \c qt_shell
82 * surfaces as part of a Qt Quick scene.
83 *
84 * \sa QWaylandQuickItem, QWaylandXdgSurface, QWaylandIviSurface, QWaylandWlShellSurface
85 */
86
87/*!
88 * Constructs a QWaylandQuickWlShellSurfaceItem with the given \a parent.
89 */
90QWaylandQuickShellSurfaceItem::QWaylandQuickShellSurfaceItem(QQuickItem *parent)
91 : QWaylandQuickItem(*new QWaylandQuickShellSurfaceItemPrivate(), parent)
92{
93}
94
95QWaylandQuickShellSurfaceItem::~QWaylandQuickShellSurfaceItem()
96{
97 Q_D(QWaylandQuickShellSurfaceItem);
98
99 d->setShellSurface(nullptr);
100}
101
102/*!
103 * \internal
104 */
105QWaylandQuickShellSurfaceItem::QWaylandQuickShellSurfaceItem(QWaylandQuickShellSurfaceItemPrivate &dd, QQuickItem *parent)
106 : QWaylandQuickItem(dd, parent)
107{
108}
109
110/*!
111 * \qmlproperty ShellSurface QtWayland.Compositor::ShellSurfaceItem::shellSurface
112 *
113 * This property holds the ShellSurface rendered by this ShellSurfaceItem.
114 * It may either be an XdgSurfaceV5, WlShellSurface or IviSurface depending on which shell protocol
115 * is in use.
116 */
117
118/*!
119 * \property QWaylandQuickShellSurfaceItem::shellSurface
120 *
121 * This property holds the QWaylandShellSurface rendered by this QWaylandQuickShellSurfaceItem.
122 * It may either be a QWaylandXdgSurfaceV5, QWaylandWlShellSurface or QWaylandIviSurface depending
123 * on which shell protocol is in use.
124 */
125QWaylandShellSurface *QWaylandQuickShellSurfaceItem::shellSurface() const
126{
127 Q_D(const QWaylandQuickShellSurfaceItem);
128 return d->m_shellSurface;
129}
130
131void QWaylandQuickShellSurfaceItem::setShellSurface(QWaylandShellSurface *shellSurface)
132{
133 Q_D(QWaylandQuickShellSurfaceItem);
134 if (d->m_shellSurface == shellSurface)
135 return;
136
137 d->setShellSurface(shellSurface);
138
139 emit shellSurfaceChanged();
140}
141
142/*!
143 * \qmlproperty Item QtWayland.Compositor::ShellSurfaceItem::moveItem
144 *
145 * This property holds the move item for this ShellSurfaceItem. This is the item that will be moved
146 * when the clients request the ShellSurface to be moved, maximized, resized etc. This property is
147 * useful when implementing server-side decorations.
148 */
149
150/*!
151 * \property QWaylandQuickShellSurfaceItem::moveItem
152 *
153 * This property holds the move item for this QWaylandQuickShellSurfaceItem. This is the item that
154 * will be moved when the clients request the QWaylandShellSurface to be moved, maximized, resized
155 * etc. This property is useful when implementing server-side decorations.
156 */
157QQuickItem *QWaylandQuickShellSurfaceItem::moveItem() const
158{
159 Q_D(const QWaylandQuickShellSurfaceItem);
160 return d->m_moveItem ? d->m_moveItem : const_cast<QWaylandQuickShellSurfaceItem *>(this);
161}
162
163void QWaylandQuickShellSurfaceItem::setMoveItem(QQuickItem *moveItem)
164{
165 Q_D(QWaylandQuickShellSurfaceItem);
166 moveItem = moveItem ? moveItem : this;
167 if (this->moveItem() == moveItem)
168 return;
169 d->m_moveItem = moveItem;
170 moveItemChanged();
171}
172
173/*!
174 * \qmlproperty bool QtWayland.Compositor::ShellSurfaceItem::autoCreatePopupItems
175 * \default true
176 *
177 * This property holds whether ShellSurfaceItems for popups parented to the shell
178 * surface managed by this item should automatically be created.
179 */
180
181/*!
182 * \property QWaylandQuickShellSurfaceItem::autoCreatePopupItems
183 *
184 * This property holds whether QWaylandQuickShellSurfaceItems for popups
185 * parented to the shell surface managed by this item should automatically be created.
186 */
187bool QWaylandQuickShellSurfaceItem::autoCreatePopupItems()
188{
189 Q_D(const QWaylandQuickShellSurfaceItem);
190 return d->m_autoCreatePopupItems;
191}
192
193void QWaylandQuickShellSurfaceItem::setAutoCreatePopupItems(bool enabled)
194{
195 Q_D(QWaylandQuickShellSurfaceItem);
196
197 if (enabled == d->m_autoCreatePopupItems)
198 return;
199
200 d->m_autoCreatePopupItems = enabled;
201 emit autoCreatePopupItemsChanged();
202}
203
204/*!
205\class QWaylandQuickShellEventFilter
206\brief QWaylandQuickShellEventFilter implements a Wayland popup grab
207\internal
208*/
209
210void QWaylandQuickShellEventFilter::startFilter(QWaylandClient *client, CallbackFunction closePopups)
211{
212 if (!self)
213 self = new QWaylandQuickShellEventFilter(qGuiApp);
214 if (!self->eventFilterInstalled) {
215 qGuiApp->installEventFilter(self);
216 self->eventFilterInstalled = true;
217 self->client = client;
218 self->closePopups = closePopups;
219 }
220}
221
222void QWaylandQuickShellEventFilter::cancelFilter()
223{
224 if (!self)
225 return;
226 if (self->eventFilterInstalled && !self->waitForRelease)
227 self->stopFilter();
228}
229
230void QWaylandQuickShellEventFilter::stopFilter()
231{
232 if (eventFilterInstalled) {
233 qGuiApp->removeEventFilter(this);
234 eventFilterInstalled = false;
235 }
236}
237QWaylandQuickShellEventFilter *QWaylandQuickShellEventFilter::self = nullptr;
238
239QWaylandQuickShellEventFilter::QWaylandQuickShellEventFilter(QObject *parent)
240 : QObject(parent)
241{
242}
243
244bool QWaylandQuickShellEventFilter::eventFilter(QObject *receiver, QEvent *e)
245{
246 if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease) {
247 bool press = e->type() == QEvent::MouseButtonPress;
248 if (press && !waitForRelease) {
249 // The user clicked something: we need to close popups unless this press is caught later
250 if (!mousePressTimeout.isActive())
251 mousePressTimeout.start(0, this);
252 }
253
254 QQuickItem *item = qobject_cast<QQuickItem*>(receiver);
255 if (!item)
256 return false;
257
258 QMouseEvent *event = static_cast<QMouseEvent*>(e);
259 QWaylandQuickShellSurfaceItem *shellSurfaceItem = qobject_cast<QWaylandQuickShellSurfaceItem*>(item);
260 bool finalRelease = (event->type() == QEvent::MouseButtonRelease) && (event->buttons() == Qt::NoButton);
261 bool popupClient = shellSurfaceItem && shellSurfaceItem->surface() && shellSurfaceItem->surface()->client() == client;
262
263 if (waitForRelease) {
264 // We are eating events until all mouse buttons are released
265 if (finalRelease) {
266 waitForRelease = false;
267 stopFilter();
268 }
269 return true;
270 }
271
272 if (finalRelease && mousePressTimeout.isActive()) {
273 // the user somehow managed to press and release the mouse button in 0 milliseconds
274 qWarning("Badly written autotest detected");
275 mousePressTimeout.stop();
276 stopFilter();
277 }
278
279 if (press && !shellSurfaceItem && !QQmlProperty(item, QStringLiteral("qtwayland_blocking_overlay")).isValid()) {
280 // the user clicked on something that's not blocking mouse events
281 e->ignore(); //propagate the event to items below
282 return true; // don't give the event to the item
283 }
284
285 mousePressTimeout.stop(); // we've got this
286
287 if (press && !popupClient) {
288 // The user clicked outside the active popup's client. The popups should
289 // be closed, but the event filter will stay to catch the release-
290 // event before removing itself.
291 waitForRelease = true;
292 closePopups();
293 return true;
294 }
295 }
296
297 return false;
298}
299
300void QWaylandQuickShellEventFilter::timerEvent(QTimerEvent *event)
301{
302 if (event->timerId() == mousePressTimeout.timerId()) {
303 mousePressTimeout.stop();
304 closePopups();
305 stopFilter();
306 // Don't wait for release: Since the press wasn't accepted,
307 // the release won't be delivered.
308 }
309}
310
311static QWaylandQuickShellSurfaceItem *findSurfaceItemFromMoveItem(QQuickItem *moveItem)
312{
313 if (Q_UNLIKELY(!moveItem))
314 return nullptr;
315 if (auto *surf = qobject_cast<QWaylandQuickShellSurfaceItem *>(moveItem))
316 return surf;
317
318 const auto childItems = moveItem->childItems();
319 for (auto *item : childItems) {
320 if (auto *surf = findSurfaceItemFromMoveItem(item))
321 return surf;
322 }
323 return nullptr;
324}
325
326static inline bool onTop(QWaylandQuickShellSurfaceItem *surf)
327{
328 return surf->staysOnTop() || (surf->shellSurface() && surf->shellSurface()->isModal());
329}
330
331static inline bool onBottom(QWaylandQuickShellSurfaceItem *surf)
332{
333 return surf->staysOnBottom() && !(surf->shellSurface() && surf->shellSurface()->isModal());
334}
335
336/*
337 To raise a surface, find the topmost suitable surface and place above that.
338 We start from the top and:
339 If we don't have staysOnTop, skip all surfaces with staysOnTop
340 If we have staysOnBottom, skip all surfaces that don't have staysOnBottom
341 A modal dialog is handled as if it had staysOnTop
342 */
343void QWaylandQuickShellSurfaceItemPrivate::raise()
344{
345 Q_Q(QWaylandQuickShellSurfaceItem);
346 auto *moveItem = q->moveItem();
347 QQuickItem *parent = moveItem->parentItem();
348 if (!parent || !m_shellSurface)
349 return;
350 const bool putOnTop = staysOnTop || m_shellSurface->isModal();
351 const bool putOnBottom = staysOnBottom && !m_shellSurface->isModal();
352
353 const QList<QQuickItem *> childItems = parent->childItems();
354 auto it = childItems.crbegin();
355 const auto end = childItems.crend();
356 auto skip = [putOnTop, putOnBottom](QQuickItem *item) {
357 if (auto *surf = findSurfaceItemFromMoveItem(item))
358 return (!putOnTop && onTop(surf)) || (putOnBottom && !onBottom(surf));
359 return true; // ignore any other Quick items that may be there
360 };
361 while (it != end && skip(*it))
362 ++it;
363 if (it != end) {
364 QQuickItem *top = *it;
365 if (moveItem != top)
366 moveItem->stackAfter(top);
367 }
368}
369
370/*
371 To lower a surface, find the lowest suitable surface and place below that.
372 We start from the bottom and:
373 If we don't have staysOnBottom, skip all surfaces with staysOnBottom
374 If we have staysOnTop, skip all surfaces that don't have staysOnTop
375 A modal dialog is handled as if it had staysOnTop
376 */
377void QWaylandQuickShellSurfaceItemPrivate::lower()
378{
379 Q_Q(QWaylandQuickShellSurfaceItem);
380 auto *moveItem = q->moveItem();
381 QQuickItem *parent = moveItem->parentItem();
382 if (!parent || !m_shellSurface)
383 return;
384 const bool putOnTop = staysOnTop || m_shellSurface->isModal();
385 const bool putOnBottom = staysOnBottom && !m_shellSurface->isModal();
386
387 const QList<QQuickItem *> childItems = parent->childItems();
388 auto it = childItems.cbegin();
389 auto skip = [putOnTop, putOnBottom](QQuickItem *item) {
390 if (auto *surf = findSurfaceItemFromMoveItem(item))
391 return (!putOnBottom && onBottom(surf)) || (putOnTop && !onTop(surf));
392 return true; // ignore any other Quick items that may be there
393 };
394 while (skip(*it))
395 ++it;
396
397 QQuickItem *bottom = *it;
398 if (moveItem != bottom)
399 moveItem->stackBefore(bottom);
400}
401
402/*!
403 * \property QWaylandQuickShellSurfaceItem::staysOnTop
404 *
405 * Keep this item above other Wayland surfaces
406 */
407bool QWaylandQuickShellSurfaceItem::staysOnTop() const
408{
409 Q_D(const QWaylandQuickShellSurfaceItem);
410 return d->staysOnTop;
411}
412
413void QWaylandQuickShellSurfaceItem::setStaysOnTop(bool onTop)
414{
415 Q_D(QWaylandQuickShellSurfaceItem);
416 if (d->staysOnTop == onTop)
417 return;
418 d->staysOnTop = onTop;
419 if (d->staysOnBottom) {
420 d->staysOnBottom = false;
421 emit staysOnBottomChanged();
422 }
423 // We need to call raise() even if onTop is false, since we need to stack under any other
424 // staysOnTop surfaces in that case
425 raise();
426 emit staysOnTopChanged();
427 Q_ASSERT(!(d->staysOnTop && d->staysOnBottom));
428}
429
430/*!
431 * \property QWaylandQuickShellSurfaceItem::staysOnBottom
432 *
433 * Keep this item above other Wayland surfaces
434 */
435bool QWaylandQuickShellSurfaceItem::staysOnBottom() const
436{
437 Q_D(const QWaylandQuickShellSurfaceItem);
438 return d->staysOnBottom;
439}
440
441void QWaylandQuickShellSurfaceItem::setStaysOnBottom(bool onBottom)
442{
443 Q_D(QWaylandQuickShellSurfaceItem);
444 if (d->staysOnBottom == onBottom)
445 return;
446 d->staysOnBottom = onBottom;
447 if (d->staysOnTop) {
448 d->staysOnTop = false;
449 emit staysOnTopChanged();
450 }
451 // We need to call lower() even if onBottom is false, since we need to stack over any other
452 // staysOnBottom surfaces in that case
453 lower();
454 emit staysOnBottomChanged();
455 Q_ASSERT(!(d->staysOnTop && d->staysOnBottom));
456}
457
458QT_END_NAMESPACE
459
460#include "moc_qwaylandquickshellsurfaceitem_p.cpp"
461
462#include "moc_qwaylandquickshellsurfaceitem.cpp"
Combined button and popup list for selecting options.
static bool onBottom(QWaylandQuickShellSurfaceItem *surf)
static QWaylandQuickShellSurfaceItem * findSurfaceItemFromMoveItem(QQuickItem *moveItem)
static bool onTop(QWaylandQuickShellSurfaceItem *surf)