Kirigami2

wheelhandler.cpp
1 /*
2  * SPDX-FileCopyrightText: 2019 Marco Martin <[email protected]>
3  *
4  * SPDX-License-Identifier: LGPL-2.0-or-later
5  */
6 
7 #include "wheelhandler.h"
8 #include "settings.h"
9 #include <QQuickItem>
10 #include <QQuickWindow>
11 #include <QWheelEvent>
12 
13 KirigamiWheelEvent::KirigamiWheelEvent(QObject *parent)
14  : QObject(parent)
15 {
16 }
17 
18 KirigamiWheelEvent::~KirigamiWheelEvent()
19 {
20 }
21 
22 void KirigamiWheelEvent::initializeFromEvent(QWheelEvent *event)
23 {
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();
30  m_accepted = false;
31  m_inverted = event->inverted();
32 }
33 
34 qreal KirigamiWheelEvent::x() const
35 {
36  return m_x;
37 }
38 
39 qreal KirigamiWheelEvent::y() const
40 {
41  return m_y;
42 }
43 
45 {
46  return m_angleDelta;
47 }
48 
50 {
51  return m_pixelDelta;
52 }
53 
55 {
56  return m_buttons;
57 }
58 
60 {
61  return m_modifiers;
62 }
63 
65 {
66  return m_inverted;
67 }
68 
69 bool KirigamiWheelEvent::isAccepted()
70 {
71  return m_accepted;
72 }
73 
74 void KirigamiWheelEvent::setAccepted(bool accepted)
75 {
76  m_accepted = accepted;
77 }
78 
79 ///////////////////////////////
80 
81 WheelFilterItem::WheelFilterItem(QQuickItem *parent)
82  : QQuickItem(parent)
83 {
84  setEnabled(false);
85 }
86 
87 ///////////////////////////////
88 
89 WheelHandler::WheelHandler(QObject *parent)
90  : QObject(parent)
91  , m_filterItem(new WheelFilterItem(nullptr))
92 {
93  m_filterItem->installEventFilter(this);
94 
95  m_wheelScrollingTimer.setSingleShot(true);
96  m_wheelScrollingTimer.setInterval(m_wheelScrollingDuration);
97  m_wheelScrollingTimer.callOnTimeout([this]() {
98  setScrolling(false);
99  });
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 
114 WheelHandler::~WheelHandler()
115 {
116  delete m_filterItem;
117 }
118 
120 {
121  return m_flickable;
122 }
123 
124 void 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);
143 
144  if (target) {
145  target->installEventFilter(this);
146 
147  // Stack WheelFilterItem over the Flickable's scrollable content
148  m_filterItem->stackAfter(target->property("contentItem").value<QQuickItem*>());
149  // Make it fill the Flickable
150  m_filterItem->setWidth(target->width());
151  m_filterItem->setHeight(target->height());
152  connect(target, &QQuickItem::widthChanged, m_filterItem, [this, target]() {
153  m_filterItem->setWidth(target->width());
154  });
155  connect(target, &QQuickItem::heightChanged, m_filterItem, [this, target]() {
156  m_filterItem->setHeight(target->height());
157  });
158  }
159 
160  _k_rebindScrollBars();
161 
162  Q_EMIT targetChanged();
163 }
164 
165 void WheelHandler::_k_rebindScrollBars()
166 {
167  struct ScrollBarAttached {
168  QObject *attached = nullptr;
169  QQuickItem *vertical = nullptr;
170  QQuickItem *horizontal = nullptr;
171  };
172 
173  ScrollBarAttached attachedToFlickable;
174  ScrollBarAttached attachedToScrollView;
175 
176  if (m_flickable) {
177  // Get ScrollBars so that we can filter them too, even if they're not
178  // in the bounds of the Flickable
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 *>();
185  break;
186  }
187  }
188 
189  // Check ScrollView if there are no scrollbars attached to the Flickable.
190  // We need to check if the parent inherits QQuickScrollView in case the
191  // parent is another Flickable that already has a Kirigami WheelHandler.
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 *>();
200  break;
201  }
202  }
203  }
204  }
205 
206  // Dilemma: ScrollBars can be attached to both ScrollView and Flickable,
207  // but only one of them should be shown anyway. Let's prefer Flickable.
208 
209  struct ChosenScrollBar {
210  QObject *attached = nullptr;
211  QQuickItem *scrollBar = nullptr;
212  };
213 
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;
221  }
222 
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;
230  }
231 
232  // Flickable may get re-parented to or out of a ScrollView, so we need to
233  // redo the discovery process. This is especially important for
234  // Kirigami.ScrollablePage component.
235  if (m_flickable) {
236  if (attachedToFlickable.horizontal && attachedToFlickable.vertical) {
237  // But if both scrollbars are already those from the preferred
238  // Flickable, there's no need for rediscovery.
239  disconnect(m_flickable, &QQuickItem::parentChanged, this, &WheelHandler::_k_rebindScrollBars);
240  } else {
241  connect(m_flickable, &QQuickItem::parentChanged, this, &WheelHandler::_k_rebindScrollBars, Qt::UniqueConnection);
242  }
243  }
244 
245  if (m_verticalScrollBar != vertical.scrollBar) {
246  if (m_verticalScrollBar) {
247  m_verticalScrollBar->removeEventFilter(this);
248  disconnect(m_verticalChangedConnection);
249  }
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()));
254  }
255  }
256 
257  if (m_horizontalScrollBar != horizontal.scrollBar) {
258  if (m_horizontalScrollBar) {
259  m_horizontalScrollBar->removeEventFilter(this);
260  disconnect(m_horizontalChangedConnection);
261  }
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()));
266  }
267  }
268 }
269 
270 qreal WheelHandler::verticalStepSize() const
271 {
272  return m_verticalStepSize;
273 }
274 
275 void WheelHandler::setVerticalStepSize(qreal stepSize)
276 {
277  m_explicitVStepSize = true;
278  if (qFuzzyCompare(m_verticalStepSize, stepSize)) {
279  return;
280  }
281  // Mimic the behavior of QQuickScrollBar when stepSize is 0
282  if (qFuzzyIsNull(stepSize)) {
283  resetVerticalStepSize();
284  return;
285  }
286  m_verticalStepSize = stepSize;
287  Q_EMIT verticalStepSizeChanged();
288 }
289 
290 void WheelHandler::resetVerticalStepSize()
291 {
292  m_explicitVStepSize = false;
293  if (qFuzzyCompare(m_verticalStepSize, m_defaultPixelStepSize)) {
294  return;
295  }
296  m_verticalStepSize = m_defaultPixelStepSize;
297  Q_EMIT verticalStepSizeChanged();
298 }
299 
301 {
302  return m_horizontalStepSize;
303 }
304 
305 void WheelHandler::setHorizontalStepSize(qreal stepSize)
306 {
307  m_explicitHStepSize = true;
308  if (qFuzzyCompare(m_horizontalStepSize, stepSize)) {
309  return;
310  }
311  // Mimic the behavior of QQuickScrollBar when stepSize is 0
312  if (qFuzzyIsNull(stepSize)) {
313  resetHorizontalStepSize();
314  return;
315  }
316  m_horizontalStepSize = stepSize;
317  Q_EMIT horizontalStepSizeChanged();
318 }
319 
320 void WheelHandler::resetHorizontalStepSize()
321 {
322  m_explicitHStepSize = false;
323  if (qFuzzyCompare(m_horizontalStepSize, m_defaultPixelStepSize)) {
324  return;
325  }
326  m_horizontalStepSize = m_defaultPixelStepSize;
327  Q_EMIT horizontalStepSizeChanged();
328 }
329 
331 {
332  return m_pageScrollModifiers;
333 }
334 
335 void WheelHandler::setPageScrollModifiers(Qt::KeyboardModifiers modifiers)
336 {
337  if (m_pageScrollModifiers == modifiers) {
338  return;
339  }
340  m_pageScrollModifiers = modifiers;
341  Q_EMIT pageScrollModifiersChanged();
342 }
343 
344 void WheelHandler::resetPageScrollModifiers()
345 {
346  setPageScrollModifiers(m_defaultPageScrollModifiers);
347 }
348 
350 {
351  return m_filterMouseEvents;
352 }
353 
354 void WheelHandler::setFilterMouseEvents(bool enabled)
355 {
356  if (m_filterMouseEvents == enabled) {
357  return;
358  }
359  m_filterMouseEvents = enabled;
360  Q_EMIT filterMouseEventsChanged();
361 }
362 
364 {
365  return m_keyNavigationEnabled;
366 }
367 
368 void WheelHandler::setKeyNavigationEnabled(bool enabled)
369 {
370  if (m_keyNavigationEnabled == enabled) {
371  return;
372  }
373  m_keyNavigationEnabled = enabled;
374  Q_EMIT keyNavigationEnabledChanged();
375 }
376 
377 void WheelHandler::setScrolling(bool scrolling)
378 {
379  if (m_wheelScrolling == scrolling) {
380  if (m_wheelScrolling) {
381  m_wheelScrollingTimer.start();
382  }
383  return;
384  }
385  m_wheelScrolling = scrolling;
386  m_filterItem->setEnabled(m_wheelScrolling);
387 }
388 
389 bool WheelHandler::scrollFlickable(QPointF pixelDelta, QPointF angleDelta, Qt::KeyboardModifiers modifiers)
390 {
391  if (!m_flickable || (pixelDelta.isNull() && angleDelta.isNull())) {
392  return false;
393  }
394 
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;
409  const auto window = m_flickable->window();
410  const qreal devicePixelRatio = window != nullptr ? window->devicePixelRatio() : qGuiApp->devicePixelRatio();
411 
412  // HACK: Only transpose deltas when not using xcb in order to not conflict with xcb's own delta transposing
413  if (modifiers & m_defaultHorizontalScrollModifiers && qGuiApp->platformName() != QLatin1String("xcb")) {
414  angleDelta = angleDelta.transposed();
415  pixelDelta = pixelDelta.transposed();
416  }
417 
418  const qreal xTicks = angleDelta.x() / 120;
419  const qreal yTicks = angleDelta.y() / 120;
420  qreal xChange;
421  qreal yChange;
422  bool scrolled = false;
423 
424  // Scroll X
425  if (contentWidth > pageWidth) {
426  // Use page size with pageScrollModifiers. Matches QScrollBar, which uses QAbstractSlider behavior.
427  if (modifiers & m_pageScrollModifiers) {
428  xChange = qBound(-pageWidth, xTicks * pageWidth, pageWidth);
429  } else if (pixelDelta.x() != 0) {
430  xChange = pixelDelta.x();
431  } else {
432  xChange = xTicks * m_horizontalStepSize;
433  }
434 
435  // contentX and contentY use reversed signs from what x and y would normally use, so flip the signs
436 
437  qreal minXExtent = leftMargin - originX;
438  qreal maxXExtent = width - (contentWidth + rightMargin + originX);
439 
440  qreal newContentX = qBound(-minXExtent, contentX - xChange, -maxXExtent);
441  // Flickable::pixelAligned rounds the position, so round to mimic that behavior.
442  // Rounding prevents fractional positioning from causing text to be
443  // clipped off on the top and bottom.
444  // Multiply by devicePixelRatio before rounding and divide by devicePixelRatio
445  // after to make position match pixels on the screen more closely.
446  newContentX = std::round(newContentX * devicePixelRatio) / devicePixelRatio;
447  if (contentX != newContentX) {
448  scrolled = true;
449  m_flickable->setProperty("contentX", newContentX);
450  }
451  }
452 
453  // Scroll Y
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();
459  } else {
460  yChange = yTicks * m_verticalStepSize;
461  }
462 
463  // contentX and contentY use reversed signs from what x and y would normally use, so flip the signs
464 
465  qreal minYExtent = topMargin - originY;
466  qreal maxYExtent = height - (contentHeight + bottomMargin + originY);
467 
468  qreal newContentY = qBound(-minYExtent, contentY - yChange, -maxYExtent);
469  // Flickable::pixelAligned rounds the position, so round to mimic that behavior.
470  // Rounding prevents fractional positioning from causing text to be
471  // clipped off on the top and bottom.
472  // Multiply by devicePixelRatio before rounding and divide by devicePixelRatio
473  // after to make position match pixels on the screen more closely.
474  newContentY = std::round(newContentY * devicePixelRatio) / devicePixelRatio;
475  if (contentY != newContentY) {
476  scrolled = true;
477  m_flickable->setProperty("contentY", newContentY);
478  }
479  }
480 
481  return scrolled;
482 }
483 
484 bool WheelHandler::scrollUp(qreal stepSize)
485 {
486  if (qFuzzyIsNull(stepSize)) {
487  return false;
488  } else if (stepSize < 0) {
489  stepSize = m_verticalStepSize;
490  }
491  // contentY uses reversed sign
492  return scrollFlickable(QPointF(0, stepSize));
493 }
494 
495 bool WheelHandler::scrollDown(qreal stepSize)
496 {
497  if (qFuzzyIsNull(stepSize)) {
498  return false;
499  } else if (stepSize < 0) {
500  stepSize = m_verticalStepSize;
501  }
502  // contentY uses reversed sign
503  return scrollFlickable(QPointF(0, -stepSize));
504 }
505 
506 bool WheelHandler::scrollLeft(qreal stepSize)
507 {
508  if (qFuzzyIsNull(stepSize)) {
509  return false;
510  } else if (stepSize < 0) {
511  stepSize = m_horizontalStepSize;
512  }
513  // contentX uses reversed sign
514  return scrollFlickable(QPoint(stepSize, 0));
515 }
516 
517 bool WheelHandler::scrollRight(qreal stepSize)
518 {
519  if (qFuzzyIsNull(stepSize)) {
520  return false;
521  } else if (stepSize < 0) {
522  stepSize = m_horizontalStepSize;
523  }
524  // contentX uses reversed sign
525  return scrollFlickable(QPoint(-stepSize, 0));
526 }
527 
528 bool WheelHandler::eventFilter(QObject *watched, QEvent *event)
529 {
530  auto item = qobject_cast<QQuickItem*>(watched);
531  if (!item || !item->isEnabled()) {
532  return false;
533  }
534 
535  qreal contentWidth = 0;
536  qreal contentHeight = 0;
537  qreal pageWidth = 0;
538  qreal pageHeight = 0;
539  if (m_flickable) {
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();
544  }
545 
546  // The code handling touch, mouse and hover events is mostly copied/adapted from QQuickScrollView::childMouseEventFilter()
547  switch (event->type()) {
548  case QEvent::Wheel: {
549  // QQuickScrollBar::interactive handling Matches behavior in QQuickScrollView::eventFilter()
550  if (m_filterMouseEvents) {
551  if (m_verticalScrollBar) {
552  m_verticalScrollBar->setProperty("interactive", true);
553  }
554  if (m_horizontalScrollBar) {
555  m_horizontalScrollBar->setProperty("interactive", true);
556  }
557  }
558  QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event);
559 
560  // NOTE: On X11 with libinput, pixelDelta is identical to angleDelta when using a mouse that shouldn't use pixelDelta.
561  // If faulty pixelDelta, reset pixelDelta to (0,0).
562  if (wheelEvent->pixelDelta() == wheelEvent->angleDelta()) {
563  // In order to change any of the data, we have to create a whole new QWheelEvent from its constructor.
564  QWheelEvent newWheelEvent(
565  wheelEvent->position(),
566  wheelEvent->globalPosition(),
567  QPoint(0,0), // pixelDelta
568  wheelEvent->angleDelta(),
569  wheelEvent->buttons(),
570  wheelEvent->modifiers(),
571  wheelEvent->phase(),
572  wheelEvent->inverted(),
573  wheelEvent->source()
574  );
575  m_kirigamiWheelEvent.initializeFromEvent(&newWheelEvent);
576  } else {
577  m_kirigamiWheelEvent.initializeFromEvent(wheelEvent);
578  }
579 
580  Q_EMIT wheel(&m_kirigamiWheelEvent);
581 
582  if (m_kirigamiWheelEvent.isAccepted()) {
583  return true;
584  }
585 
586  bool scrolled = false;
587  if (m_scrollFlickableTarget || (contentHeight <= pageHeight && contentWidth <= pageWidth)) {
588  // Don't use pixelDelta from the event unless angleDelta is not available
589  // because scrolling by pixelDelta is too slow on Wayland with libinput.
590  QPointF pixelDelta = m_kirigamiWheelEvent.angleDelta().isNull() ? m_kirigamiWheelEvent.pixelDelta() : QPoint(0, 0);
591  scrolled = scrollFlickable(pixelDelta,
592  m_kirigamiWheelEvent.angleDelta(),
593  Qt::KeyboardModifiers(m_kirigamiWheelEvent.modifiers()));
594  }
595  setScrolling(scrolled);
596 
597  // NOTE: Wheel events created by touchpad gestures with pixel deltas will cause scrolling to jump back
598  // to where scrolling started unless the event is always accepted before it reaches the Flickable.
599  bool flickableWillUseGestureScrolling = !(wheelEvent->source() == Qt::MouseEventNotSynthesized || wheelEvent->pixelDelta().isNull());
600  return scrolled || m_blockTargetWheel || flickableWillUseGestureScrolling;
601  }
602 
603  case QEvent::TouchBegin: {
604  m_wasTouched = true;
605  if (!m_filterMouseEvents) {
606  break;
607  }
608  if (m_verticalScrollBar) {
609  m_verticalScrollBar->setProperty("interactive", false);
610  }
611  if (m_horizontalScrollBar) {
612  m_horizontalScrollBar->setProperty("interactive", false);
613  }
614  break;
615  }
616 
617  case QEvent::TouchEnd: {
618  m_wasTouched = false;
619  break;
620  }
621 
623  // NOTE: Flickable does not handle touch events, only synthesized mouse events
624  m_wasTouched = static_cast<QMouseEvent *>(event)->source() != Qt::MouseEventNotSynthesized;
625  if (!m_filterMouseEvents) {
626  break;
627  }
628  if (!m_wasTouched) {
629  if (m_verticalScrollBar) {
630  m_verticalScrollBar->setProperty("interactive", true);
631  }
632  if (m_horizontalScrollBar) {
633  m_horizontalScrollBar->setProperty("interactive", true);
634  }
635  break;
636  }
637  return !m_wasTouched && item == m_flickable;
638  }
639 
640  case QEvent::MouseMove:
642  setScrolling(false);
643  if (!m_filterMouseEvents) {
644  break;
645  }
646  if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized && item == m_flickable) {
647  return true;
648  }
649  break;
650  }
651 
652  case QEvent::HoverEnter:
653  case QEvent::HoverMove: {
654  if (!m_filterMouseEvents) {
655  break;
656  }
657  if (m_wasTouched && (item == m_verticalScrollBar || item == m_horizontalScrollBar)) {
658  if (m_verticalScrollBar) {
659  m_verticalScrollBar->setProperty("interactive", true);
660  }
661  if (m_horizontalScrollBar) {
662  m_horizontalScrollBar->setProperty("interactive", true);
663  }
664  }
665  break;
666  }
667 
668  case QEvent::KeyPress: {
669  if (!m_keyNavigationEnabled) {
670  break;
671  }
672  QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
673  bool horizontalScroll = keyEvent->modifiers() & m_defaultHorizontalScrollModifiers;
674  switch (keyEvent->key()) {
675  case Qt::Key_Up: return scrollUp();
676  case Qt::Key_Down: return scrollDown();
677  case Qt::Key_Left: return scrollLeft();
678  case Qt::Key_Right: return scrollRight();
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);
683  default: break;
684  }
685  break;
686  }
687 
688  default: break;
689  }
690 
691  return false;
692 }
int devicePixelRatio() const const
QWidget * window() const const
bool isNull() const const
Qt::ScrollPhase phase() const const
QPointF angleDelta
angleDelta: point
Definition: wheelhandler.h:47
Q_EMITQ_EMIT
int modifiers
modifiers: int
Definition: wheelhandler.h:73
T value() const const
qreal verticalStepSize
This property holds the vertical step size.
Definition: wheelhandler.h:187
QPoint angleDelta() const const
bool inherits(const char *className) const const
qreal y
y: real
Definition: wheelhandler.h:37
void wheelScrollLinesChanged(int scrollLines)
QPointF transposed() const const
qreal horizontalStepSize
This property holds the horizontal step size.
Definition: wheelhandler.h:200
int buttons
buttons: int
Definition: wheelhandler.h:62
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool inverted
inverted: bool
Definition: wheelhandler.h:81
QPoint pixelDelta() const const
Q_INVOKABLE bool scrollLeft(qreal stepSize=-1)
Scroll left one step.
void parentChanged(QQuickItem *)
void installEventFilter(QObject *filterObj)
Qt::MouseButtons buttons() const const
QPointF globalPosition() const const
QPointF pixelDelta
pixelDelta: point
Definition: wheelhandler.h:54
UniqueConnection
QStyleHints * styleHints()
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
bool inverted() const const
Q_INVOKABLE bool scrollRight(qreal stepSize=-1)
Scroll right one step.
bool keyNavigationEnabled
This property holds whether the WheelHandler handles keyboard scrolling.
Definition: wheelhandler.h:247
bool filterMouseEvents
This property holds whether the WheelHandler filters mouse events like a Qt Quick Controls ScrollView...
Definition: wheelhandler.h:227
Qt::KeyboardModifiers modifiers() const const
Qt::MouseEventSource source() const const
Q_INVOKABLE bool scrollDown(qreal stepSize=-1)
Scroll down one step.
qreal x() const const
qreal y() const const
Q_INVOKABLE bool scrollUp(qreal stepSize=-1)
Scroll up one step.
QQmlInfo qmlWarning(const QObject *object)
void keyEvent(QTest::KeyAction action, QWindow *window, char ascii, Qt::KeyboardModifiers modifier, int delay)
Qt::KeyboardModifiers pageScrollModifiers
This property holds the keyboard modifiers that will be used to start page scrolling.
Definition: wheelhandler.h:211
QPointF position() const const
KJOBWIDGETS_EXPORT QWidget * window(KJob *job)
MouseEventNotSynthesized
qreal x
x: real
Definition: wheelhandler.h:30
typedef KeyboardModifiers
QQuickItem target
This property holds the Qt Quick Flickable that the WheelHandler will control.
Definition: wheelhandler.h:174
bool isNull() const const
const QObjectList & children() const const
QVariant property(const char *name) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Jan 29 2023 04:11:03 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.