1 | // Copyright (C) 2019 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qquickpointerdevicehandler_p_p.h" |
5 | #include <private/qquickitem_p.h> |
6 | #include <QMouseEvent> |
7 | #include <QDebug> |
8 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | /*! |
12 | \qmltype PointerDeviceHandler |
13 | \qmlabstract |
14 | \since 5.10 |
15 | \preliminary |
16 | \nativetype QQuickPointerDeviceHandler |
17 | \inherits PointerHandler |
18 | \inqmlmodule QtQuick |
19 | \brief Abstract handler for pointer events with device-specific constraints. |
20 | |
21 | An intermediate class (not registered as a QML type) for handlers which |
22 | allow filtering based on device type, pointer type, or keyboard modifiers. |
23 | */ |
24 | QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QQuickItem *parent) |
25 | : QQuickPointerHandler(*(new QQuickPointerDeviceHandlerPrivate), parent) |
26 | { |
27 | } |
28 | |
29 | QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QQuickPointerDeviceHandlerPrivate &dd, QQuickItem *parent) |
30 | : QQuickPointerHandler(dd, parent) |
31 | { |
32 | } |
33 | |
34 | QInputDevice::DeviceTypes QQuickPointerDeviceHandler::acceptedDevices() const |
35 | { |
36 | Q_D(const QQuickPointerDeviceHandler); |
37 | return d->acceptedDevices; |
38 | } |
39 | |
40 | QPointingDevice::PointerTypes QQuickPointerDeviceHandler::acceptedPointerTypes() const |
41 | { |
42 | Q_D(const QQuickPointerDeviceHandler); |
43 | return d->acceptedPointerTypes; |
44 | } |
45 | |
46 | /*! |
47 | \qmlproperty flags QtQuick::PointerDeviceHandler::acceptedButtons |
48 | |
49 | The mouse buttons which can activate this Pointer Handler. |
50 | |
51 | By default, this property is set to \l {QtQuick::MouseEvent::button} {Qt.LeftButton}. |
52 | It can be set to an OR combination of mouse buttons, and will ignore events |
53 | from other buttons. |
54 | |
55 | For example, a control could be made to respond to left and right clicks |
56 | in different ways, with two handlers: |
57 | |
58 | \qml |
59 | Item { |
60 | TapHandler { |
61 | onTapped: console.log("left clicked") |
62 | } |
63 | TapHandler { |
64 | acceptedButtons: Qt.RightButton |
65 | onTapped: console.log("right clicked") |
66 | } |
67 | } |
68 | \endqml |
69 | |
70 | \note Tapping on a touchscreen or tapping the stylus on a graphics tablet |
71 | emulates clicking the left mouse button. This behavior can be altered via |
72 | \l {PointerDeviceHandler::acceptedDevices}{acceptedDevices} or |
73 | \l {PointerDeviceHandler::acceptedPointerTypes}{acceptedPointerTypes}. |
74 | */ |
75 | Qt::MouseButtons QQuickPointerDeviceHandler::acceptedButtons() const |
76 | { |
77 | Q_D(const QQuickPointerDeviceHandler); |
78 | return d->acceptedButtons; |
79 | } |
80 | |
81 | void QQuickPointerDeviceHandler::setAcceptedButtons(Qt::MouseButtons buttons) |
82 | { |
83 | Q_D(QQuickPointerDeviceHandler); |
84 | if (d->acceptedButtons == buttons) |
85 | return; |
86 | |
87 | d->acceptedButtons = buttons; |
88 | emit acceptedButtonsChanged(); |
89 | } |
90 | |
91 | Qt::KeyboardModifiers QQuickPointerDeviceHandler::acceptedModifiers() const |
92 | { |
93 | Q_D(const QQuickPointerDeviceHandler); |
94 | return d->acceptedModifiers; |
95 | } |
96 | |
97 | /*! |
98 | \qmlproperty flags PointerDeviceHandler::acceptedDevices |
99 | |
100 | The types of pointing devices that can activate this Pointer Handler. |
101 | |
102 | By default, this property is set to |
103 | \l{QInputDevice::DeviceType}{PointerDevice.AllDevices}. |
104 | If you set it to an OR combination of device types, it will ignore events |
105 | from non-matching devices. |
106 | |
107 | For example, a control could be made to respond to mouse and stylus clicks |
108 | in one way, and touchscreen taps in another way, with two handlers: |
109 | |
110 | \qml |
111 | Item { |
112 | TapHandler { |
113 | acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus |
114 | onTapped: console.log("clicked") |
115 | } |
116 | TapHandler { |
117 | acceptedDevices: PointerDevice.TouchScreen |
118 | onTapped: console.log("tapped") |
119 | } |
120 | } |
121 | \endqml |
122 | |
123 | \note Not all platforms are yet able to distinguish mouse and touchpad; and |
124 | on those that do, you often want to make mouse and touchpad behavior the same. |
125 | */ |
126 | void QQuickPointerDeviceHandler::setAcceptedDevices(QPointingDevice::DeviceTypes acceptedDevices) |
127 | { |
128 | Q_D(QQuickPointerDeviceHandler); |
129 | if (d->acceptedDevices == acceptedDevices) |
130 | return; |
131 | |
132 | d->acceptedDevices = acceptedDevices; |
133 | emit acceptedDevicesChanged(); |
134 | } |
135 | |
136 | /*! |
137 | \qmlproperty flags PointerDeviceHandler::acceptedPointerTypes |
138 | |
139 | The types of pointing instruments (finger, stylus, eraser, etc.) |
140 | that can activate this Pointer Handler. |
141 | |
142 | By default, this property is set to |
143 | \l {QPointingDevice::PointerType} {PointerDevice.AllPointerTypes}. |
144 | If you set it to an OR combination of device types, it will ignore events |
145 | from non-matching \l {PointerDevice}{devices}. |
146 | |
147 | For example, a control could be made to respond to mouse, touch, and stylus clicks |
148 | in some way, but delete itself if tapped with an eraser tool on a graphics tablet, |
149 | with two handlers: |
150 | |
151 | \qml |
152 | Rectangle { |
153 | id: rect |
154 | TapHandler { |
155 | acceptedPointerTypes: PointerDevice.Generic | PointerDevice.Finger | PointerDevice.Pen |
156 | onTapped: console.log("clicked") |
157 | } |
158 | TapHandler { |
159 | acceptedPointerTypes: PointerDevice.Eraser |
160 | onTapped: rect.destroy() |
161 | } |
162 | } |
163 | \endqml |
164 | */ |
165 | void QQuickPointerDeviceHandler::setAcceptedPointerTypes(QPointingDevice::PointerTypes acceptedPointerTypes) |
166 | { |
167 | Q_D(QQuickPointerDeviceHandler); |
168 | if (d->acceptedPointerTypes == acceptedPointerTypes) |
169 | return; |
170 | |
171 | d->acceptedPointerTypes = acceptedPointerTypes; |
172 | emit acceptedPointerTypesChanged(); |
173 | } |
174 | |
175 | /*! |
176 | \qmlproperty flags PointerDeviceHandler::acceptedModifiers |
177 | |
178 | If this property is set, it will require the given keyboard modifiers to |
179 | be pressed in order to react to pointer events, and otherwise ignore them. |
180 | |
181 | If this property is set to \c Qt.KeyboardModifierMask (the default value), |
182 | then the PointerHandler ignores the modifier keys. |
183 | |
184 | For example, an \l [QML] Item could have two handlers of the same type, |
185 | one of which is enabled only if the required keyboard modifiers are |
186 | pressed: |
187 | |
188 | \qml |
189 | Item { |
190 | TapHandler { |
191 | acceptedModifiers: Qt.ControlModifier |
192 | onTapped: console.log("control-tapped") |
193 | } |
194 | TapHandler { |
195 | acceptedModifiers: Qt.NoModifier |
196 | onTapped: console.log("tapped") |
197 | } |
198 | } |
199 | \endqml |
200 | |
201 | If you set \c acceptedModifiers to an OR combination of modifier keys, |
202 | it means \e all of those modifiers must be pressed to activate the handler: |
203 | |
204 | \qml |
205 | Item { |
206 | TapHandler { |
207 | acceptedModifiers: Qt.ControlModifier | Qt.AltModifier | Qt.ShiftModifier |
208 | onTapped: console.log("control-alt-shift-tapped") |
209 | } |
210 | } |
211 | \endqml |
212 | |
213 | The available modifiers are as follows: |
214 | |
215 | \value NoModifier No modifier key is allowed. |
216 | \value ShiftModifier A Shift key on the keyboard must be pressed. |
217 | \value ControlModifier A Ctrl key on the keyboard must be pressed. |
218 | \value AltModifier An Alt key on the keyboard must be pressed. |
219 | \value MetaModifier A Meta key on the keyboard must be pressed. |
220 | \value KeypadModifier A keypad button must be pressed. |
221 | \value GroupSwitchModifier X11 only (unless activated on Windows by a command line argument). |
222 | A Mode_switch key on the keyboard must be pressed. |
223 | \value KeyboardModifierMask The handler does not care which modifiers are pressed. |
224 | |
225 | If you need even more complex behavior than can be achieved with |
226 | combinations of multiple handlers with multiple modifier flags, you can |
227 | check the modifiers in JavaScript code: |
228 | |
229 | \qml |
230 | Item { |
231 | TapHandler { |
232 | onTapped: |
233 | switch (point.modifiers) { |
234 | case Qt.ControlModifier | Qt.AltModifier: |
235 | console.log("CTRL+ALT"); |
236 | break; |
237 | case Qt.ControlModifier | Qt.AltModifier | Qt.MetaModifier: |
238 | console.log("CTRL+META+ALT"); |
239 | break; |
240 | default: |
241 | console.log("other modifiers", point.modifiers); |
242 | break; |
243 | } |
244 | } |
245 | } |
246 | \endqml |
247 | |
248 | \sa Qt::KeyboardModifier |
249 | */ |
250 | void QQuickPointerDeviceHandler::setAcceptedModifiers(Qt::KeyboardModifiers acceptedModifiers) |
251 | { |
252 | Q_D(QQuickPointerDeviceHandler); |
253 | if (d->acceptedModifiers == acceptedModifiers) |
254 | return; |
255 | |
256 | d->acceptedModifiers = acceptedModifiers; |
257 | emit acceptedModifiersChanged(); |
258 | } |
259 | |
260 | bool QQuickPointerDeviceHandler::wantsPointerEvent(QPointerEvent *event) |
261 | { |
262 | Q_D(QQuickPointerDeviceHandler); |
263 | if (!QQuickPointerHandler::wantsPointerEvent(event)) |
264 | return false; |
265 | qCDebug(lcPointerHandlerDispatch) << objectName() |
266 | << "checking device type" << d->acceptedDevices |
267 | << "pointer type" << d->acceptedPointerTypes |
268 | << "modifiers" << d->acceptedModifiers; |
269 | if (!d->acceptedDevices.testFlag(flag: event->device()->type())) |
270 | return false; |
271 | if (!d->acceptedPointerTypes.testFlag(flag: event->pointingDevice()->pointerType())) |
272 | return false; |
273 | if (d->acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != d->acceptedModifiers) |
274 | return false; |
275 | // Some handlers (HoverHandler, PinchHandler, PointHandler) set |
276 | // acceptedButtons to Qt::NoButton to indicate that button state is irrelevant. |
277 | if (event->isSinglePointEvent() && acceptedButtons() != Qt::NoButton && event->type() != QEvent::Wheel && |
278 | (static_cast<QSinglePointEvent *>(event)->buttons() & acceptedButtons()) == 0 && |
279 | (static_cast<QSinglePointEvent *>(event)->button() & acceptedButtons()) == 0) |
280 | return false; |
281 | return true; |
282 | } |
283 | |
284 | QT_END_NAMESPACE |
285 | |
286 | #include "moc_qquickpointerdevicehandler_p.cpp" |
287 | |