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