File indexing completed on 2024-04-21 11:33:54
0001 /* 0002 * SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "wheelhandler.h" 0008 #include "settings.h" 0009 #include <QQmlInfo> 0010 #include <QQuickItem> 0011 #include <QQuickWindow> 0012 #include <QWheelEvent> 0013 0014 #include <cmath> 0015 0016 KirigamiWheelEvent::KirigamiWheelEvent(QObject *parent) 0017 : QObject(parent) 0018 { 0019 } 0020 0021 KirigamiWheelEvent::~KirigamiWheelEvent() 0022 { 0023 } 0024 0025 void KirigamiWheelEvent::initializeFromEvent(QWheelEvent *event) 0026 { 0027 m_x = event->position().x(); 0028 m_y = event->position().y(); 0029 m_angleDelta = event->angleDelta(); 0030 m_pixelDelta = event->pixelDelta(); 0031 m_buttons = event->buttons(); 0032 m_modifiers = event->modifiers(); 0033 m_accepted = false; 0034 m_inverted = event->inverted(); 0035 } 0036 0037 qreal KirigamiWheelEvent::x() const 0038 { 0039 return m_x; 0040 } 0041 0042 qreal KirigamiWheelEvent::y() const 0043 { 0044 return m_y; 0045 } 0046 0047 QPointF KirigamiWheelEvent::angleDelta() const 0048 { 0049 return m_angleDelta; 0050 } 0051 0052 QPointF KirigamiWheelEvent::pixelDelta() const 0053 { 0054 return m_pixelDelta; 0055 } 0056 0057 int KirigamiWheelEvent::buttons() const 0058 { 0059 return m_buttons; 0060 } 0061 0062 int KirigamiWheelEvent::modifiers() const 0063 { 0064 return m_modifiers; 0065 } 0066 0067 bool KirigamiWheelEvent::inverted() const 0068 { 0069 return m_inverted; 0070 } 0071 0072 bool KirigamiWheelEvent::isAccepted() 0073 { 0074 return m_accepted; 0075 } 0076 0077 void KirigamiWheelEvent::setAccepted(bool accepted) 0078 { 0079 m_accepted = accepted; 0080 } 0081 0082 /////////////////////////////// 0083 0084 WheelFilterItem::WheelFilterItem(QQuickItem *parent) 0085 : QQuickItem(parent) 0086 { 0087 setEnabled(false); 0088 } 0089 0090 /////////////////////////////// 0091 0092 WheelHandler::WheelHandler(QObject *parent) 0093 : QObject(parent) 0094 , m_filterItem(new WheelFilterItem(nullptr)) 0095 { 0096 m_filterItem->installEventFilter(this); 0097 0098 m_wheelScrollingTimer.setSingleShot(true); 0099 m_wheelScrollingTimer.setInterval(m_wheelScrollingDuration); 0100 m_wheelScrollingTimer.callOnTimeout([this]() { 0101 setScrolling(false); 0102 }); 0103 0104 connect(QGuiApplication::styleHints(), &QStyleHints::wheelScrollLinesChanged, this, [this](int scrollLines) { 0105 m_defaultPixelStepSize = 20 * scrollLines; 0106 if (!m_explicitVStepSize && m_verticalStepSize != m_defaultPixelStepSize) { 0107 m_verticalStepSize = m_defaultPixelStepSize; 0108 Q_EMIT verticalStepSizeChanged(); 0109 } 0110 if (!m_explicitHStepSize && m_horizontalStepSize != m_defaultPixelStepSize) { 0111 m_horizontalStepSize = m_defaultPixelStepSize; 0112 Q_EMIT horizontalStepSizeChanged(); 0113 } 0114 }); 0115 } 0116 0117 WheelHandler::~WheelHandler() 0118 { 0119 delete m_filterItem; 0120 } 0121 0122 QQuickItem *WheelHandler::target() const 0123 { 0124 return m_flickable; 0125 } 0126 0127 void WheelHandler::setTarget(QQuickItem *target) 0128 { 0129 if (m_flickable == target) { 0130 return; 0131 } 0132 0133 if (target && !target->inherits("QQuickFlickable")) { 0134 qmlWarning(this) << "target must be a QQuickFlickable"; 0135 return; 0136 } 0137 0138 if (m_flickable) { 0139 m_flickable->removeEventFilter(this); 0140 disconnect(m_flickable, nullptr, m_filterItem, nullptr); 0141 disconnect(m_flickable, &QQuickItem::parentChanged, this, &WheelHandler::_k_rebindScrollBars); 0142 } 0143 0144 m_flickable = target; 0145 m_filterItem->setParentItem(target); 0146 0147 if (target) { 0148 target->installEventFilter(this); 0149 0150 // Stack WheelFilterItem over the Flickable's scrollable content 0151 m_filterItem->stackAfter(target->property("contentItem").value<QQuickItem*>()); 0152 // Make it fill the Flickable 0153 m_filterItem->setWidth(target->width()); 0154 m_filterItem->setHeight(target->height()); 0155 connect(target, &QQuickItem::widthChanged, m_filterItem, [this, target]() { 0156 m_filterItem->setWidth(target->width()); 0157 }); 0158 connect(target, &QQuickItem::heightChanged, m_filterItem, [this, target]() { 0159 m_filterItem->setHeight(target->height()); 0160 }); 0161 } 0162 0163 _k_rebindScrollBars(); 0164 0165 Q_EMIT targetChanged(); 0166 } 0167 0168 void WheelHandler::_k_rebindScrollBars() 0169 { 0170 struct ScrollBarAttached { 0171 QObject *attached = nullptr; 0172 QQuickItem *vertical = nullptr; 0173 QQuickItem *horizontal = nullptr; 0174 }; 0175 0176 ScrollBarAttached attachedToFlickable; 0177 ScrollBarAttached attachedToScrollView; 0178 0179 if (m_flickable) { 0180 // Get ScrollBars so that we can filter them too, even if they're not 0181 // in the bounds of the Flickable 0182 const auto flickableChildren = m_flickable->children(); 0183 for (const auto child : flickableChildren) { 0184 if (child->inherits("QQuickScrollBarAttached")) { 0185 attachedToFlickable.attached = child; 0186 attachedToFlickable.vertical = child->property("vertical").value<QQuickItem *>(); 0187 attachedToFlickable.horizontal = child->property("horizontal").value<QQuickItem *>(); 0188 break; 0189 } 0190 } 0191 0192 // Check ScrollView if there are no scrollbars attached to the Flickable. 0193 // We need to check if the parent inherits QQuickScrollView in case the 0194 // parent is another Flickable that already has a Kirigami WheelHandler. 0195 auto flickableParent = m_flickable->parentItem(); 0196 if (flickableParent && flickableParent->inherits("QQuickScrollView")) { 0197 const auto siblings = flickableParent->children(); 0198 for (const auto child : siblings) { 0199 if (child->inherits("QQuickScrollBarAttached")) { 0200 attachedToScrollView.attached = child; 0201 attachedToScrollView.vertical = child->property("vertical").value<QQuickItem *>(); 0202 attachedToScrollView.horizontal = child->property("horizontal").value<QQuickItem *>(); 0203 break; 0204 } 0205 } 0206 } 0207 } 0208 0209 // Dilemma: ScrollBars can be attached to both ScrollView and Flickable, 0210 // but only one of them should be shown anyway. Let's prefer Flickable. 0211 0212 struct ChosenScrollBar { 0213 QObject *attached = nullptr; 0214 QQuickItem *scrollBar = nullptr; 0215 }; 0216 0217 ChosenScrollBar vertical; 0218 if (attachedToFlickable.vertical) { 0219 vertical.attached = attachedToFlickable.attached; 0220 vertical.scrollBar = attachedToFlickable.vertical; 0221 } else if (attachedToScrollView.vertical) { 0222 vertical.attached = attachedToScrollView.attached; 0223 vertical.scrollBar = attachedToScrollView.vertical; 0224 } 0225 0226 ChosenScrollBar horizontal; 0227 if (attachedToFlickable.horizontal) { 0228 horizontal.attached = attachedToFlickable.attached; 0229 horizontal.scrollBar = attachedToFlickable.horizontal; 0230 } else if (attachedToScrollView.horizontal) { 0231 horizontal.attached = attachedToScrollView.attached; 0232 horizontal.scrollBar = attachedToScrollView.horizontal; 0233 } 0234 0235 // Flickable may get re-parented to or out of a ScrollView, so we need to 0236 // redo the discovery process. This is especially important for 0237 // Kirigami.ScrollablePage component. 0238 if (m_flickable) { 0239 if (attachedToFlickable.horizontal && attachedToFlickable.vertical) { 0240 // But if both scrollbars are already those from the preferred 0241 // Flickable, there's no need for rediscovery. 0242 disconnect(m_flickable, &QQuickItem::parentChanged, this, &WheelHandler::_k_rebindScrollBars); 0243 } else { 0244 connect(m_flickable, &QQuickItem::parentChanged, this, &WheelHandler::_k_rebindScrollBars, Qt::UniqueConnection); 0245 } 0246 } 0247 0248 if (m_verticalScrollBar != vertical.scrollBar) { 0249 if (m_verticalScrollBar) { 0250 m_verticalScrollBar->removeEventFilter(this); 0251 disconnect(m_verticalChangedConnection); 0252 } 0253 m_verticalScrollBar = vertical.scrollBar; 0254 if (vertical.scrollBar) { 0255 vertical.scrollBar->installEventFilter(this); 0256 m_verticalChangedConnection = connect(vertical.attached, SIGNAL(verticalChanged()), this, SLOT(_k_rebindScrollBars())); 0257 } 0258 } 0259 0260 if (m_horizontalScrollBar != horizontal.scrollBar) { 0261 if (m_horizontalScrollBar) { 0262 m_horizontalScrollBar->removeEventFilter(this); 0263 disconnect(m_horizontalChangedConnection); 0264 } 0265 m_horizontalScrollBar = horizontal.scrollBar; 0266 if (horizontal.scrollBar) { 0267 horizontal.scrollBar->installEventFilter(this); 0268 m_horizontalChangedConnection = connect(horizontal.attached, SIGNAL(horizontalChanged()), this, SLOT(_k_rebindScrollBars())); 0269 } 0270 } 0271 } 0272 0273 qreal WheelHandler::verticalStepSize() const 0274 { 0275 return m_verticalStepSize; 0276 } 0277 0278 void WheelHandler::setVerticalStepSize(qreal stepSize) 0279 { 0280 m_explicitVStepSize = true; 0281 if (qFuzzyCompare(m_verticalStepSize, stepSize)) { 0282 return; 0283 } 0284 // Mimic the behavior of QQuickScrollBar when stepSize is 0 0285 if (qFuzzyIsNull(stepSize)) { 0286 resetVerticalStepSize(); 0287 return; 0288 } 0289 m_verticalStepSize = stepSize; 0290 Q_EMIT verticalStepSizeChanged(); 0291 } 0292 0293 void WheelHandler::resetVerticalStepSize() 0294 { 0295 m_explicitVStepSize = false; 0296 if (qFuzzyCompare(m_verticalStepSize, m_defaultPixelStepSize)) { 0297 return; 0298 } 0299 m_verticalStepSize = m_defaultPixelStepSize; 0300 Q_EMIT verticalStepSizeChanged(); 0301 } 0302 0303 qreal WheelHandler::horizontalStepSize() const 0304 { 0305 return m_horizontalStepSize; 0306 } 0307 0308 void WheelHandler::setHorizontalStepSize(qreal stepSize) 0309 { 0310 m_explicitHStepSize = true; 0311 if (qFuzzyCompare(m_horizontalStepSize, stepSize)) { 0312 return; 0313 } 0314 // Mimic the behavior of QQuickScrollBar when stepSize is 0 0315 if (qFuzzyIsNull(stepSize)) { 0316 resetHorizontalStepSize(); 0317 return; 0318 } 0319 m_horizontalStepSize = stepSize; 0320 Q_EMIT horizontalStepSizeChanged(); 0321 } 0322 0323 void WheelHandler::resetHorizontalStepSize() 0324 { 0325 m_explicitHStepSize = false; 0326 if (qFuzzyCompare(m_horizontalStepSize, m_defaultPixelStepSize)) { 0327 return; 0328 } 0329 m_horizontalStepSize = m_defaultPixelStepSize; 0330 Q_EMIT horizontalStepSizeChanged(); 0331 } 0332 0333 Qt::KeyboardModifiers WheelHandler::pageScrollModifiers() const 0334 { 0335 return m_pageScrollModifiers; 0336 } 0337 0338 void WheelHandler::setPageScrollModifiers(Qt::KeyboardModifiers modifiers) 0339 { 0340 if (m_pageScrollModifiers == modifiers) { 0341 return; 0342 } 0343 m_pageScrollModifiers = modifiers; 0344 Q_EMIT pageScrollModifiersChanged(); 0345 } 0346 0347 void WheelHandler::resetPageScrollModifiers() 0348 { 0349 setPageScrollModifiers(m_defaultPageScrollModifiers); 0350 } 0351 0352 bool WheelHandler::filterMouseEvents() const 0353 { 0354 return m_filterMouseEvents; 0355 } 0356 0357 void WheelHandler::setFilterMouseEvents(bool enabled) 0358 { 0359 if (m_filterMouseEvents == enabled) { 0360 return; 0361 } 0362 m_filterMouseEvents = enabled; 0363 Q_EMIT filterMouseEventsChanged(); 0364 } 0365 0366 bool WheelHandler::keyNavigationEnabled() const 0367 { 0368 return m_keyNavigationEnabled; 0369 } 0370 0371 void WheelHandler::setKeyNavigationEnabled(bool enabled) 0372 { 0373 if (m_keyNavigationEnabled == enabled) { 0374 return; 0375 } 0376 m_keyNavigationEnabled = enabled; 0377 Q_EMIT keyNavigationEnabledChanged(); 0378 } 0379 0380 void WheelHandler::setScrolling(bool scrolling) 0381 { 0382 if (m_wheelScrolling == scrolling) { 0383 if (m_wheelScrolling) { 0384 m_wheelScrollingTimer.start(); 0385 } 0386 return; 0387 } 0388 m_wheelScrolling = scrolling; 0389 m_filterItem->setEnabled(m_wheelScrolling); 0390 } 0391 0392 bool WheelHandler::scrollFlickable(QPointF pixelDelta, QPointF angleDelta, Qt::KeyboardModifiers modifiers) 0393 { 0394 if (!m_flickable || (pixelDelta.isNull() && angleDelta.isNull())) { 0395 return false; 0396 } 0397 0398 const qreal width = m_flickable->width(); 0399 const qreal height = m_flickable->height(); 0400 const qreal contentWidth = m_flickable->property("contentWidth").toReal(); 0401 const qreal contentHeight = m_flickable->property("contentHeight").toReal(); 0402 const qreal contentX = m_flickable->property("contentX").toReal(); 0403 const qreal contentY = m_flickable->property("contentY").toReal(); 0404 const qreal topMargin = m_flickable->property("topMargin").toReal(); 0405 const qreal bottomMargin = m_flickable->property("bottomMargin").toReal(); 0406 const qreal leftMargin = m_flickable->property("leftMargin").toReal(); 0407 const qreal rightMargin = m_flickable->property("rightMargin").toReal(); 0408 const qreal originX = m_flickable->property("originX").toReal(); 0409 const qreal originY = m_flickable->property("originY").toReal(); 0410 const qreal pageWidth = width - leftMargin - rightMargin; 0411 const qreal pageHeight = height - topMargin - bottomMargin; 0412 const auto window = m_flickable->window(); 0413 const qreal devicePixelRatio = window != nullptr ? window->devicePixelRatio() : qGuiApp->devicePixelRatio(); 0414 0415 // HACK: Only transpose deltas when not using xcb in order to not conflict with xcb's own delta transposing 0416 if (modifiers & m_defaultHorizontalScrollModifiers && qGuiApp->platformName() != QLatin1String("xcb")) { 0417 angleDelta = angleDelta.transposed(); 0418 pixelDelta = pixelDelta.transposed(); 0419 } 0420 0421 const qreal xTicks = angleDelta.x() / 120; 0422 const qreal yTicks = angleDelta.y() / 120; 0423 qreal xChange; 0424 qreal yChange; 0425 bool scrolled = false; 0426 0427 // Scroll X 0428 if (contentWidth > pageWidth) { 0429 // Use page size with pageScrollModifiers. Matches QScrollBar, which uses QAbstractSlider behavior. 0430 if (modifiers & m_pageScrollModifiers) { 0431 xChange = qBound(-pageWidth, xTicks * pageWidth, pageWidth); 0432 } else if (pixelDelta.x() != 0) { 0433 xChange = pixelDelta.x(); 0434 } else { 0435 xChange = xTicks * m_horizontalStepSize; 0436 } 0437 0438 // contentX and contentY use reversed signs from what x and y would normally use, so flip the signs 0439 0440 qreal minXExtent = leftMargin - originX; 0441 qreal maxXExtent = width - (contentWidth + rightMargin + originX); 0442 0443 qreal newContentX = qBound(-minXExtent, contentX - xChange, -maxXExtent); 0444 // Flickable::pixelAligned rounds the position, so round to mimic that behavior. 0445 // Rounding prevents fractional positioning from causing text to be 0446 // clipped off on the top and bottom. 0447 // Multiply by devicePixelRatio before rounding and divide by devicePixelRatio 0448 // after to make position match pixels on the screen more closely. 0449 newContentX = std::round(newContentX * devicePixelRatio) / devicePixelRatio; 0450 if (contentX != newContentX) { 0451 scrolled = true; 0452 m_flickable->setProperty("contentX", newContentX); 0453 } 0454 } 0455 0456 // Scroll Y 0457 if (contentHeight > pageHeight) { 0458 if (modifiers & m_pageScrollModifiers) { 0459 yChange = qBound(-pageHeight, yTicks * pageHeight, pageHeight); 0460 } else if (pixelDelta.y() != 0) { 0461 yChange = pixelDelta.y(); 0462 } else { 0463 yChange = yTicks * m_verticalStepSize; 0464 } 0465 0466 // contentX and contentY use reversed signs from what x and y would normally use, so flip the signs 0467 0468 qreal minYExtent = topMargin - originY; 0469 qreal maxYExtent = height - (contentHeight + bottomMargin + originY); 0470 0471 qreal newContentY = qBound(-minYExtent, contentY - yChange, -maxYExtent); 0472 // Flickable::pixelAligned rounds the position, so round to mimic that behavior. 0473 // Rounding prevents fractional positioning from causing text to be 0474 // clipped off on the top and bottom. 0475 // Multiply by devicePixelRatio before rounding and divide by devicePixelRatio 0476 // after to make position match pixels on the screen more closely. 0477 newContentY = std::round(newContentY * devicePixelRatio) / devicePixelRatio; 0478 if (contentY != newContentY) { 0479 scrolled = true; 0480 m_flickable->setProperty("contentY", newContentY); 0481 } 0482 } 0483 0484 return scrolled; 0485 } 0486 0487 bool WheelHandler::scrollUp(qreal stepSize) 0488 { 0489 if (qFuzzyIsNull(stepSize)) { 0490 return false; 0491 } else if (stepSize < 0) { 0492 stepSize = m_verticalStepSize; 0493 } 0494 // contentY uses reversed sign 0495 return scrollFlickable(QPointF(0, stepSize)); 0496 } 0497 0498 bool WheelHandler::scrollDown(qreal stepSize) 0499 { 0500 if (qFuzzyIsNull(stepSize)) { 0501 return false; 0502 } else if (stepSize < 0) { 0503 stepSize = m_verticalStepSize; 0504 } 0505 // contentY uses reversed sign 0506 return scrollFlickable(QPointF(0, -stepSize)); 0507 } 0508 0509 bool WheelHandler::scrollLeft(qreal stepSize) 0510 { 0511 if (qFuzzyIsNull(stepSize)) { 0512 return false; 0513 } else if (stepSize < 0) { 0514 stepSize = m_horizontalStepSize; 0515 } 0516 // contentX uses reversed sign 0517 return scrollFlickable(QPoint(stepSize, 0)); 0518 } 0519 0520 bool WheelHandler::scrollRight(qreal stepSize) 0521 { 0522 if (qFuzzyIsNull(stepSize)) { 0523 return false; 0524 } else if (stepSize < 0) { 0525 stepSize = m_horizontalStepSize; 0526 } 0527 // contentX uses reversed sign 0528 return scrollFlickable(QPoint(-stepSize, 0)); 0529 } 0530 0531 bool WheelHandler::eventFilter(QObject *watched, QEvent *event) 0532 { 0533 auto item = qobject_cast<QQuickItem*>(watched); 0534 if (!item || !item->isEnabled()) { 0535 return false; 0536 } 0537 0538 qreal contentWidth = 0; 0539 qreal contentHeight = 0; 0540 qreal pageWidth = 0; 0541 qreal pageHeight = 0; 0542 if (m_flickable) { 0543 contentWidth = m_flickable->property("contentWidth").toReal(); 0544 contentHeight = m_flickable->property("contentHeight").toReal(); 0545 pageWidth = m_flickable->width() - m_flickable->property("leftMargin").toReal() - m_flickable->property("rightMargin").toReal(); 0546 pageHeight = m_flickable->height() - m_flickable->property("topMargin").toReal() - m_flickable->property("bottomMargin").toReal(); 0547 } 0548 0549 // The code handling touch, mouse and hover events is mostly copied/adapted from QQuickScrollView::childMouseEventFilter() 0550 switch (event->type()) { 0551 case QEvent::Wheel: { 0552 // QQuickScrollBar::interactive handling Matches behavior in QQuickScrollView::eventFilter() 0553 if (m_filterMouseEvents) { 0554 if (m_verticalScrollBar) { 0555 m_verticalScrollBar->setProperty("interactive", true); 0556 } 0557 if (m_horizontalScrollBar) { 0558 m_horizontalScrollBar->setProperty("interactive", true); 0559 } 0560 } 0561 QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event); 0562 0563 // NOTE: On X11 with libinput, pixelDelta is identical to angleDelta when using a mouse that shouldn't use pixelDelta. 0564 // If faulty pixelDelta, reset pixelDelta to (0,0). 0565 if (wheelEvent->pixelDelta() == wheelEvent->angleDelta()) { 0566 // In order to change any of the data, we have to create a whole new QWheelEvent from its constructor. 0567 QWheelEvent newWheelEvent( 0568 wheelEvent->position(), 0569 wheelEvent->globalPosition(), 0570 QPoint(0,0), // pixelDelta 0571 wheelEvent->angleDelta(), 0572 wheelEvent->buttons(), 0573 wheelEvent->modifiers(), 0574 wheelEvent->phase(), 0575 wheelEvent->inverted(), 0576 wheelEvent->source() 0577 ); 0578 m_kirigamiWheelEvent.initializeFromEvent(&newWheelEvent); 0579 } else { 0580 m_kirigamiWheelEvent.initializeFromEvent(wheelEvent); 0581 } 0582 0583 Q_EMIT wheel(&m_kirigamiWheelEvent); 0584 0585 if (m_kirigamiWheelEvent.isAccepted()) { 0586 return true; 0587 } 0588 0589 bool scrolled = false; 0590 if (m_scrollFlickableTarget || (contentHeight <= pageHeight && contentWidth <= pageWidth)) { 0591 // Don't use pixelDelta from the event unless angleDelta is not available 0592 // because scrolling by pixelDelta is too slow on Wayland with libinput. 0593 QPointF pixelDelta = m_kirigamiWheelEvent.angleDelta().isNull() ? m_kirigamiWheelEvent.pixelDelta() : QPoint(0, 0); 0594 scrolled = scrollFlickable(pixelDelta, 0595 m_kirigamiWheelEvent.angleDelta(), 0596 Qt::KeyboardModifiers(m_kirigamiWheelEvent.modifiers())); 0597 } 0598 setScrolling(scrolled); 0599 0600 // NOTE: Wheel events created by touchpad gestures with pixel deltas will cause scrolling to jump back 0601 // to where scrolling started unless the event is always accepted before it reaches the Flickable. 0602 bool flickableWillUseGestureScrolling = !(wheelEvent->source() == Qt::MouseEventNotSynthesized || wheelEvent->pixelDelta().isNull()); 0603 return scrolled || m_blockTargetWheel || flickableWillUseGestureScrolling; 0604 } 0605 0606 case QEvent::TouchBegin: { 0607 m_wasTouched = true; 0608 if (!m_filterMouseEvents) { 0609 break; 0610 } 0611 if (m_verticalScrollBar) { 0612 m_verticalScrollBar->setProperty("interactive", false); 0613 } 0614 if (m_horizontalScrollBar) { 0615 m_horizontalScrollBar->setProperty("interactive", false); 0616 } 0617 break; 0618 } 0619 0620 case QEvent::TouchEnd: { 0621 m_wasTouched = false; 0622 break; 0623 } 0624 0625 case QEvent::MouseButtonPress: { 0626 // NOTE: Flickable does not handle touch events, only synthesized mouse events 0627 m_wasTouched = static_cast<QMouseEvent *>(event)->source() != Qt::MouseEventNotSynthesized; 0628 if (!m_filterMouseEvents) { 0629 break; 0630 } 0631 if (!m_wasTouched) { 0632 if (m_verticalScrollBar) { 0633 m_verticalScrollBar->setProperty("interactive", true); 0634 } 0635 if (m_horizontalScrollBar) { 0636 m_horizontalScrollBar->setProperty("interactive", true); 0637 } 0638 break; 0639 } 0640 return !m_wasTouched && item == m_flickable; 0641 } 0642 0643 case QEvent::MouseMove: 0644 case QEvent::MouseButtonRelease: { 0645 setScrolling(false); 0646 if (!m_filterMouseEvents) { 0647 break; 0648 } 0649 if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized && item == m_flickable) { 0650 return true; 0651 } 0652 break; 0653 } 0654 0655 case QEvent::HoverEnter: 0656 case QEvent::HoverMove: { 0657 if (!m_filterMouseEvents) { 0658 break; 0659 } 0660 if (m_wasTouched && (item == m_verticalScrollBar || item == m_horizontalScrollBar)) { 0661 if (m_verticalScrollBar) { 0662 m_verticalScrollBar->setProperty("interactive", true); 0663 } 0664 if (m_horizontalScrollBar) { 0665 m_horizontalScrollBar->setProperty("interactive", true); 0666 } 0667 } 0668 break; 0669 } 0670 0671 case QEvent::KeyPress: { 0672 if (!m_keyNavigationEnabled) { 0673 break; 0674 } 0675 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); 0676 bool horizontalScroll = keyEvent->modifiers() & m_defaultHorizontalScrollModifiers; 0677 switch (keyEvent->key()) { 0678 case Qt::Key_Up: return scrollUp(); 0679 case Qt::Key_Down: return scrollDown(); 0680 case Qt::Key_Left: return scrollLeft(); 0681 case Qt::Key_Right: return scrollRight(); 0682 case Qt::Key_PageUp: return horizontalScroll ? scrollLeft(pageWidth) : scrollUp(pageHeight); 0683 case Qt::Key_PageDown: return horizontalScroll ? scrollRight(pageWidth) : scrollDown(pageHeight); 0684 case Qt::Key_Home: return horizontalScroll ? scrollLeft(contentWidth) : scrollUp(contentHeight); 0685 case Qt::Key_End: return horizontalScroll ? scrollRight(contentWidth) : scrollDown(contentHeight); 0686 default: break; 0687 } 0688 break; 0689 } 0690 0691 default: break; 0692 } 0693 0694 return false; 0695 } 0696 0697 #include "moc_wheelhandler.cpp"