7 #include "wheelhandler.h"
10 #include <QQuickWindow>
11 #include <QWheelEvent>
13 KirigamiWheelEvent::KirigamiWheelEvent(
QObject *parent)
18 KirigamiWheelEvent::~KirigamiWheelEvent()
22 void KirigamiWheelEvent::initializeFromEvent(
QWheelEvent *event)
24 m_x =
event->position().x();
25 m_y =
event->position().y();
26 m_angleDelta =
event->angleDelta();
27 m_pixelDelta =
event->pixelDelta();
28 m_buttons =
event->buttons();
29 m_modifiers =
event->modifiers();
31 m_inverted =
event->inverted();
69 bool KirigamiWheelEvent::isAccepted()
74 void KirigamiWheelEvent::setAccepted(
bool accepted)
76 m_accepted = accepted;
81 WheelFilterItem::WheelFilterItem(
QQuickItem *parent)
89 WheelHandler::WheelHandler(
QObject *parent)
91 , m_filterItem(new WheelFilterItem(nullptr))
95 m_wheelScrollingTimer.setSingleShot(
true);
96 m_wheelScrollingTimer.setInterval(m_wheelScrollingDuration);
97 m_wheelScrollingTimer.callOnTimeout([
this]() {
102 m_defaultPixelStepSize = 20 * scrollLines;
103 if (!m_explicitVStepSize && m_verticalStepSize != m_defaultPixelStepSize) {
104 m_verticalStepSize = m_defaultPixelStepSize;
105 Q_EMIT verticalStepSizeChanged();
107 if (!m_explicitHStepSize && m_horizontalStepSize != m_defaultPixelStepSize) {
108 m_horizontalStepSize = m_defaultPixelStepSize;
109 Q_EMIT horizontalStepSizeChanged();
114 WheelHandler::~WheelHandler()
124 void WheelHandler::setTarget(
QQuickItem *target)
126 if (m_flickable == target) {
130 if (target && !target->
inherits(
"QQuickFlickable")) {
131 qmlWarning(
this) <<
"target must be a QQuickFlickable";
136 m_flickable->removeEventFilter(
this);
137 disconnect(m_flickable,
nullptr, m_filterItem,
nullptr);
141 m_flickable = target;
150 m_filterItem->setWidth(target->
width());
151 m_filterItem->setHeight(target->
height());
153 m_filterItem->setWidth(target->
width());
156 m_filterItem->setHeight(target->
height());
160 _k_rebindScrollBars();
162 Q_EMIT targetChanged();
165 void WheelHandler::_k_rebindScrollBars()
167 struct ScrollBarAttached {
173 ScrollBarAttached attachedToFlickable;
174 ScrollBarAttached attachedToScrollView;
179 const auto flickableChildren = m_flickable->
children();
180 for (
const auto child : flickableChildren) {
181 if (child->inherits(
"QQuickScrollBarAttached")) {
182 attachedToFlickable.attached = child;
183 attachedToFlickable.vertical = child->property(
"vertical").value<
QQuickItem *>();
184 attachedToFlickable.horizontal = child->property(
"horizontal").value<
QQuickItem *>();
192 auto flickableParent = m_flickable->
parentItem();
193 if (flickableParent && flickableParent->inherits(
"QQuickScrollView")) {
194 const auto siblings = flickableParent->
children();
195 for (
const auto child : siblings) {
196 if (child->inherits(
"QQuickScrollBarAttached")) {
197 attachedToScrollView.attached = child;
198 attachedToScrollView.vertical = child->property(
"vertical").value<
QQuickItem *>();
199 attachedToScrollView.horizontal = child->property(
"horizontal").value<
QQuickItem *>();
209 struct ChosenScrollBar {
214 ChosenScrollBar vertical;
215 if (attachedToFlickable.vertical) {
216 vertical.attached = attachedToFlickable.attached;
217 vertical.scrollBar = attachedToFlickable.vertical;
218 }
else if (attachedToScrollView.vertical) {
219 vertical.attached = attachedToScrollView.attached;
220 vertical.scrollBar = attachedToScrollView.vertical;
223 ChosenScrollBar horizontal;
224 if (attachedToFlickable.horizontal) {
225 horizontal.attached = attachedToFlickable.attached;
226 horizontal.scrollBar = attachedToFlickable.horizontal;
227 }
else if (attachedToScrollView.horizontal) {
228 horizontal.attached = attachedToScrollView.attached;
229 horizontal.scrollBar = attachedToScrollView.horizontal;
236 if (attachedToFlickable.horizontal && attachedToFlickable.vertical) {
245 if (m_verticalScrollBar != vertical.scrollBar) {
246 if (m_verticalScrollBar) {
247 m_verticalScrollBar->removeEventFilter(
this);
248 disconnect(m_verticalChangedConnection);
250 m_verticalScrollBar = vertical.scrollBar;
251 if (vertical.scrollBar) {
252 vertical.scrollBar->installEventFilter(
this);
253 m_verticalChangedConnection = connect(vertical.attached, SIGNAL(verticalChanged()),
this, SLOT(_k_rebindScrollBars()));
257 if (m_horizontalScrollBar != horizontal.scrollBar) {
258 if (m_horizontalScrollBar) {
259 m_horizontalScrollBar->removeEventFilter(
this);
260 disconnect(m_horizontalChangedConnection);
262 m_horizontalScrollBar = horizontal.scrollBar;
263 if (horizontal.scrollBar) {
264 horizontal.scrollBar->installEventFilter(
this);
265 m_horizontalChangedConnection = connect(horizontal.attached, SIGNAL(horizontalChanged()),
this, SLOT(_k_rebindScrollBars()));
272 return m_verticalStepSize;
275 void WheelHandler::setVerticalStepSize(qreal stepSize)
277 m_explicitVStepSize =
true;
278 if (qFuzzyCompare(m_verticalStepSize, stepSize)) {
282 if (qFuzzyIsNull(stepSize)) {
283 resetVerticalStepSize();
286 m_verticalStepSize = stepSize;
287 Q_EMIT verticalStepSizeChanged();
290 void WheelHandler::resetVerticalStepSize()
292 m_explicitVStepSize =
false;
293 if (qFuzzyCompare(m_verticalStepSize, m_defaultPixelStepSize)) {
296 m_verticalStepSize = m_defaultPixelStepSize;
297 Q_EMIT verticalStepSizeChanged();
302 return m_horizontalStepSize;
305 void WheelHandler::setHorizontalStepSize(qreal stepSize)
307 m_explicitHStepSize =
true;
308 if (qFuzzyCompare(m_horizontalStepSize, stepSize)) {
312 if (qFuzzyIsNull(stepSize)) {
313 resetHorizontalStepSize();
316 m_horizontalStepSize = stepSize;
317 Q_EMIT horizontalStepSizeChanged();
320 void WheelHandler::resetHorizontalStepSize()
322 m_explicitHStepSize =
false;
323 if (qFuzzyCompare(m_horizontalStepSize, m_defaultPixelStepSize)) {
326 m_horizontalStepSize = m_defaultPixelStepSize;
327 Q_EMIT horizontalStepSizeChanged();
332 return m_pageScrollModifiers;
337 if (m_pageScrollModifiers == modifiers) {
340 m_pageScrollModifiers = modifiers;
341 Q_EMIT pageScrollModifiersChanged();
344 void WheelHandler::resetPageScrollModifiers()
346 setPageScrollModifiers(m_defaultPageScrollModifiers);
351 return m_filterMouseEvents;
354 void WheelHandler::setFilterMouseEvents(
bool enabled)
356 if (m_filterMouseEvents == enabled) {
359 m_filterMouseEvents = enabled;
360 Q_EMIT filterMouseEventsChanged();
365 return m_keyNavigationEnabled;
368 void WheelHandler::setKeyNavigationEnabled(
bool enabled)
370 if (m_keyNavigationEnabled == enabled) {
373 m_keyNavigationEnabled = enabled;
374 Q_EMIT keyNavigationEnabledChanged();
377 void WheelHandler::setScrolling(
bool scrolling)
379 if (m_wheelScrolling == scrolling) {
380 if (m_wheelScrolling) {
381 m_wheelScrollingTimer.start();
385 m_wheelScrolling = scrolling;
386 m_filterItem->setEnabled(m_wheelScrolling);
391 if (!m_flickable || (pixelDelta.
isNull() && angleDelta.
isNull())) {
395 const qreal width = m_flickable->width();
396 const qreal height = m_flickable->height();
397 const qreal contentWidth = m_flickable->property(
"contentWidth").toReal();
398 const qreal contentHeight = m_flickable->property(
"contentHeight").toReal();
399 const qreal contentX = m_flickable->property(
"contentX").toReal();
400 const qreal contentY = m_flickable->property(
"contentY").toReal();
401 const qreal topMargin = m_flickable->property(
"topMargin").toReal();
402 const qreal bottomMargin = m_flickable->property(
"bottomMargin").toReal();
403 const qreal leftMargin = m_flickable->property(
"leftMargin").toReal();
404 const qreal rightMargin = m_flickable->property(
"rightMargin").toReal();
405 const qreal originX = m_flickable->property(
"originX").toReal();
406 const qreal originY = m_flickable->property(
"originY").toReal();
407 const qreal pageWidth = width - leftMargin - rightMargin;
408 const qreal pageHeight = height - topMargin - bottomMargin;
413 if (modifiers & m_defaultHorizontalScrollModifiers && qGuiApp->platformName() !=
QLatin1String(
"xcb")) {
418 const qreal xTicks = angleDelta.
x() / 120;
419 const qreal yTicks = angleDelta.
y() / 120;
422 bool scrolled =
false;
425 if (contentWidth > pageWidth) {
427 if (modifiers & m_pageScrollModifiers) {
428 xChange = qBound(-pageWidth, xTicks * pageWidth, pageWidth);
429 }
else if (pixelDelta.
x() != 0) {
430 xChange = pixelDelta.
x();
432 xChange = xTicks * m_horizontalStepSize;
437 qreal minXExtent = leftMargin - originX;
438 qreal maxXExtent = width - (contentWidth + rightMargin + originX);
440 qreal newContentX = qBound(-minXExtent, contentX - xChange, -maxXExtent);
446 newContentX = std::round(newContentX * devicePixelRatio) / devicePixelRatio;
447 if (contentX != newContentX) {
449 m_flickable->setProperty(
"contentX", newContentX);
454 if (contentHeight > pageHeight) {
455 if (modifiers & m_pageScrollModifiers) {
456 yChange = qBound(-pageHeight, yTicks * pageHeight, pageHeight);
457 }
else if (pixelDelta.
y() != 0) {
458 yChange = pixelDelta.
y();
460 yChange = yTicks * m_verticalStepSize;
465 qreal minYExtent = topMargin - originY;
466 qreal maxYExtent = height - (contentHeight + bottomMargin + originY);
468 qreal newContentY = qBound(-minYExtent, contentY - yChange, -maxYExtent);
474 newContentY = std::round(newContentY * devicePixelRatio) / devicePixelRatio;
475 if (contentY != newContentY) {
477 m_flickable->setProperty(
"contentY", newContentY);
486 if (qFuzzyIsNull(stepSize)) {
488 }
else if (stepSize < 0) {
489 stepSize = m_verticalStepSize;
492 return scrollFlickable(
QPointF(0, stepSize));
497 if (qFuzzyIsNull(stepSize)) {
499 }
else if (stepSize < 0) {
500 stepSize = m_verticalStepSize;
503 return scrollFlickable(
QPointF(0, -stepSize));
508 if (qFuzzyIsNull(stepSize)) {
510 }
else if (stepSize < 0) {
511 stepSize = m_horizontalStepSize;
514 return scrollFlickable(
QPoint(stepSize, 0));
519 if (qFuzzyIsNull(stepSize)) {
521 }
else if (stepSize < 0) {
522 stepSize = m_horizontalStepSize;
525 return scrollFlickable(
QPoint(-stepSize, 0));
528 bool WheelHandler::eventFilter(
QObject *watched,
QEvent *event)
530 auto item = qobject_cast<QQuickItem*>(watched);
531 if (!item || !item->isEnabled()) {
535 qreal contentWidth = 0;
536 qreal contentHeight = 0;
538 qreal pageHeight = 0;
540 contentWidth = m_flickable->property(
"contentWidth").toReal();
541 contentHeight = m_flickable->property(
"contentHeight").toReal();
542 pageWidth = m_flickable->width() - m_flickable->property(
"leftMargin").toReal() - m_flickable->property(
"rightMargin").toReal();
543 pageHeight = m_flickable->height() - m_flickable->property(
"topMargin").toReal() - m_flickable->property(
"bottomMargin").toReal();
547 switch (
event->type()) {
550 if (m_filterMouseEvents) {
551 if (m_verticalScrollBar) {
552 m_verticalScrollBar->setProperty(
"interactive",
true);
554 if (m_horizontalScrollBar) {
555 m_horizontalScrollBar->setProperty(
"interactive",
true);
575 m_kirigamiWheelEvent.initializeFromEvent(&newWheelEvent);
577 m_kirigamiWheelEvent.initializeFromEvent(wheelEvent);
580 Q_EMIT wheel(&m_kirigamiWheelEvent);
582 if (m_kirigamiWheelEvent.isAccepted()) {
586 bool scrolled =
false;
587 if (m_scrollFlickableTarget || (contentHeight <= pageHeight && contentWidth <= pageWidth)) {
590 QPointF pixelDelta = m_kirigamiWheelEvent.angleDelta().
isNull() ? m_kirigamiWheelEvent.pixelDelta() :
QPoint(0, 0);
591 scrolled = scrollFlickable(pixelDelta,
592 m_kirigamiWheelEvent.angleDelta(),
595 setScrolling(scrolled);
600 return scrolled || m_blockTargetWheel || flickableWillUseGestureScrolling;
605 if (!m_filterMouseEvents) {
608 if (m_verticalScrollBar) {
609 m_verticalScrollBar->setProperty(
"interactive",
false);
611 if (m_horizontalScrollBar) {
612 m_horizontalScrollBar->setProperty(
"interactive",
false);
618 m_wasTouched =
false;
625 if (!m_filterMouseEvents) {
629 if (m_verticalScrollBar) {
630 m_verticalScrollBar->setProperty(
"interactive",
true);
632 if (m_horizontalScrollBar) {
633 m_horizontalScrollBar->setProperty(
"interactive",
true);
637 return !m_wasTouched && item == m_flickable;
643 if (!m_filterMouseEvents) {
654 if (!m_filterMouseEvents) {
657 if (m_wasTouched && (item == m_verticalScrollBar || item == m_horizontalScrollBar)) {
658 if (m_verticalScrollBar) {
659 m_verticalScrollBar->setProperty(
"interactive",
true);
661 if (m_horizontalScrollBar) {
662 m_horizontalScrollBar->setProperty(
"interactive",
true);
669 if (!m_keyNavigationEnabled) {
673 bool horizontalScroll =
keyEvent->modifiers() & m_defaultHorizontalScrollModifiers;
679 case Qt::Key_PageUp:
return horizontalScroll ? scrollLeft(pageWidth) : scrollUp(pageHeight);
680 case Qt::Key_PageDown:
return horizontalScroll ? scrollRight(pageWidth) : scrollDown(pageHeight);
681 case Qt::Key_Home:
return horizontalScroll ? scrollLeft(contentWidth) : scrollUp(contentHeight);
682 case Qt::Key_End:
return horizontalScroll ? scrollRight(contentWidth) : scrollDown(contentHeight);