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 }