1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtWaylandCompositor module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 or (at your option) any later version |
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by |
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | #include "qwaylandquickshellsurfaceitem.h" |
31 | #include "qwaylandquickshellsurfaceitem_p.h" |
32 | |
33 | #include <QtWaylandCompositor/QWaylandShellSurface> |
34 | #include <QGuiApplication> |
35 | |
36 | QT_BEGIN_NAMESPACE |
37 | |
38 | QWaylandQuickShellSurfaceItem *QWaylandQuickShellSurfaceItemPrivate::maybeCreateAutoPopup(QWaylandShellSurface* shellSurface) |
39 | { |
40 | if (!m_autoCreatePopupItems) |
41 | return nullptr; |
42 | |
43 | Q_Q(QWaylandQuickShellSurfaceItem); |
44 | auto * = new QWaylandQuickShellSurfaceItem(q); |
45 | popupItem->setShellSurface(shellSurface); |
46 | popupItem->setAutoCreatePopupItems(true); |
47 | QObject::connect(sender: popupItem, signal: &QWaylandQuickShellSurfaceItem::surfaceDestroyed, slot: [popupItem](){ |
48 | popupItem->deleteLater(); |
49 | }); |
50 | return popupItem; |
51 | } |
52 | |
53 | /*! |
54 | * \qmltype ShellSurfaceItem |
55 | * \instantiates QWaylandQuickShellSurfaceItem |
56 | * \inherits WaylandQuickItem |
57 | * \inqmlmodule QtWayland.Compositor |
58 | * \since 5.8 |
59 | * \brief A Qt Quick item type for displaying and interacting with a ShellSurface. |
60 | * |
61 | * This type is used to render \c wl_shell, \c xdg_shell or \c ivi_application surfaces as part of |
62 | * a Qt Quick scene. It handles moving and resizing triggered by clicking on the window decorations. |
63 | * |
64 | * \sa WaylandQuickItem, WlShellSurface, XdgSurfaceV5, IviSurface |
65 | */ |
66 | |
67 | /*! |
68 | * \class QWaylandQuickShellSurfaceItem |
69 | * \inmodule QtWaylandCompositor |
70 | * \since 5.8 |
71 | * \brief The QWaylandQuickShellSurfaceItem class provides a Qt Quick item that represents a QWaylandShellSurface. |
72 | * |
73 | * This class is used to render \c wl_shell, \c xdg_shell or \c ivi_application surfaces as part of |
74 | * a Qt Quick scene. It handles moving and resizing triggered by clicking on the window decorations. |
75 | * |
76 | * \sa QWaylandQuickItem, QWaylandWlShellSurface, QWaylandXdgSurfaceV5, QWaylandIviSurface |
77 | */ |
78 | |
79 | /*! |
80 | * Constructs a QWaylandQuickWlShellSurfaceItem with the given \a parent. |
81 | */ |
82 | QWaylandQuickShellSurfaceItem::QWaylandQuickShellSurfaceItem(QQuickItem *parent) |
83 | : QWaylandQuickItem(*new QWaylandQuickShellSurfaceItemPrivate(), parent) |
84 | { |
85 | } |
86 | |
87 | QWaylandQuickShellSurfaceItem::~QWaylandQuickShellSurfaceItem() |
88 | { |
89 | Q_D(QWaylandQuickShellSurfaceItem); |
90 | |
91 | if (d->m_shellIntegration) { |
92 | removeEventFilter(obj: d->m_shellIntegration); |
93 | delete d->m_shellIntegration; |
94 | } |
95 | } |
96 | |
97 | /*! |
98 | * \internal |
99 | */ |
100 | QWaylandQuickShellSurfaceItem::QWaylandQuickShellSurfaceItem(QWaylandQuickShellSurfaceItemPrivate &dd, QQuickItem *parent) |
101 | : QWaylandQuickItem(dd, parent) |
102 | { |
103 | } |
104 | |
105 | /*! |
106 | * \qmlproperty ShellSurface QtWaylandCompositor::ShellSurfaceItem::shellSurface |
107 | * |
108 | * This property holds the ShellSurface rendered by this ShellSurfaceItem. |
109 | * It may either be an XdgSurfaceV5, WlShellSurface or IviSurface depending on which shell protocol |
110 | * is in use. |
111 | */ |
112 | |
113 | /*! |
114 | * \property QWaylandQuickShellSurfaceItem::shellSurface |
115 | * |
116 | * This property holds the QWaylandShellSurface rendered by this QWaylandQuickShellSurfaceItem. |
117 | * It may either be a QWaylandXdgSurfaceV5, QWaylandWlShellSurface or QWaylandIviSurface depending |
118 | * on which shell protocol is in use. |
119 | */ |
120 | QWaylandShellSurface *QWaylandQuickShellSurfaceItem::shellSurface() const |
121 | { |
122 | Q_D(const QWaylandQuickShellSurfaceItem); |
123 | return d->m_shellSurface; |
124 | } |
125 | |
126 | void QWaylandQuickShellSurfaceItem::setShellSurface(QWaylandShellSurface *shellSurface) |
127 | { |
128 | Q_D(QWaylandQuickShellSurfaceItem); |
129 | if (d->m_shellSurface == shellSurface) |
130 | return; |
131 | |
132 | d->m_shellSurface = shellSurface; |
133 | |
134 | if (d->m_shellIntegration) { |
135 | removeEventFilter(obj: d->m_shellIntegration); |
136 | delete d->m_shellIntegration; |
137 | d->m_shellIntegration = nullptr; |
138 | } |
139 | |
140 | if (shellSurface) { |
141 | d->m_shellIntegration = shellSurface->createIntegration(item: this); |
142 | installEventFilter(filterObj: d->m_shellIntegration); |
143 | } |
144 | |
145 | emit shellSurfaceChanged(); |
146 | } |
147 | |
148 | /*! |
149 | * \qmlproperty Item QtWaylandCompositor::ShellSurfaceItem::moveItem |
150 | * |
151 | * This property holds the move item for this ShellSurfaceItem. This is the item that will be moved |
152 | * when the clients request the ShellSurface to be moved, maximized, resized etc. This property is |
153 | * useful when implementing server-side decorations. |
154 | */ |
155 | |
156 | /*! |
157 | * \property QWaylandQuickShellSurfaceItem::moveItem |
158 | * |
159 | * This property holds the move item for this QWaylandQuickShellSurfaceItem. This is the item that |
160 | * will be moved when the clients request the QWaylandShellSurface to be moved, maximized, resized |
161 | * etc. This property is useful when implementing server-side decorations. |
162 | */ |
163 | QQuickItem *QWaylandQuickShellSurfaceItem::moveItem() const |
164 | { |
165 | Q_D(const QWaylandQuickShellSurfaceItem); |
166 | return d->m_moveItem ? d->m_moveItem : const_cast<QWaylandQuickShellSurfaceItem *>(this); |
167 | } |
168 | |
169 | void QWaylandQuickShellSurfaceItem::setMoveItem(QQuickItem *moveItem) |
170 | { |
171 | Q_D(QWaylandQuickShellSurfaceItem); |
172 | moveItem = moveItem ? moveItem : this; |
173 | if (this->moveItem() == moveItem) |
174 | return; |
175 | d->m_moveItem = moveItem; |
176 | moveItemChanged(); |
177 | } |
178 | |
179 | /*! |
180 | * \qmlproperty bool QtWaylandCompositor::ShellSurfaceItem::autoCreatePopupItems |
181 | * |
182 | * This property holds whether ShellSurfaceItems for popups parented to the shell |
183 | * surface managed by this item should automatically be created. |
184 | */ |
185 | |
186 | /*! |
187 | * \property QWaylandQuickShellSurfaceItem::autoCreatePopupItems |
188 | * |
189 | * This property holds whether QWaylandQuickShellSurfaceItems for popups |
190 | * parented to the shell surface managed by this item should automatically be created. |
191 | */ |
192 | bool QWaylandQuickShellSurfaceItem::autoCreatePopupItems() |
193 | { |
194 | Q_D(const QWaylandQuickShellSurfaceItem); |
195 | return d->m_autoCreatePopupItems; |
196 | } |
197 | |
198 | void QWaylandQuickShellSurfaceItem::setAutoCreatePopupItems(bool enabled) |
199 | { |
200 | Q_D(QWaylandQuickShellSurfaceItem); |
201 | |
202 | if (enabled == d->m_autoCreatePopupItems) |
203 | return; |
204 | |
205 | d->m_autoCreatePopupItems = enabled; |
206 | emit autoCreatePopupItemsChanged(); |
207 | } |
208 | |
209 | /*! |
210 | \class QWaylandQuickShellEventFilter |
211 | \brief QWaylandQuickShellEventFilter implements a Wayland popup grab |
212 | \internal |
213 | */ |
214 | |
215 | void QWaylandQuickShellEventFilter::startFilter(QWaylandClient *client, CallbackFunction ) |
216 | { |
217 | if (!self) |
218 | self = new QWaylandQuickShellEventFilter(qGuiApp); |
219 | if (!self->eventFilterInstalled) { |
220 | qGuiApp->installEventFilter(filterObj: self); |
221 | self->eventFilterInstalled = true; |
222 | self->client = client; |
223 | self->closePopups = closePopups; |
224 | } |
225 | } |
226 | |
227 | void QWaylandQuickShellEventFilter::cancelFilter() |
228 | { |
229 | if (!self) |
230 | return; |
231 | if (self->eventFilterInstalled && !self->waitForRelease) |
232 | self->stopFilter(); |
233 | } |
234 | |
235 | void QWaylandQuickShellEventFilter::stopFilter() |
236 | { |
237 | if (eventFilterInstalled) { |
238 | qGuiApp->removeEventFilter(obj: this); |
239 | eventFilterInstalled = false; |
240 | } |
241 | } |
242 | QWaylandQuickShellEventFilter *QWaylandQuickShellEventFilter::self = nullptr; |
243 | |
244 | QWaylandQuickShellEventFilter::QWaylandQuickShellEventFilter(QObject *parent) |
245 | : QObject(parent) |
246 | { |
247 | } |
248 | |
249 | bool QWaylandQuickShellEventFilter::eventFilter(QObject *receiver, QEvent *e) |
250 | { |
251 | if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease) { |
252 | bool press = e->type() == QEvent::MouseButtonPress; |
253 | if (press && !waitForRelease) { |
254 | // The user clicked something: we need to close popups unless this press is caught later |
255 | if (!mousePressTimeout.isActive()) |
256 | mousePressTimeout.start(msec: 0, obj: this); |
257 | } |
258 | |
259 | QQuickItem *item = qobject_cast<QQuickItem*>(object: receiver); |
260 | if (!item) |
261 | return false; |
262 | |
263 | QMouseEvent *event = static_cast<QMouseEvent*>(e); |
264 | QWaylandQuickShellSurfaceItem *shellSurfaceItem = qobject_cast<QWaylandQuickShellSurfaceItem*>(object: item); |
265 | bool finalRelease = (event->type() == QEvent::MouseButtonRelease) && (event->buttons() == Qt::NoButton); |
266 | bool = shellSurfaceItem && shellSurfaceItem->surface() && shellSurfaceItem->surface()->client() == client; |
267 | |
268 | if (waitForRelease) { |
269 | // We are eating events until all mouse buttons are released |
270 | if (finalRelease) { |
271 | waitForRelease = false; |
272 | stopFilter(); |
273 | } |
274 | return true; |
275 | } |
276 | |
277 | if (finalRelease && mousePressTimeout.isActive()) { |
278 | // the user somehow managed to press and release the mouse button in 0 milliseconds |
279 | qWarning(msg: "Badly written autotest detected" ); |
280 | mousePressTimeout.stop(); |
281 | stopFilter(); |
282 | } |
283 | |
284 | if (press && !shellSurfaceItem && !QQmlProperty(item, QStringLiteral("qtwayland_blocking_overlay" )).isValid()) { |
285 | // the user clicked on something that's not blocking mouse events |
286 | e->ignore(); //propagate the event to items below |
287 | return true; // don't give the event to the item |
288 | } |
289 | |
290 | mousePressTimeout.stop(); // we've got this |
291 | |
292 | if (press && !popupClient) { |
293 | // The user clicked outside the active popup's client. The popups should |
294 | // be closed, but the event filter will stay to catch the release- |
295 | // event before removing itself. |
296 | waitForRelease = true; |
297 | closePopups(); |
298 | return true; |
299 | } |
300 | } |
301 | |
302 | return false; |
303 | } |
304 | |
305 | void QWaylandQuickShellEventFilter::timerEvent(QTimerEvent *event) |
306 | { |
307 | if (event->timerId() == mousePressTimeout.timerId()) { |
308 | mousePressTimeout.stop(); |
309 | closePopups(); |
310 | stopFilter(); |
311 | // Don't wait for release: Since the press wasn't accepted, |
312 | // the release won't be delivered. |
313 | } |
314 | } |
315 | |
316 | QT_END_NAMESPACE |
317 | |