Kirigami2

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

KDE's Doxygen guidelines are available online.