1 | // Copyright (C) 2020 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 "qquickwheelhandler_p.h" |
5 | #include "qquickwheelhandler_p_p.h" |
6 | #include <QtQuick/private/qquickitem_p.h> |
7 | #include <QLoggingCategory> |
8 | #include <QtMath> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | Q_LOGGING_CATEGORY(lcWheelHandler, "qt.quick.handler.wheel" ) |
13 | |
14 | /*! |
15 | \qmltype WheelHandler |
16 | \instantiates QQuickWheelHandler |
17 | \inherits SinglePointHandler |
18 | \inqmlmodule QtQuick |
19 | \ingroup qtquick-input-handlers |
20 | \brief Handler for the mouse wheel. |
21 | |
22 | WheelHandler is a handler that is used to interactively manipulate some |
23 | numeric property of an Item as the user rotates the mouse wheel. Like other |
24 | Input Handlers, by default it manipulates its \l {PointerHandler::target} |
25 | {target}. Declare \l property to control which target property will be |
26 | manipulated: |
27 | |
28 | \snippet pointerHandlers/wheelHandler.qml 0 |
29 | |
30 | \l BoundaryRule is quite useful in combination with WheelHandler (as well |
31 | as with other Input Handlers) to declare the allowed range of values that |
32 | the target property can have. For example it is possible to implement |
33 | scrolling using a combination of WheelHandler and \l DragHandler to |
34 | manipulate the scrollable Item's \l{QQuickItem::y}{y} property when the |
35 | user rotates the wheel or drags the item on a touchscreen, and |
36 | \l BoundaryRule to limit the range of motion from the top to the bottom: |
37 | |
38 | \snippet pointerHandlers/handlerFlick.qml 0 |
39 | |
40 | Alternatively, if \l property is not set or \l target is null, |
41 | WheelHandler will not automatically manipulate anything; but the |
42 | \l rotation property can be used in a binding to manipulate another |
43 | property, or you can implement \c onWheel and handle the wheel event |
44 | directly. |
45 | |
46 | WheelHandler handles only a rotating mouse wheel by default; this |
47 | can be changed by setting acceptedDevices. |
48 | |
49 | \sa MouseArea, Flickable, {Qt Quick Examples - Pointer Handlers} |
50 | */ |
51 | |
52 | QQuickWheelHandler::QQuickWheelHandler(QQuickItem *parent) |
53 | : QQuickSinglePointHandler(*(new QQuickWheelHandlerPrivate), parent) |
54 | { |
55 | setAcceptedDevices(QInputDevice::DeviceType::Mouse); |
56 | } |
57 | |
58 | /*! |
59 | \qmlproperty enumeration QtQuick::WheelHandler::orientation |
60 | |
61 | Which wheel to react to. The default is \c Qt.Vertical. |
62 | |
63 | Not every mouse has a \c Horizontal wheel; sometimes it is emulated by |
64 | tilting the wheel sideways. A touchpad can usually generate both vertical |
65 | and horizontal wheel events. |
66 | */ |
67 | Qt::Orientation QQuickWheelHandler::orientation() const |
68 | { |
69 | Q_D(const QQuickWheelHandler); |
70 | return d->orientation; |
71 | } |
72 | |
73 | void QQuickWheelHandler::setOrientation(Qt::Orientation orientation) |
74 | { |
75 | Q_D(QQuickWheelHandler); |
76 | if (d->orientation == orientation) |
77 | return; |
78 | |
79 | d->orientation = orientation; |
80 | emit orientationChanged(); |
81 | } |
82 | |
83 | /*! |
84 | \qmlproperty bool QtQuick::WheelHandler::invertible |
85 | |
86 | Whether or not to reverse the direction of property change if |
87 | \l QWheelEvent::inverted is \c true. The default is \c true. |
88 | |
89 | If the operating system has a "natural scrolling" setting that causes |
90 | scrolling to be in the same direction as the finger movement, then if this |
91 | property is set to \c true, and WheelHandler is directly setting a property |
92 | on \l target, the direction of movement will correspond to the system setting. |
93 | If this property is set to \c false, it will invert the \l rotation so that |
94 | the direction of motion is always the same as the direction of finger movement. |
95 | */ |
96 | bool QQuickWheelHandler::isInvertible() const |
97 | { |
98 | Q_D(const QQuickWheelHandler); |
99 | return d->invertible; |
100 | } |
101 | |
102 | void QQuickWheelHandler::setInvertible(bool invertible) |
103 | { |
104 | Q_D(QQuickWheelHandler); |
105 | if (d->invertible == invertible) |
106 | return; |
107 | |
108 | d->invertible = invertible; |
109 | emit invertibleChanged(); |
110 | } |
111 | |
112 | /*! |
113 | \qmlproperty real QtQuick::WheelHandler::activeTimeout |
114 | |
115 | The amount of time in seconds after which the \l active property will |
116 | revert to \c false if no more wheel events are received. The default is |
117 | \c 0.1 (100 ms). |
118 | |
119 | When WheelHandler handles events that contain |
120 | \l {Qt::ScrollPhase}{scroll phase} information, such as events from some |
121 | touchpads, the \l active property will become \c false as soon as an event |
122 | with phase \l Qt::ScrollEnd is received; in that case the timeout is not |
123 | necessary. But a conventional mouse with a wheel does not provide a scroll |
124 | phase: the mouse cannot detect when the user has decided to stop |
125 | scrolling, so the \l active property transitions to \c false after this |
126 | much time has elapsed. |
127 | |
128 | \sa QWheelEvent::phase() |
129 | */ |
130 | qreal QQuickWheelHandler::activeTimeout() const |
131 | { |
132 | Q_D(const QQuickWheelHandler); |
133 | return d->activeTimeout; |
134 | } |
135 | |
136 | void QQuickWheelHandler::setActiveTimeout(qreal timeout) |
137 | { |
138 | Q_D(QQuickWheelHandler); |
139 | if (qFuzzyCompare(p1: d->activeTimeout, p2: timeout)) |
140 | return; |
141 | |
142 | if (timeout < 0) { |
143 | qWarning(msg: "activeTimeout must be positive" ); |
144 | return; |
145 | } |
146 | |
147 | d->activeTimeout = timeout; |
148 | emit activeTimeoutChanged(); |
149 | } |
150 | |
151 | /*! |
152 | \qmlproperty real QtQuick::WheelHandler::rotation |
153 | |
154 | The angle through which the mouse wheel has been rotated since the last |
155 | time this property was set, in wheel degrees. |
156 | |
157 | A positive value indicates that the wheel was rotated up/right; |
158 | a negative value indicates that the wheel was rotated down/left. |
159 | |
160 | A basic mouse click-wheel works in steps of 15 degrees. |
161 | |
162 | The default is \c 0 at startup. It can be programmatically set to any value |
163 | at any time. The value will be adjusted from there as the user rotates the |
164 | mouse wheel. |
165 | |
166 | \sa orientation |
167 | */ |
168 | qreal QQuickWheelHandler::rotation() const |
169 | { |
170 | Q_D(const QQuickWheelHandler); |
171 | return d->rotation * d->rotationScale; |
172 | } |
173 | |
174 | void QQuickWheelHandler::setRotation(qreal rotation) |
175 | { |
176 | Q_D(QQuickWheelHandler); |
177 | if (qFuzzyCompare(p1: d->rotation, p2: rotation / d->rotationScale)) |
178 | return; |
179 | |
180 | d->rotation = rotation / d->rotationScale; |
181 | emit rotationChanged(); |
182 | } |
183 | |
184 | /*! |
185 | \qmlproperty real QtQuick::WheelHandler::rotationScale |
186 | |
187 | The scaling to be applied to the \l rotation property, and to the |
188 | \l property on the \l target item, if any. The default is 1, such that |
189 | \l rotation will be in units of degrees of rotation. It can be set to a |
190 | negative number to invert the effect of the direction of mouse wheel |
191 | rotation. |
192 | */ |
193 | qreal QQuickWheelHandler::rotationScale() const |
194 | { |
195 | Q_D(const QQuickWheelHandler); |
196 | return d->rotationScale; |
197 | } |
198 | |
199 | void QQuickWheelHandler::setRotationScale(qreal rotationScale) |
200 | { |
201 | Q_D(QQuickWheelHandler); |
202 | if (qFuzzyCompare(p1: d->rotationScale, p2: rotationScale)) |
203 | return; |
204 | if (qFuzzyIsNull(d: rotationScale)) { |
205 | qWarning(msg: "rotationScale cannot be set to zero" ); |
206 | return; |
207 | } |
208 | |
209 | d->rotationScale = rotationScale; |
210 | emit rotationScaleChanged(); |
211 | } |
212 | |
213 | /*! |
214 | \qmlproperty string QtQuick::WheelHandler::property |
215 | |
216 | The property to be modified on the \l target when the mouse wheel is rotated. |
217 | |
218 | The default is no property (empty string). When no target property is being |
219 | automatically modified, you can use bindings to react to mouse wheel |
220 | rotation in arbitrary ways. |
221 | |
222 | You can use the mouse wheel to adjust any numeric property. For example if |
223 | \c property is set to \c x, the \l target will move horizontally as the |
224 | wheel is rotated. The following properties have special behavior: |
225 | |
226 | \value scale |
227 | \l{QQuickItem::scale}{scale} will be modified in a non-linear fashion |
228 | as described under \l targetScaleMultiplier. If |
229 | \l targetTransformAroundCursor is \c true, the \l{QQuickItem::x}{x} and |
230 | \l{QQuickItem::y}{y} properties will be simultaneously adjusted so that |
231 | the user will effectively zoom into or out of the point under the mouse |
232 | cursor. |
233 | \value rotation |
234 | \l{QQuickItem::rotation}{rotation} will be set to \l rotation. If |
235 | \l targetTransformAroundCursor is \c true, the l{QQuickItem::x}{x} and |
236 | \l{QQuickItem::y}{y} properties will be simultaneously adjusted so |
237 | that the user will effectively rotate the item around the point under |
238 | the mouse cursor. |
239 | |
240 | The adjustment of the given target property is always scaled by \l rotationScale. |
241 | */ |
242 | QString QQuickWheelHandler::property() const |
243 | { |
244 | Q_D(const QQuickWheelHandler); |
245 | return d->propertyName; |
246 | } |
247 | |
248 | void QQuickWheelHandler::setProperty(const QString &propertyName) |
249 | { |
250 | Q_D(QQuickWheelHandler); |
251 | if (d->propertyName == propertyName) |
252 | return; |
253 | |
254 | d->propertyName = propertyName; |
255 | d->metaPropertyDirty = true; |
256 | emit propertyChanged(); |
257 | } |
258 | |
259 | /*! |
260 | \qmlproperty real QtQuick::WheelHandler::targetScaleMultiplier |
261 | |
262 | The amount by which the \l target \l{QQuickItem::scale}{scale} is to be |
263 | multiplied whenever the \l rotation changes by 15 degrees. This |
264 | is relevant only when \l property is \c "scale". |
265 | |
266 | The \c scale will be multiplied by |
267 | \c targetScaleMultiplier \sup {angleDelta * rotationScale / 15}. |
268 | The default is \c 2 \sup {1/3}, which means that if \l rotationScale is left |
269 | at its default value, and the mouse wheel is rotated by one "click" |
270 | (15 degrees), the \l target will be scaled by approximately 1.25; after |
271 | three "clicks" its size will be doubled or halved, depending on the |
272 | direction that the wheel is rotated. If you want to make it double or halve |
273 | with every 2 clicks of the wheel, set this to \c 2 \sup {1/2} (1.4142). |
274 | If you want to make it scale the opposite way as the wheel is rotated, |
275 | set \c rotationScale to a negative value. |
276 | */ |
277 | qreal QQuickWheelHandler::targetScaleMultiplier() const |
278 | { |
279 | Q_D(const QQuickWheelHandler); |
280 | return d->targetScaleMultiplier; |
281 | } |
282 | |
283 | void QQuickWheelHandler::setTargetScaleMultiplier(qreal targetScaleMultiplier) |
284 | { |
285 | Q_D(QQuickWheelHandler); |
286 | if (qFuzzyCompare(p1: d->targetScaleMultiplier, p2: targetScaleMultiplier)) |
287 | return; |
288 | |
289 | d->targetScaleMultiplier = targetScaleMultiplier; |
290 | emit targetScaleMultiplierChanged(); |
291 | } |
292 | |
293 | /*! |
294 | \qmlproperty bool QtQuick::WheelHandler::targetTransformAroundCursor |
295 | |
296 | Whether the \l target should automatically be repositioned in such a way |
297 | that it is transformed around the mouse cursor position while the |
298 | \l property is adjusted. The default is \c true. |
299 | |
300 | If \l property is set to \c "rotation" and \l targetTransformAroundCursor |
301 | is \c true, then as the wheel is rotated, the \l target item will rotate in |
302 | place around the mouse cursor position. If \c targetTransformAroundCursor |
303 | is \c false, it will rotate around its |
304 | \l{QQuickItem::transformOrigin}{transformOrigin} instead. |
305 | */ |
306 | bool QQuickWheelHandler::isTargetTransformAroundCursor() const |
307 | { |
308 | Q_D(const QQuickWheelHandler); |
309 | return d->targetTransformAroundCursor; |
310 | } |
311 | |
312 | void QQuickWheelHandler::setTargetTransformAroundCursor(bool ttac) |
313 | { |
314 | Q_D(QQuickWheelHandler); |
315 | if (d->targetTransformAroundCursor == ttac) |
316 | return; |
317 | |
318 | d->targetTransformAroundCursor = ttac; |
319 | emit targetTransformAroundCursorChanged(); |
320 | } |
321 | |
322 | /*! |
323 | \qmlproperty bool QtQuick::WheelHandler::blocking |
324 | \since 6.3 |
325 | |
326 | Whether this handler prevents other items or handlers behind it from |
327 | handling the same wheel event. This property is \c true by default. |
328 | */ |
329 | bool QQuickWheelHandler::isBlocking() const |
330 | { |
331 | Q_D(const QQuickWheelHandler); |
332 | return d->blocking; |
333 | } |
334 | |
335 | void QQuickWheelHandler::setBlocking(bool blocking) |
336 | { |
337 | Q_D(QQuickWheelHandler); |
338 | if (d->blocking == blocking) |
339 | return; |
340 | |
341 | d->blocking = blocking; |
342 | emit blockingChanged(); |
343 | } |
344 | |
345 | bool QQuickWheelHandler::wantsPointerEvent(QPointerEvent *event) |
346 | { |
347 | if (!event) |
348 | return false; |
349 | if (event->type() != QEvent::Wheel) |
350 | return false; |
351 | QWheelEvent *we = static_cast<QWheelEvent *>(event); |
352 | if (!acceptedDevices().testFlag(flag: QPointingDevice::DeviceType::TouchPad) |
353 | && we->source() != Qt::MouseEventNotSynthesized) |
354 | return false; |
355 | if (!active()) { |
356 | switch (orientation()) { |
357 | case Qt::Horizontal: |
358 | if (!(we->angleDelta().x()) && !(we->pixelDelta().x())) |
359 | return false; |
360 | break; |
361 | case Qt::Vertical: |
362 | if (!(we->angleDelta().y()) && !(we->pixelDelta().y())) |
363 | return false; |
364 | break; |
365 | } |
366 | } |
367 | auto &point = event->point(i: 0); |
368 | if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(event, point) && parentContains(point)) { |
369 | setPointId(point.id()); |
370 | return true; |
371 | } |
372 | return false; |
373 | } |
374 | |
375 | void QQuickWheelHandler::handleEventPoint(QPointerEvent *ev, QEventPoint &point) |
376 | { |
377 | Q_D(QQuickWheelHandler); |
378 | QQuickSinglePointHandler::handleEventPoint(event: ev, point); |
379 | |
380 | if (ev->type() != QEvent::Wheel) |
381 | return; |
382 | const QWheelEvent *event = static_cast<const QWheelEvent *>(ev); |
383 | setActive(true); // ScrollEnd will not happen unless it was already active (see setActive(false) below) |
384 | if (d->blocking) |
385 | point.setAccepted(); |
386 | qreal inversion = !d->invertible && event->isInverted() ? -1 : 1; |
387 | qreal angleDelta = inversion * qreal(orientation() == Qt::Horizontal ? event->angleDelta().x() : |
388 | event->angleDelta().y()) / 8; |
389 | d->rotation += angleDelta; |
390 | emit rotationChanged(); |
391 | |
392 | d->wheelEvent.reset(event); |
393 | emit wheel(event: &d->wheelEvent); |
394 | if (!d->propertyName.isEmpty() && target()) { |
395 | QQuickItem *t = target(); |
396 | // writing target()'s property is done via QMetaProperty::write() so that any registered interceptors can react. |
397 | if (d->propertyName == QLatin1String("scale" )) { |
398 | qreal multiplier = qPow(x: d->targetScaleMultiplier, y: angleDelta * d->rotationScale / 15); // wheel "clicks" |
399 | const QPointF centroidParentPos = t->parentItem()->mapFromScene(point: point.scenePosition()); |
400 | const QPointF positionWas = t->position(); |
401 | const qreal scaleWas = t->scale(); |
402 | const qreal activePropertyValue = scaleWas * multiplier; |
403 | qCDebug(lcWheelHandler) << objectName() << "angle delta" << event->angleDelta() << "pixel delta" << event->pixelDelta() |
404 | << "@" << point.position() << "in parent" << centroidParentPos |
405 | << "in scene" << point.scenePosition() |
406 | << "multiplier" << multiplier << "scale" << scaleWas |
407 | << "->" << activePropertyValue; |
408 | d->targetMetaProperty().write(obj: t, value: activePropertyValue); |
409 | if (d->targetTransformAroundCursor) { |
410 | // If an interceptor intervened, scale may now be different than we asked for. Adjust accordingly. |
411 | multiplier = t->scale() / scaleWas; |
412 | const QPointF adjPos = QQuickItemPrivate::get(item: t)->adjustedPosForTransform( |
413 | centroid: centroidParentPos, startPos: positionWas, activeTranslatation: QVector2D(), startScale: scaleWas, activeScale: multiplier, startRotation: t->rotation(), activeRotation: 0); |
414 | qCDebug(lcWheelHandler) << "adjusting item pos" << adjPos << "in scene" << t->parentItem()->mapToScene(point: adjPos); |
415 | t->setPosition(adjPos); |
416 | } |
417 | } else if (d->propertyName == QLatin1String("rotation" )) { |
418 | const QPointF positionWas = t->position(); |
419 | const qreal rotationWas = t->rotation(); |
420 | const qreal activePropertyValue = rotationWas + angleDelta * d->rotationScale; |
421 | const QPointF centroidParentPos = t->parentItem()->mapFromScene(point: point.scenePosition()); |
422 | qCDebug(lcWheelHandler) << objectName() << "angle delta" << event->angleDelta() << "pixel delta" << event->pixelDelta() |
423 | << "@" << point.position() << "in parent" << centroidParentPos |
424 | << "in scene" << point.scenePosition() << "rotation" << t->rotation() |
425 | << "->" << activePropertyValue; |
426 | d->targetMetaProperty().write(obj: t, value: activePropertyValue); |
427 | if (d->targetTransformAroundCursor) { |
428 | // If an interceptor intervened, rotation may now be different than we asked for. Adjust accordingly. |
429 | const QPointF adjPos = QQuickItemPrivate::get(item: t)->adjustedPosForTransform( |
430 | centroid: centroidParentPos, startPos: positionWas, activeTranslatation: QVector2D(), |
431 | startScale: t->scale(), activeScale: 1, startRotation: rotationWas, activeRotation: t->rotation() - rotationWas); |
432 | qCDebug(lcWheelHandler) << "adjusting item pos" << adjPos << "in scene" << t->parentItem()->mapToScene(point: adjPos); |
433 | t->setPosition(adjPos); |
434 | } |
435 | } else { |
436 | qCDebug(lcWheelHandler) << objectName() << "angle delta" << event->angleDelta() << "scaled" << angleDelta |
437 | << "total" << d->rotation << "pixel delta" << event->pixelDelta() |
438 | << "@" << point.position() << "in scene" << point.scenePosition() << "rotation" << t->rotation(); |
439 | qreal delta = 0; |
440 | if (event->hasPixelDelta()) { |
441 | delta = inversion * d->rotationScale * qreal(orientation() == Qt::Horizontal ? event->pixelDelta().x() : event->pixelDelta().y()); |
442 | qCDebug(lcWheelHandler) << "changing target" << d->propertyName << "by pixel delta" << delta << "from" << event; |
443 | } else { |
444 | delta = angleDelta * d->rotationScale; |
445 | qCDebug(lcWheelHandler) << "changing target" << d->propertyName << "by scaled angle delta" << delta << "from" << event; |
446 | } |
447 | bool ok = false; |
448 | qreal value = d->targetMetaProperty().read(obj: t).toReal(ok: &ok); |
449 | if (ok) |
450 | d->targetMetaProperty().write(obj: t, value: value + qreal(delta)); |
451 | else |
452 | qWarning() << "failed to read property" << d->propertyName << "of" << t; |
453 | } |
454 | } |
455 | switch (event->phase()) { |
456 | case Qt::ScrollEnd: |
457 | qCDebug(lcWheelHandler) << objectName() << "deactivating due to ScrollEnd phase" ; |
458 | setActive(false); |
459 | break; |
460 | case Qt::NoScrollPhase: |
461 | d->deactivationTimer.start(msec: qRound(d: d->activeTimeout * 1000), obj: this); |
462 | break; |
463 | case Qt::ScrollBegin: |
464 | case Qt::ScrollUpdate: |
465 | case Qt::ScrollMomentum: |
466 | break; |
467 | } |
468 | } |
469 | |
470 | void QQuickWheelHandler::onTargetChanged(QQuickItem *oldTarget) |
471 | { |
472 | Q_UNUSED(oldTarget); |
473 | Q_D(QQuickWheelHandler); |
474 | d->metaPropertyDirty = true; |
475 | } |
476 | |
477 | void QQuickWheelHandler::onActiveChanged() |
478 | { |
479 | Q_D(QQuickWheelHandler); |
480 | if (!active()) |
481 | d->deactivationTimer.stop(); |
482 | } |
483 | |
484 | void QQuickWheelHandler::timerEvent(QTimerEvent *event) |
485 | { |
486 | Q_D(const QQuickWheelHandler); |
487 | if (event->timerId() == d->deactivationTimer.timerId()) { |
488 | qCDebug(lcWheelHandler) << objectName() << "deactivating due to timeout" ; |
489 | setActive(false); |
490 | } |
491 | } |
492 | |
493 | /*! |
494 | \qmlsignal QtQuick::WheelHandler::wheel(WheelEvent event) |
495 | |
496 | This signal is emitted every time this handler receives an \a event |
497 | of type \l QWheelEvent: that is, every time the wheel is moved or the |
498 | scrolling gesture is updated. |
499 | */ |
500 | |
501 | QQuickWheelHandlerPrivate::QQuickWheelHandlerPrivate() |
502 | : QQuickSinglePointHandlerPrivate() |
503 | { |
504 | } |
505 | |
506 | QMetaProperty &QQuickWheelHandlerPrivate::targetMetaProperty() const |
507 | { |
508 | Q_Q(const QQuickWheelHandler); |
509 | if (metaPropertyDirty && q->target()) { |
510 | if (!propertyName.isEmpty()) { |
511 | const QMetaObject *targetMeta = q->target()->metaObject(); |
512 | metaProperty = targetMeta->property( |
513 | index: targetMeta->indexOfProperty(name: propertyName.toLocal8Bit().constData())); |
514 | } |
515 | metaPropertyDirty = false; |
516 | } |
517 | return metaProperty; |
518 | } |
519 | |
520 | /*! |
521 | \qmlproperty flags WheelHandler::acceptedDevices |
522 | |
523 | The types of pointing devices that can activate this handler. |
524 | |
525 | By default, this property is set to |
526 | \l{QInputDevice::DeviceType}{PointerDevice.Mouse}, so as to react only to |
527 | events from an actual mouse wheel. |
528 | |
529 | WheelHandler can be made to respond to both mouse wheel and touchpad |
530 | scrolling by setting acceptedDevices to |
531 | \c{PointerDevice.Mouse | PointerDevice.TouchPad}. |
532 | |
533 | \note Some non-mouse hardware (such as a touch-sensitive Wacom tablet, or a |
534 | Linux laptop touchpad) generates real wheel events from gestures. |
535 | WheelHandler will respond to those events as wheel events even if |
536 | \c acceptedDevices remains set to its default value. |
537 | */ |
538 | |
539 | QT_END_NAMESPACE |
540 | |
541 | #include "moc_qquickwheelhandler_p.cpp" |
542 | |