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