MauiKit Controls

wheelhandler.cpp
1/*
2 * SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7#include "wheelhandler.h"
8#include <QWheelEvent>
9#include <QQuickWindow>
10
11KirigamiWheelEvent::KirigamiWheelEvent(QObject *parent)
12 : QObject(parent)
13{
14}
15
16KirigamiWheelEvent::~KirigamiWheelEvent()
17{
18}
19
20void KirigamiWheelEvent::initializeFromEvent(QWheelEvent *event)
21{
22 m_x = event->position().x();
23 m_y = event->position().y();
24 m_angleDelta = event->angleDelta();
25 m_pixelDelta = event->pixelDelta();
26 m_buttons = event->buttons();
27 m_modifiers = event->modifiers();
28 m_accepted = false;
29 m_inverted = event->inverted();
30}
31
32qreal KirigamiWheelEvent::x() const
33{
34 return m_x;
35}
36
37qreal KirigamiWheelEvent::y() const
38{
39 return m_y;
40}
41
43{
44 return m_angleDelta;
45}
46
48{
49 return m_pixelDelta;
50}
51
53{
54 return m_buttons;
55}
56
58{
59 return m_modifiers;
60}
61
63{
64 return m_inverted;
65}
66
67bool KirigamiWheelEvent::isAccepted()
68{
69 return m_accepted;
70}
71
72void KirigamiWheelEvent::setAccepted(bool accepted)
73{
74 m_accepted = accepted;
75}
76
77///////////////////////////////
78
79WheelFilterItem::WheelFilterItem(QQuickItem *parent)
80 : QQuickItem(parent)
81{
82 setEnabled(false);
83}
84
85///////////////////////////////
86
87WheelHandler::WheelHandler(QObject *parent)
88 : QObject(parent)
89 , m_filterItem(new WheelFilterItem(nullptr))
90{
91 m_filterItem->installEventFilter(this);
92
93 m_wheelScrollingTimer.setSingleShot(true);
94 m_wheelScrollingTimer.setInterval(m_wheelScrollingDuration);
95 m_wheelScrollingTimer.callOnTimeout([this]() {
96 setScrolling(false);
97 });
98
99 m_yScrollAnimation.setEasingCurve(QEasingCurve::OutCubic);
100
102 m_defaultPixelStepSize = 20 * scrollLines;
103 if (!m_explicitVStepSize && m_verticalStepSize != m_defaultPixelStepSize) {
104 m_verticalStepSize = m_defaultPixelStepSize;
105 Q_EMIT verticalStepSizeChanged();
106 }
107 if (!m_explicitHStepSize && m_horizontalStepSize != m_defaultPixelStepSize) {
108 m_horizontalStepSize = m_defaultPixelStepSize;
109 Q_EMIT horizontalStepSizeChanged();
110 }
111 });
112}
113
114WheelHandler::~WheelHandler()
115{
116 delete m_filterItem;
117}
118
120{
121 return m_flickable;
122}
123
124void WheelHandler::setTarget(QQuickItem *target)
125{
126 if (m_flickable == target) {
127 return;
128 }
129
130 if (target && !target->inherits("QQuickFlickable")) {
131 qmlWarning(this) << "target must be a QQuickFlickable";
132 return;
133 }
134
135 if (m_flickable) {
136 m_flickable->removeEventFilter(this);
137 disconnect(m_flickable, nullptr, m_filterItem, nullptr);
138 disconnect(m_flickable, &QQuickItem::parentChanged, this, &WheelHandler::_k_rebindScrollBars);
139 }
140
141 m_flickable = target;
142 m_filterItem->setParentItem(target);
143if (m_yScrollAnimation.targetObject()) {
144 m_yScrollAnimation.stop();
145 }
146 m_yScrollAnimation.setTargetObject(target);
147
148
149
150 if (target) {
151 target->installEventFilter(this);
152
153 // Stack WheelFilterItem over the Flickable's scrollable content
154 m_filterItem->stackAfter(target->property("contentItem").value<QQuickItem*>());
155 // Make it fill the Flickable
156 m_filterItem->setWidth(target->width());
157 m_filterItem->setHeight(target->height());
158 connect(target, &QQuickItem::widthChanged, m_filterItem, [this, target](){
159 m_filterItem->setWidth(target->width());
160 });
161 connect(target, &QQuickItem::heightChanged, m_filterItem, [this, target](){
162 m_filterItem->setHeight(target->height());
163 });
164 }
165
166 _k_rebindScrollBars();
167
168 Q_EMIT targetChanged();
169
170}
171
172void WheelHandler::_k_rebindScrollBars()
173{
174 struct ScrollBarAttached {
175 QObject *attached = nullptr;
176 QQuickItem *vertical = nullptr;
177 QQuickItem *horizontal = nullptr;
178 };
179
180 ScrollBarAttached attachedToFlickable;
181 ScrollBarAttached attachedToScrollView;
182
183 if (m_flickable) {
184 // Get ScrollBars so that we can filter them too, even if they're not
185 // in the bounds of the Flickable
186 const auto flickableChildren = m_flickable->children();
187 for (const auto child : flickableChildren) {
188 if (child->inherits("QQuickScrollBarAttached")) {
189 attachedToFlickable.attached = child;
190 attachedToFlickable.vertical = child->property("vertical").value<QQuickItem *>();
191 attachedToFlickable.horizontal = child->property("horizontal").value<QQuickItem *>();
192 break;
193 }
194 }
195
196 // Check ScrollView if there are no scrollbars attached to the Flickable.
197 // We need to check if the parent inherits QQuickScrollView in case the
198 // parent is another Flickable that already has a Kirigami WheelHandler.
199 auto flickableParent = m_flickable->parentItem();
200 if (flickableParent && flickableParent->inherits("QQuickScrollView")) {
201 const auto siblings = flickableParent->children();
202 for (const auto child : siblings) {
203 if (child->inherits("QQuickScrollBarAttached")) {
204 attachedToScrollView.attached = child;
205 attachedToScrollView.vertical = child->property("vertical").value<QQuickItem *>();
206 attachedToScrollView.horizontal = child->property("horizontal").value<QQuickItem *>();
207 break;
208 }
209 }
210 }
211 }
212
213 // Dilemma: ScrollBars can be attached to both ScrollView and Flickable,
214 // but only one of them should be shown anyway. Let's prefer Flickable.
215
216 struct ChosenScrollBar {
217 QObject *attached = nullptr;
218 QQuickItem *scrollBar = nullptr;
219 };
220
221 ChosenScrollBar vertical;
222 if (attachedToFlickable.vertical) {
223 vertical.attached = attachedToFlickable.attached;
224 vertical.scrollBar = attachedToFlickable.vertical;
225 } else if (attachedToScrollView.vertical) {
226 vertical.attached = attachedToScrollView.attached;
227 vertical.scrollBar = attachedToScrollView.vertical;
228 }
229
230 ChosenScrollBar horizontal;
231 if (attachedToFlickable.horizontal) {
232 horizontal.attached = attachedToFlickable.attached;
233 horizontal.scrollBar = attachedToFlickable.horizontal;
234 } else if (attachedToScrollView.horizontal) {
235 horizontal.attached = attachedToScrollView.attached;
236 horizontal.scrollBar = attachedToScrollView.horizontal;
237 }
238
239 // Flickable may get re-parented to or out of a ScrollView, so we need to
240 // redo the discovery process. This is especially important for
241 // Kirigami.ScrollablePage component.
242 if (m_flickable) {
243 if (attachedToFlickable.horizontal && attachedToFlickable.vertical) {
244 // But if both scrollbars are already those from the preferred
245 // Flickable, there's no need for rediscovery.
246 disconnect(m_flickable, &QQuickItem::parentChanged, this, &WheelHandler::_k_rebindScrollBars);
247 } else {
248 connect(m_flickable, &QQuickItem::parentChanged, this, &WheelHandler::_k_rebindScrollBars, Qt::UniqueConnection);
249 }
250 }
251
252 if (m_verticalScrollBar != vertical.scrollBar) {
253 if (m_verticalScrollBar) {
254 m_verticalScrollBar->removeEventFilter(this);
255 disconnect(m_verticalChangedConnection);
256 }
257 m_verticalScrollBar = vertical.scrollBar;
258 if (vertical.scrollBar) {
259 vertical.scrollBar->installEventFilter(this);
260 m_verticalChangedConnection = connect(vertical.attached, SIGNAL(verticalChanged()), this, SLOT(_k_rebindScrollBars()));
261 }
262 }
263
264 if (m_horizontalScrollBar != horizontal.scrollBar) {
265 if (m_horizontalScrollBar) {
266 m_horizontalScrollBar->removeEventFilter(this);
267 disconnect(m_horizontalChangedConnection);
268 }
269 m_horizontalScrollBar = horizontal.scrollBar;
270 if (horizontal.scrollBar) {
271 horizontal.scrollBar->installEventFilter(this);
272 m_horizontalChangedConnection = connect(horizontal.attached, SIGNAL(horizontalChanged()), this, SLOT(_k_rebindScrollBars()));
273 }
274 }
275}
276
278{
279 return m_verticalStepSize;
280}
281
282void WheelHandler::setVerticalStepSize(qreal stepSize)
283{
284 m_explicitVStepSize = true;
285 if (qFuzzyCompare(m_verticalStepSize, stepSize)) {
286 return;
287 }
288 // Mimic the behavior of QQuickScrollBar when stepSize is 0
289 if (qFuzzyIsNull(stepSize)) {
290 resetVerticalStepSize();
291 return;
292 }
293 m_verticalStepSize = stepSize;
294 Q_EMIT verticalStepSizeChanged();
295}
296
297void WheelHandler::resetVerticalStepSize()
298{
299 m_explicitVStepSize = false;
300 if (qFuzzyCompare(m_verticalStepSize, m_defaultPixelStepSize)) {
301 return;
302 }
303 m_verticalStepSize = m_defaultPixelStepSize;
304 Q_EMIT verticalStepSizeChanged();
305}
306
308{
309 return m_horizontalStepSize;
310}
311
312void WheelHandler::setHorizontalStepSize(qreal stepSize)
313{
314 m_explicitHStepSize = true;
315 if (qFuzzyCompare(m_horizontalStepSize, stepSize)) {
316 return;
317 }
318 // Mimic the behavior of QQuickScrollBar when stepSize is 0
319 if (qFuzzyIsNull(stepSize)) {
320 resetHorizontalStepSize();
321 return;
322 }
323 m_horizontalStepSize = stepSize;
324 Q_EMIT horizontalStepSizeChanged();
325}
326
327void WheelHandler::resetHorizontalStepSize()
328{
329 m_explicitHStepSize = false;
330 if (qFuzzyCompare(m_horizontalStepSize, m_defaultPixelStepSize)) {
331 return;
332 }
333 m_horizontalStepSize = m_defaultPixelStepSize;
334 Q_EMIT horizontalStepSizeChanged();
335}
336
338{
339 return m_pageScrollModifiers;
340}
341
342void WheelHandler::setPageScrollModifiers(Qt::KeyboardModifiers modifiers)
343{
344 if (m_pageScrollModifiers == modifiers) {
345 return;
346 }
347 m_pageScrollModifiers = modifiers;
348 Q_EMIT pageScrollModifiersChanged();
349}
350
351void WheelHandler::resetPageScrollModifiers()
352{
353 setPageScrollModifiers(m_defaultPageScrollModifiers);
354}
355
357{
358 return m_filterMouseEvents;
359}
360
361void WheelHandler::setFilterMouseEvents(bool enabled)
362{
363 if (m_filterMouseEvents == enabled) {
364 return;
365 }
366 m_filterMouseEvents = enabled;
367 Q_EMIT filterMouseEventsChanged();
368}
369
371{
372 return m_keyNavigationEnabled;
373}
374
375void WheelHandler::setKeyNavigationEnabled(bool enabled)
376{
377 if (m_keyNavigationEnabled == enabled) {
378 return;
379 }
380 m_keyNavigationEnabled = enabled;
381 Q_EMIT keyNavigationEnabledChanged();
382}
383
384void WheelHandler::classBegin()
385{
386 // Initializes smooth scrolling
387 m_engine = qmlEngine(this);
388 // auto units = m_engine->singletonInstance<Style *>("org.kde.kirigami.platform", "Units");
389 m_yScrollAnimation.setDuration(400);
390 // connect(units, &Kirigami::Platform::Units::longDurationChanged, this, [this] {
391 // m_yScrollAnimation.setDuration(static_cast<Kirigami::Platform::Units *>(sender())->longDuration());
392 // });
393}
394
395void WheelHandler::componentComplete()
396{
397}
398
399void WheelHandler::setScrolling(bool scrolling)
400{
401 if (m_wheelScrolling == scrolling) {
402 if (m_wheelScrolling) {
403 m_wheelScrollingTimer.start();
404 }
405 return;
406 }
407 m_wheelScrolling = scrolling;
408 m_filterItem->setEnabled(m_wheelScrolling);
409}
410
411Qt::Orientation WheelHandler::primaryOrientation() const
412{
413 return m_primaryOrientation;
414}
415
416void WheelHandler::setPrimaryOrientation(Qt::Orientation orientation)
417{
418 if(m_primaryOrientation == orientation)
419 {
420 return;
421 }
422
423 m_primaryOrientation = orientation;
424 Q_EMIT primaryOrientationChanged();
425}
426
427
428bool WheelHandler::scrollFlickable(QPointF pixelDelta, QPointF angleDelta, Qt::KeyboardModifiers modifiers)
429{
430 if (!m_flickable || (pixelDelta.isNull() && angleDelta.isNull())) {
431 return false;
432 }
433
434 const qreal width = m_flickable->width();
435 const qreal height = m_flickable->height();
436 const qreal contentWidth = m_flickable->property("contentWidth").toReal();
437 const qreal contentHeight = m_flickable->property("contentHeight").toReal();
438 const qreal contentX = m_flickable->property("contentX").toReal();
439 const qreal contentY = m_flickable->property("contentY").toReal();
440 const qreal topMargin = m_flickable->property("topMargin").toReal();
441 const qreal bottomMargin = m_flickable->property("bottomMargin").toReal();
442 const qreal leftMargin = m_flickable->property("leftMargin").toReal();
443 const qreal rightMargin = m_flickable->property("rightMargin").toReal();
444 const qreal originX = m_flickable->property("originX").toReal();
445 const qreal originY = m_flickable->property("originY").toReal();
446 const qreal pageWidth = width - leftMargin - rightMargin;
447 const qreal pageHeight = height - topMargin - bottomMargin;
448 const auto window = m_flickable->window();
449 const qreal devicePixelRatio = window != nullptr ? window->devicePixelRatio() : qGuiApp->devicePixelRatio();
450
451 // HACK: Only transpose deltas when not using xcb in order to not conflict with xcb's own delta transposing
452 if (m_primaryOrientation == Qt::Orientation::Horizontal) {
453 angleDelta = angleDelta.transposed();
454 pixelDelta = pixelDelta.transposed();
455 }
456
457 const qreal xTicks = angleDelta.x() / 120;
458 const qreal yTicks = angleDelta.y() / 120;
459 qreal xChange;
460 qreal yChange;
461 bool scrolled = false;
462
463 // Scroll X
464 if (contentWidth > pageWidth) {
465 // Use page size with pageScrollModifiers. Matches QScrollBar, which uses QAbstractSlider behavior.
466 if (modifiers & m_pageScrollModifiers) {
467 xChange = qBound(-pageWidth, xTicks * pageWidth, pageWidth);
468 } else if (pixelDelta.x() != 0) {
469 xChange = pixelDelta.x();
470 } else {
471 xChange = xTicks * m_horizontalStepSize;
472 }
473
474 // contentX and contentY use reversed signs from what x and y would normally use, so flip the signs
475
476 qreal minXExtent = leftMargin - originX;
477 qreal maxXExtent = width - (contentWidth + rightMargin + originX);
478
479 qreal newContentX = qBound(-minXExtent, contentX - xChange, -maxXExtent);
480 // Flickable::pixelAligned rounds the position, so round to mimic that behavior.
481 // Rounding prevents fractional positioning from causing text to be
482 // clipped off on the top and bottom.
483 // Multiply by devicePixelRatio before rounding and divide by devicePixelRatio
484 // after to make position match pixels on the screen more closely.
485 newContentX = std::round(newContentX * devicePixelRatio) / devicePixelRatio;
486 if (contentX != newContentX) {
487 scrolled = true;
488 m_flickable->setProperty("contentX", newContentX);
489 }
490 }
491
492 // Scroll Y
493 if (contentHeight > pageHeight) {
494 if (modifiers & m_pageScrollModifiers) {
495 yChange = qBound(-pageHeight, yTicks * pageHeight, pageHeight);
496 } else if (pixelDelta.y() != 0) {
497 yChange = pixelDelta.y();
498 } else {
499 yChange = yTicks * m_verticalStepSize;
500 }
501
502 // contentX and contentY use reversed signs from what x and y would normally use, so flip the signs
503
504 qreal minYExtent = topMargin - originY;
505 qreal maxYExtent = height - (contentHeight + bottomMargin + originY);
506
507 qreal newContentY = qBound(-minYExtent, contentY - yChange, -maxYExtent);
508 // Flickable::pixelAligned rounds the position, so round to mimic that behavior.
509 // Rounding prevents fractional positioning from causing text to be
510 // clipped off on the top and bottom.
511 // Multiply by devicePixelRatio before rounding and divide by devicePixelRatio
512 // after to make position match pixels on the screen more closely.
513 newContentY = std::round(newContentY * devicePixelRatio) / devicePixelRatio;
514 if (contentY != newContentY) {
515 scrolled = true;
516 m_flickable->setProperty("contentY", newContentY);
517 }
518 }
519
520 return scrolled;
521}
522
523bool WheelHandler::scrollUp(qreal stepSize)
524{
525 if (qFuzzyIsNull(stepSize)) {
526 return false;
527 } else if (stepSize < 0) {
528 stepSize = m_verticalStepSize;
529 }
530 // contentY uses reversed sign
531 return scrollFlickable(QPointF(0, stepSize));
532}
533
534bool WheelHandler::scrollDown(qreal stepSize)
535{
536 if (qFuzzyIsNull(stepSize)) {
537 return false;
538 } else if (stepSize < 0) {
539 stepSize = m_verticalStepSize;
540 }
541 // contentY uses reversed sign
542 return scrollFlickable(QPointF(0, -stepSize));
543}
544
545bool WheelHandler::scrollLeft(qreal stepSize)
546{
547 if (qFuzzyIsNull(stepSize)) {
548 return false;
549 } else if (stepSize < 0) {
550 stepSize = m_horizontalStepSize;
551 }
552 // contentX uses reversed sign
553 return scrollFlickable(QPoint(stepSize, 0));
554}
555
556bool WheelHandler::scrollRight(qreal stepSize)
557{
558 if (qFuzzyIsNull(stepSize)) {
559 return false;
560 } else if (stepSize < 0) {
561 stepSize = m_horizontalStepSize;
562 }
563 // contentX uses reversed sign
564 return scrollFlickable(QPoint(-stepSize, 0));
565}
566
567bool WheelHandler::eventFilter(QObject *watched, QEvent *event)
568{
569 auto item = qobject_cast<QQuickItem *>(watched);
570 if (!item || !item->isEnabled()) {
571 return false;
572 }
573
574 qreal contentWidth = 0;
575 qreal contentHeight = 0;
576 qreal pageWidth = 0;
577 qreal pageHeight = 0;
578 if (m_flickable) {
579 contentWidth = m_flickable->property("contentWidth").toReal();
580 contentHeight = m_flickable->property("contentHeight").toReal();
581 pageWidth = m_flickable->width() - m_flickable->property("leftMargin").toReal() - m_flickable->property("rightMargin").toReal();
582 pageHeight = m_flickable->height() - m_flickable->property("topMargin").toReal() - m_flickable->property("bottomMargin").toReal();
583 }
584
585 // The code handling touch, mouse and hover events is mostly copied/adapted from QQuickScrollView::childMouseEventFilter()
586 switch (event->type()) {
587 case QEvent::Wheel: {
588 // QQuickScrollBar::interactive handling Matches behavior in QQuickScrollView::eventFilter()
589 if (m_filterMouseEvents) {
590 if (m_verticalScrollBar) {
591 m_verticalScrollBar->setProperty("interactive", true);
592 }
593 if (m_horizontalScrollBar) {
594 m_horizontalScrollBar->setProperty("interactive", true);
595 }
596 }
597 QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event);
598
599 // Can't use wheelEvent->deviceType() to determine device type since on Wayland mouse is always regarded as touchpad
600 // https://invent.kde.org/qt/qt/qtwayland/-/blob/e695a39519a7629c1549275a148cfb9ab99a07a9/src/client/qwaylandinputdevice.cpp#L445
601 // and we can only expect a touchpad never generates the same angle delta as a mouse
602 m_wasTouched = std::abs(wheelEvent->angleDelta().y()) != 120 && std::abs(wheelEvent->angleDelta().x()) != 120;
603
604 // NOTE: On X11 with libinput, pixelDelta is identical to angleDelta when using a mouse that shouldn't use pixelDelta.
605 // If faulty pixelDelta, reset pixelDelta to (0,0).
606 if (wheelEvent->pixelDelta() == wheelEvent->angleDelta()) {
607 // In order to change any of the data, we have to create a whole new QWheelEvent from its constructor.
608 QWheelEvent newWheelEvent(wheelEvent->position(),
609 wheelEvent->globalPosition(),
610 QPoint(0, 0), // pixelDelta
611 wheelEvent->angleDelta(),
612 wheelEvent->buttons(),
613 wheelEvent->modifiers(),
614 wheelEvent->phase(),
615 wheelEvent->inverted(),
616 wheelEvent->source());
617 m_kirigamiWheelEvent.initializeFromEvent(&newWheelEvent);
618 } else {
619 m_kirigamiWheelEvent.initializeFromEvent(wheelEvent);
620 }
621
622 Q_EMIT wheel(&m_kirigamiWheelEvent);
623
624 if (m_kirigamiWheelEvent.isAccepted()) {
625 return true;
626 }
627
628 bool scrolled = false;
629 if (m_scrollFlickableTarget || (contentHeight <= pageHeight && contentWidth <= pageWidth)) {
630 // Don't use pixelDelta from the event unless angleDelta is not available
631 // because scrolling by pixelDelta is too slow on Wayland with libinput.
632 QPointF pixelDelta = m_kirigamiWheelEvent.angleDelta().isNull() ? m_kirigamiWheelEvent.pixelDelta() : QPoint(0, 0);
633 scrolled = scrollFlickable(pixelDelta, m_kirigamiWheelEvent.angleDelta(), Qt::KeyboardModifiers(m_kirigamiWheelEvent.modifiers()));
634 }
635 setScrolling(scrolled);
636
637 // NOTE: Wheel events created by touchpad gestures with pixel deltas will cause scrolling to jump back
638 // to where scrolling started unless the event is always accepted before it reaches the Flickable.
639 bool flickableWillUseGestureScrolling = !(wheelEvent->source() == Qt::MouseEventNotSynthesized || wheelEvent->pixelDelta().isNull());
640 return scrolled || m_blockTargetWheel || flickableWillUseGestureScrolling;
641 }
642
643 case QEvent::TouchBegin: {
644 m_wasTouched = true;
645 if (!m_filterMouseEvents) {
646 break;
647 }
648 if (m_verticalScrollBar) {
649 m_verticalScrollBar->setProperty("interactive", false);
650 }
651 if (m_horizontalScrollBar) {
652 m_horizontalScrollBar->setProperty("interactive", false);
653 }
654 break;
655 }
656
657 case QEvent::TouchEnd: {
658 m_wasTouched = false;
659 break;
660 }
661
663 // NOTE: Flickable does not handle touch events, only synthesized mouse events
664 m_wasTouched = static_cast<QMouseEvent *>(event)->source() != Qt::MouseEventNotSynthesized;
665 if (!m_filterMouseEvents) {
666 break;
667 }
668 if (!m_wasTouched) {
669 if (m_verticalScrollBar) {
670 m_verticalScrollBar->setProperty("interactive", true);
671 }
672 if (m_horizontalScrollBar) {
673 m_horizontalScrollBar->setProperty("interactive", true);
674 }
675 break;
676 }
677 return !m_wasTouched && item == m_flickable;
678 }
679
682 setScrolling(false);
683 if (!m_filterMouseEvents) {
684 break;
685 }
686 if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized && item == m_flickable) {
687 return true;
688 }
689 break;
690 }
691
693 case QEvent::HoverMove: {
694 if (!m_filterMouseEvents) {
695 break;
696 }
697 if (m_wasTouched && (item == m_verticalScrollBar || item == m_horizontalScrollBar)) {
698 if (m_verticalScrollBar) {
699 m_verticalScrollBar->setProperty("interactive", true);
700 }
701 if (m_horizontalScrollBar) {
702 m_horizontalScrollBar->setProperty("interactive", true);
703 }
704 }
705 break;
706 }
707
708 case QEvent::KeyPress: {
709 if (!m_keyNavigationEnabled) {
710 break;
711 }
712 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
713 bool horizontalScroll = keyEvent->modifiers() & m_defaultHorizontalScrollModifiers;
714 switch (keyEvent->key()) {
715 case Qt::Key_Up:
716 return scrollUp();
717 case Qt::Key_Down:
718 return scrollDown();
719 case Qt::Key_Left:
720 return scrollLeft();
721 case Qt::Key_Right:
722 return scrollRight();
723 case Qt::Key_PageUp:
724 return horizontalScroll ? scrollLeft(pageWidth) : scrollUp(pageHeight);
725 case Qt::Key_PageDown:
726 return horizontalScroll ? scrollRight(pageWidth) : scrollDown(pageHeight);
727 case Qt::Key_Home:
728 return horizontalScroll ? scrollLeft(contentWidth) : scrollUp(contentHeight);
729 case Qt::Key_End:
730 return horizontalScroll ? scrollRight(contentWidth) : scrollDown(contentHeight);
731 default:
732 break;
733 }
734 break;
735 }
736
737 default:
738 break;
739 }
740
741 return false;
742}
QML_ELEMENTqreal x
x: real
QPointF pixelDelta
pixelDelta: point
QPointF angleDelta
angleDelta: point
int buttons
buttons: int
bool inverted
inverted: bool
int modifiers
modifiers: int
bool accepted
accepted: bool
bool filterMouseEvents
This property holds whether the WheelHandler filters mouse events like a Qt Quick Controls ScrollView...
qreal horizontalStepSize
This property holds the horizontal step size.
Qt::KeyboardModifiers pageScrollModifiers
This property holds the keyboard modifiers that will be used to start page scrolling.
bool keyNavigationEnabled
This property holds whether the WheelHandler handles keyboard scrolling.
Q_INVOKABLE bool scrollDown(qreal stepSize=-1)
Scroll down one step.
QML_ELEMENTQQuickItem * target
This property holds the Qt Quick Flickable that the WheelHandler will control.
Q_INVOKABLE bool scrollRight(qreal stepSize=-1)
Scroll right one step.
void wheel(KirigamiWheelEvent *wheel)
This signal is emitted when a wheel event reaches the event filter, just before scrolling is handled.
qreal verticalStepSize
This property holds the vertical step size.
Q_INVOKABLE bool scrollUp(qreal stepSize=-1)
Scroll up one step.
Q_INVOKABLE bool scrollLeft(qreal stepSize=-1)
Scroll left one step.
QWidget * window(QObject *job)
QStyleHints * styleHints()
Qt::KeyboardModifiers modifiers() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
virtual bool event(QEvent *e)
void installEventFilter(QObject *filterObj)
T qobject_cast(QObject *object)
qreal devicePixelRatio() const const
bool isNull() const const
int x() const const
int y() const const
bool isNull() const const
QPointF transposed() const const
qreal x() const const
qreal y() const const
void heightChanged()
void parentChanged(QQuickItem *)
void widthChanged()
Qt::MouseButtons buttons() const const
QPointF globalPosition() const const
QPointF position() const const
void wheelScrollLinesChanged(int scrollLines)
UniqueConnection
typedef KeyboardModifiers
MouseEventNotSynthesized
Orientation
void keyEvent(KeyAction action, QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier, int delay)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void start()
void setDuration(int msecs)
Qt::MouseEventSource source() const const
QPoint angleDelta() const const
bool inverted() const const
Qt::ScrollPhase phase() const const
QPoint pixelDelta() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:12:43 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.