Kirigami2

wheelhandler.h
1 /* SPDX-FileCopyrightText: 2019 Marco Martin <[email protected]>
2  * SPDX-FileCopyrightText: 2021 Noah Davis <[email protected]>
3  * SPDX-License-Identifier: LGPL-2.0-or-later
4  */
5 
6 #pragma once
7 
8 #include <QGuiApplication>
9 #include <QObject>
10 #include <QPoint>
11 #include <QQuickItem>
12 #include <QStyleHints>
13 #include <QtQml>
14 
15 class QWheelEvent;
16 class WheelHandler;
17 
18 /**
19  * Describes the mouse wheel event
20  */
22 {
23  Q_OBJECT
24 
25  /**
26  * x: real
27  *
28  * X coordinate of the mouse pointer
29  */
30  Q_PROPERTY(qreal x READ x CONSTANT)
31 
32  /**
33  * y: real
34  *
35  * Y coordinate of the mouse pointer
36  */
37  Q_PROPERTY(qreal y READ y CONSTANT)
38 
39  /**
40  * angleDelta: point
41  *
42  * The distance the wheel is rotated in degrees.
43  * The x and y coordinates indicate the horizontal and vertical wheels respectively.
44  * A positive value indicates it was rotated up/right, negative, bottom/left
45  * This value is more likely to be set in traditional mice.
46  */
48 
49  /**
50  * pixelDelta: point
51  *
52  * provides the delta in screen pixels available on high resolution trackpads
53  */
55 
56  /**
57  * buttons: int
58  *
59  * it contains an OR combination of the buttons that were pressed during the wheel, they can be:
60  * Qt.LeftButton, Qt.MiddleButton, Qt.RightButton
61  */
62  Q_PROPERTY(int buttons READ buttons CONSTANT)
63 
64  /**
65  * modifiers: int
66  *
67  * Keyboard mobifiers that were pressed during the wheel event, such as:
68  * Qt.NoModifier (default, no modifiers)
69  * Qt.ControlModifier
70  * Qt.ShiftModifier
71  * ...
72  */
73  Q_PROPERTY(int modifiers READ modifiers CONSTANT)
74 
75  /**
76  * inverted: bool
77  *
78  * Whether the delta values are inverted
79  * On some platformsthe returned delta are inverted, so positive values would mean bottom/left
80  */
81  Q_PROPERTY(bool inverted READ inverted CONSTANT)
82 
83  /**
84  * accepted: bool
85  *
86  * If set, the event shouldn't be managed anymore,
87  * for instance it can be used to block the handler to manage the scroll of a view on some scenarios
88  * @code
89  * // This handler handles automatically the scroll of
90  * // flickableItem, unless Ctrl is pressed, in this case the
91  * // app has custom code to handle Ctrl+wheel zooming
92  * Kirigami.WheelHandler {
93  * target: flickableItem
94  * blockTargetWheel: true
95  * scrollFlickableTarget: true
96  * onWheel: {
97  * if (wheel.modifiers & Qt.ControlModifier) {
98  * wheel.accepted = true;
99  * // Handle scaling of the view
100  * }
101  * }
102  * }
103  * @endcode
104  *
105  */
106  Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
107 
108 public:
109  KirigamiWheelEvent(QObject *parent = nullptr);
110  ~KirigamiWheelEvent() override;
111 
112  void initializeFromEvent(QWheelEvent *event);
113 
114  qreal x() const;
115  qreal y() const;
116  QPointF angleDelta() const;
117  QPointF pixelDelta() const;
118  int buttons() const;
119  int modifiers() const;
120  bool inverted() const;
121  bool isAccepted();
122  void setAccepted(bool accepted);
123 
124 private:
125  qreal m_x = 0;
126  qreal m_y = 0;
127  QPointF m_angleDelta;
128  QPointF m_pixelDelta;
129  Qt::MouseButtons m_buttons = Qt::NoButton;
130  Qt::KeyboardModifiers m_modifiers = Qt::NoModifier;
131  bool m_inverted = false;
132  bool m_accepted = false;
133 };
134 
135 class WheelFilterItem : public QQuickItem
136 {
137  Q_OBJECT
138 public:
139  WheelFilterItem(QQuickItem *parent = nullptr);
140 };
141 
142 /**
143  * @brief Handles scrolling for a Flickable and 2 attached ScrollBars.
144  *
145  * WheelHandler filters events from a Flickable, a vertical ScrollBar and a horizontal ScrollBar.
146  * Wheel and KeyPress events (when `keyNavigationEnabled` is true) are used to scroll the Flickable.
147  * When `filterMouseEvents` is true, WheelHandler blocks mouse button input from reaching the Flickable
148  * and sets the `interactive` property of the scrollbars to false when touch input is used.
149  *
150  * Wheel event handling behavior:
151  *
152  * - Pixel delta is ignored unless angle delta is not available because pixel delta scrolling is too slow. Qt Widgets doesn't use pixel delta either, so the default scroll speed should be consistent with Qt Widgets.
153  * - When using angle delta, scroll using the step increments defined by `verticalStepSize` and `horizontalStepSize`.
154  * - When one of the keyboard modifiers in `pageScrollModifiers` is used, scroll by pages.
155  * - When using a device that doesn't use 120 angle delta unit increments such as a touchpad, the `verticalStepSize`, `horizontalStepSize` and page increments (if using page scrolling) will be multiplied by `angle delta / 120` to keep scrolling smooth.
156  * - If scrolling has happened in the last 400ms, use an internal QQuickItem stacked over the Flickable's contentItem to catch wheel events and use those wheel events to scroll, if possible. This prevents controls inside the Flickable's contentItem that allow scrolling to change the value (e.g., Sliders, SpinBoxes) from conflicting with scrolling the page.
157  *
158  * Common usage with a Flickable:
159  *
160  * @include wheelhandler/FlickableUsage.qml
161  *
162  * Common usage inside of a ScrollView template:
163  *
164  * @include wheelhandler/ScrollViewUsage.qml
165  *
166  */
167 class WheelHandler : public QObject
168 {
169  Q_OBJECT
170 
171  /**
172  * @brief This property holds the Qt Quick Flickable that the WheelHandler will control.
173  */
174  Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged FINAL)
175 
176  /**
177  * @brief This property holds the vertical step size.
178  *
179  * The default value is equivalent to `20 * Qt.styleHints.wheelScrollLines`. This is consistent with the default increment for QScrollArea.
180  *
181  * @sa horizontalStepSize
182  *
183  * @since KDE Frameworks 5.89
184  */
185  Q_PROPERTY(qreal verticalStepSize READ verticalStepSize
186  WRITE setVerticalStepSize RESET resetVerticalStepSize
187  NOTIFY verticalStepSizeChanged FINAL)
188 
189  /**
190  * @brief This property holds the horizontal step size.
191  *
192  * The default value is equivalent to `20 * Qt.styleHints.wheelScrollLines`. This is consistent with the default increment for QScrollArea.
193  *
194  * @sa verticalStepSize
195  *
196  * @since KDE Frameworks 5.89
197  */
198  Q_PROPERTY(qreal horizontalStepSize READ horizontalStepSize
199  WRITE setHorizontalStepSize RESET resetHorizontalStepSize
200  NOTIFY horizontalStepSizeChanged FINAL)
201 
202  /**
203  * @brief This property holds the keyboard modifiers that will be used to start page scrolling.
204  *
205  * The default value is equivalent to `Qt.ControlModifier | Qt.ShiftModifier`. This matches QScrollBar, which uses QAbstractSlider behavior.
206  *
207  * @since KDE Frameworks 5.89
208  */
209  Q_PROPERTY(Qt::KeyboardModifiers pageScrollModifiers READ pageScrollModifiers
210  WRITE setPageScrollModifiers RESET resetPageScrollModifiers
211  NOTIFY pageScrollModifiersChanged FINAL)
212 
213  /**
214  * @brief This property holds whether the WheelHandler filters mouse events like a Qt Quick Controls ScrollView would.
215  *
216  * Touch events are allowed to flick the view and they make the scrollbars not interactive.
217  *
218  * Mouse events are not allowed to flick the view and they make the scrollbars interactive.
219  *
220  * Hover events on the scrollbars and wheel events on anything also make the scrollbars interactive when this property is set to true.
221  *
222  * The default value is `false`.
223  *
224  * @since KDE Frameworks 5.89
225  */
226  Q_PROPERTY(bool filterMouseEvents READ filterMouseEvents
227  WRITE setFilterMouseEvents NOTIFY filterMouseEventsChanged FINAL)
228 
229  /**
230  * @brief This property holds whether the WheelHandler handles keyboard scrolling.
231  *
232  * - Left arrow scrolls a step to the left.
233  * - Right arrow scrolls a step to the right.
234  * - Up arrow scrolls a step upwards.
235  * - Down arrow scrolls a step downwards.
236  * - PageUp scrolls to the previous page.
237  * - PageDown scrolls to the next page.
238  * - Home scrolls to the beginning.
239  * - End scrolls to the end.
240  * - When Alt is held, scroll horizontally when using PageUp, PageDown, Home or End.
241  *
242  * The default value is `false`.
243  *
244  * @since KDE Frameworks 5.89
245  */
246  Q_PROPERTY(bool keyNavigationEnabled READ keyNavigationEnabled
247  WRITE setKeyNavigationEnabled NOTIFY keyNavigationEnabledChanged FINAL)
248 
249  /**
250  * @brief This property holds whether the WheelHandler blocks all wheel events from reaching the Flickable.
251  *
252  * When this property is false, scrolling the Flickable with WheelHandler will only block an event from reaching the Flickable if the Flickable is actually scrolled by WheelHandler.
253  *
254  * NOTE: Wheel events created by touchpad gestures with pixel deltas will always be accepted no matter what. This is because they will cause the Flickable to jump back to where scrolling started unless the events are always accepted before they reach the Flickable.
255  *
256  * The default value is true.
257  */
258  Q_PROPERTY(bool blockTargetWheel MEMBER m_blockTargetWheel NOTIFY blockTargetWheelChanged)
259 
260  /**
261  * @brief This property holds whether the WheelHandler can use wheel events to scroll the Flickable.
262  *
263  * The default value is true.
264  */
265  Q_PROPERTY(bool scrollFlickableTarget MEMBER m_scrollFlickableTarget NOTIFY scrollFlickableTargetChanged)
266 
267 public:
268  explicit WheelHandler(QObject *parent = nullptr);
269  ~WheelHandler() override;
270 
271  QQuickItem *target() const;
272  void setTarget(QQuickItem *target);
273 
274  qreal verticalStepSize() const;
275  void setVerticalStepSize(qreal stepSize);
276  void resetVerticalStepSize();
277 
278  qreal horizontalStepSize() const;
279  void setHorizontalStepSize(qreal stepSize);
280  void resetHorizontalStepSize();
281 
282  Qt::KeyboardModifiers pageScrollModifiers() const;
283  void setPageScrollModifiers(Qt::KeyboardModifiers modifiers);
284  void resetPageScrollModifiers();
285 
286  bool filterMouseEvents() const;
287  void setFilterMouseEvents(bool enabled);
288 
289  bool keyNavigationEnabled() const;
290  void setKeyNavigationEnabled(bool enabled);
291 
292  /**
293  * Scroll up one step. If the stepSize parameter is less than 0, the verticalStepSize will be used.
294  *
295  * returns true if the contentItem was moved.
296  *
297  * @since KDE Frameworks 5.89
298  */
299  Q_INVOKABLE bool scrollUp(qreal stepSize = -1);
300 
301  /**
302  * Scroll down one step. If the stepSize parameter is less than 0, the verticalStepSize will be used.
303  *
304  * returns true if the contentItem was moved.
305  *
306  * @since KDE Frameworks 5.89
307  */
308  Q_INVOKABLE bool scrollDown(qreal stepSize = -1);
309 
310  /**
311  * Scroll left one step. If the stepSize parameter is less than 0, the horizontalStepSize will be used.
312  *
313  * returns true if the contentItem was moved.
314  *
315  * @since KDE Frameworks 5.89
316  */
317  Q_INVOKABLE bool scrollLeft(qreal stepSize = -1);
318 
319  /**
320  * Scroll right one step. If the stepSize parameter is less than 0, the horizontalStepSize will be used.
321  *
322  * returns true if the contentItem was moved.
323  *
324  * @since KDE Frameworks 5.89
325  */
326  Q_INVOKABLE bool scrollRight(qreal stepSize = -1);
327 
328 Q_SIGNALS:
329  void targetChanged();
330  void verticalStepSizeChanged();
331  void horizontalStepSizeChanged();
332  void pageScrollModifiersChanged();
333  void filterMouseEventsChanged();
334  void keyNavigationEnabledChanged();
335  void blockTargetWheelChanged();
336  void scrollFlickableTargetChanged();
337 
338  /**
339  * @brief This signal is emitted when a wheel event reaches the event filter, just before scrolling is handled.
340  *
341  * Accepting the wheel event in the `onWheel` signal handler prevents scrolling from happening.
342  */
343  void wheel(KirigamiWheelEvent *wheel);
344 
345 protected:
346  bool eventFilter(QObject *watched, QEvent *event) override;
347 
348 private Q_SLOTS:
349  void _k_rebindScrollBars();
350 
351 private:
352  void setScrolling(bool scrolling);
353  bool scrollFlickable(QPointF pixelDelta,
354  QPointF angleDelta = {},
356 
357  QPointer<QQuickItem> m_flickable;
358  QPointer<QQuickItem> m_verticalScrollBar;
359  QPointer<QQuickItem> m_horizontalScrollBar;
360  QMetaObject::Connection m_verticalChangedConnection;
361  QMetaObject::Connection m_horizontalChangedConnection;
362  QPointer<QQuickItem> m_filterItem;
363  // Matches QScrollArea and QTextEdit
364  qreal m_defaultPixelStepSize = 20 * QGuiApplication::styleHints()->wheelScrollLines();
365  qreal m_verticalStepSize = m_defaultPixelStepSize;
366  qreal m_horizontalStepSize = m_defaultPixelStepSize;
367  bool m_explicitVStepSize = false;
368  bool m_explicitHStepSize = false;
369  bool m_wheelScrolling = false;
370  constexpr static qreal m_wheelScrollingDuration = 400;
371  bool m_filterMouseEvents = false;
372  bool m_keyNavigationEnabled = false;
373  bool m_wasTouched = false;
374  bool m_blockTargetWheel = true;
375  bool m_scrollFlickableTarget = true;
376  // Same as QXcbWindow.
377  constexpr static Qt::KeyboardModifiers m_defaultHorizontalScrollModifiers = Qt::AltModifier;
378  // Same as QScrollBar/QAbstractSlider.
379  constexpr static Qt::KeyboardModifiers m_defaultPageScrollModifiers = Qt::ControlModifier | Qt::ShiftModifier;
380  Qt::KeyboardModifiers m_pageScrollModifiers = m_defaultPageScrollModifiers;
381  QTimer m_wheelScrollingTimer;
382  KirigamiWheelEvent m_kirigamiWheelEvent;
383 };
Q_OBJECTQ_OBJECT
Q_PROPERTY(...)
QPointF angleDelta
angleDelta: point
Definition: wheelhandler.h:47
bool accepted
accepted: bool
Definition: wheelhandler.h:106
Q_SLOTSQ_SLOTS
int modifiers
modifiers: int
Definition: wheelhandler.h:73
Handles scrolling for a Flickable and 2 attached ScrollBars.
Definition: wheelhandler.h:167
qreal y
y: real
Definition: wheelhandler.h:37
int buttons
buttons: int
Definition: wheelhandler.h:62
bool inverted
inverted: bool
Definition: wheelhandler.h:81
virtual bool eventFilter(QObject *watched, QEvent *event)
virtual bool event(QEvent *e)
QPointF pixelDelta
pixelDelta: point
Definition: wheelhandler.h:54
QStyleHints * styleHints()
Q_INVOKABLEQ_INVOKABLE
Q_SIGNALSQ_SIGNALS
Describes the mouse wheel event.
Definition: wheelhandler.h:21
void setTarget(const SkyPoint &targetCoord)
qreal x
x: real
Definition: wheelhandler.h:30
typedef KeyboardModifiers
QObject * parent() 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.