1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Quick Extras module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickmousethief_p.h"
41
42#include <QtCore/QMetaObject>
43#include <QtQuick/QQuickWindow>
44
45QQuickMouseThief::QQuickMouseThief(QObject *parent) :
46 QObject(parent),
47 mItem(0),
48 mReceivedPressEvent(false),
49 mAcceptCurrentEvent(false)
50{
51}
52
53bool QQuickMouseThief::receivedPressEvent() const
54{
55 return mReceivedPressEvent;
56}
57
58void QQuickMouseThief::setReceivedPressEvent(bool receivedPressEvent)
59{
60 if (receivedPressEvent != mReceivedPressEvent) {
61 mReceivedPressEvent = receivedPressEvent;
62 emit receivedPressEventChanged();
63 }
64}
65
66void QQuickMouseThief::grabMouse(QQuickItem *item)
67{
68 if (item) {
69 mItem = item;
70
71 // Handle the case where someone makes the PieMenu an orphan. E.g.:
72 // property PieMenu pieMenu: PieMenu {}
73 // They have to set the parent explicitly if they want that to work.
74 // This can also be null if the menu is visible when constructed.
75 if (mItem->window()) {
76 // We install an event filter so that we can track release events.
77 // This is because the MouseArea that we steal the mouse from stores the
78 // pressed flag (before item is visible) and we don't, so we don't know
79 // that it's a release.
80 mItem->grabMouse();
81 mItem->window()->installEventFilter(filterObj: this);
82 } else {
83 // The menu was visible when constructed (visible: true), so install the
84 // event filter later on.
85 connect(sender: mItem, SIGNAL(windowChanged(QQuickWindow*)),
86 receiver: this, SLOT(itemWindowChanged(QQuickWindow*)));
87 }
88 }
89}
90
91void QQuickMouseThief::ungrabMouse()
92{
93 if (mItem) {
94 // This can be null if someone does the following:
95 // PieMenu { Component.onCompleted: { visible = true; visible = false; }
96 if (mItem->window()) {
97 if (mItem->window()->mouseGrabberItem() == mItem) {
98 mItem->ungrabMouse();
99 }
100 mItem->window()->removeEventFilter(obj: this);
101 }
102 mItem = 0;
103 }
104}
105
106void QQuickMouseThief::acceptCurrentEvent()
107{
108 mAcceptCurrentEvent = true;
109}
110
111void QQuickMouseThief::itemWindowChanged(QQuickWindow *window)
112{
113 // The window will be null when the application is closing.
114 if (window) {
115 // This can be null if someone does the following:
116 // PieMenu { Component.onCompleted: { visible = true; visible = false; }
117 if (mItem) {
118 mItem->grabMouse();
119 window->installEventFilter(filterObj: this);
120 }
121 }
122}
123
124bool QQuickMouseThief::eventFilter(QObject *, QEvent *event)
125{
126 if (!mItem)
127 return false;
128
129 // The PieMenu QML code dictates which events are accepted by
130 // setting this property when it responds to our signals.
131 mAcceptCurrentEvent = false;
132
133 if (event->type() == QEvent::MouseButtonRelease) {
134 const QPointF mouseWindowPos = static_cast<QMouseEvent *>(event)->windowPos();
135 emitReleased(pos: mouseWindowPos);
136 // Even if the release should close the menu, we should emit
137 // clicked to be consistent since we have to do it ourselves.
138 bool releaseAccepted = mAcceptCurrentEvent;
139 mAcceptCurrentEvent = false;
140
141 emitClicked(pos: mouseWindowPos);
142 if (!mAcceptCurrentEvent) {
143 // We might not have accepted click, but we may have accepted release.
144 mAcceptCurrentEvent = releaseAccepted;
145 }
146 } else if (event->type() == QEvent::MouseButtonPress) {
147 emitPressed(pos: static_cast<QMouseEvent *>(event)->windowPos());
148 } else if (event->type() == QEvent::TouchEnd) {
149 // The finger(s) were lifted off the screen. We don't get this as a release event since
150 // we're doing our own custom handling, so we handle it here.
151 QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
152 const QList<QTouchEvent::TouchPoint> points = touchEvent->touchPoints();
153 // We only care about one finger. If there's more than one, ignore them.
154 if (!points.isEmpty()) {
155 const QPointF pos = points.first().pos();
156 emitReleased(pos);
157 // Even if the release should close the menu, we should emit
158 // clicked to be consistent since we have to do it ourselves.
159 bool releaseAccepted = mAcceptCurrentEvent;
160 mAcceptCurrentEvent = false;
161
162 emitClicked(pos);
163 if (!mAcceptCurrentEvent) {
164 // We might not have accepted click, but we may have accepted release.
165 mAcceptCurrentEvent = releaseAccepted;
166 }
167 }
168 } else if (event->type() == QEvent::TouchBegin) {
169 QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
170 const QList<QTouchEvent::TouchPoint> points = touchEvent->touchPoints();
171 if (!points.isEmpty()) {
172 emitPressed(pos: points.first().pos());
173 }
174 } else if (event->type() == QEvent::TouchUpdate) {
175 QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
176 const QList<QTouchEvent::TouchPoint> points = touchEvent->touchPoints();
177 if (!points.isEmpty()) {
178 const QPointF mappedPos = mItem->mapFromScene(point: points.first().pos());
179 emit touchUpdate(mouseX: mappedPos.x(), mouseY: mappedPos.y());
180 }
181 }
182 return mAcceptCurrentEvent;
183}
184
185void QQuickMouseThief::emitPressed(const QPointF &pos)
186{
187 setReceivedPressEvent(true);
188
189 // PieMenu can't detect presses outside its own MouseArea,
190 // so we need to provide these as well.
191 QPointF mappedPos = mItem->mapFromScene(point: pos);
192 emit pressed(mouseX: mappedPos.x(), mouseY: mappedPos.y());
193}
194
195void QQuickMouseThief::emitReleased(const QPointF &pos)
196{
197 QPointF mappedPos = mItem->mapFromScene(point: pos);
198 emit released(mouseX: mappedPos.x(), mouseY: mappedPos.y());
199}
200
201void QQuickMouseThief::emitClicked(const QPointF &pos)
202{
203 // mItem can be destroyed if the slots connected to released() caused it to be hidden.
204 // That's fine for us, since PieMenu doesn't need to handle anything if released()
205 // is emitted when the triggerMode is PieMenu.TriggerOnRelease.
206 if (mItem) {
207 QPointF mappedPos = mItem->mapFromScene(point: pos);
208 // Since we are creating the release events, we must also handle clicks.
209 emit clicked(mouseX: mappedPos.x(), mouseY: mappedPos.y());
210 }
211}
212

source code of qtquickcontrols/src/extras/Private/qquickmousethief.cpp