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