MauiKit Controls

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

KDE's Doxygen guidelines are available online.