Welcome to mirror list, hosted at ThFree Co, Russian Federation.

wheelhandler.h « kirigami « 3rdparty « src - github.com/nextcloud/desktop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: d921a6c9d7c24f048ee1fffe2c716cce6b3aa02c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
 * SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>
 * SPDX-License-Identifier: LGPL-2.0-or-later
 */

#pragma once

#include <QGuiApplication>
#include <QObject>
#include <QPoint>
#include <QQuickItem>
#include <QStyleHints>
#include <QtQml>

class QWheelEvent;
class WheelHandler;

/**
 * Describes the mouse wheel event
 */
class KirigamiWheelEvent : public QObject
{
    Q_OBJECT

    /**
     * x: real
     *
     * X coordinate of the mouse pointer
     */
    Q_PROPERTY(qreal x READ x CONSTANT)

    /**
     * y: real
     *
     * Y coordinate of the mouse pointer
     */
    Q_PROPERTY(qreal y READ y CONSTANT)

    /**
     * angleDelta: point
     *
     * The distance the wheel is rotated in degrees.
     * The x and y coordinates indicate the horizontal and vertical wheels respectively.
     * A positive value indicates it was rotated up/right, negative, bottom/left
     * This value is more likely to be set in traditional mice.
     */
    Q_PROPERTY(QPointF angleDelta READ angleDelta CONSTANT)

    /**
     * pixelDelta: point
     *
     * provides the delta in screen pixels available on high resolution trackpads
     */
    Q_PROPERTY(QPointF pixelDelta READ pixelDelta CONSTANT)

    /**
     * buttons: int
     *
     * it contains an OR combination of the buttons that were pressed during the wheel, they can be:
     * Qt.LeftButton, Qt.MiddleButton, Qt.RightButton
     */
    Q_PROPERTY(int buttons READ buttons CONSTANT)

    /**
     * modifiers: int
     *
     * Keyboard mobifiers that were pressed during the wheel event, such as:
     * Qt.NoModifier (default, no modifiers)
     * Qt.ControlModifier
     * Qt.ShiftModifier
     * ...
     */
    Q_PROPERTY(int modifiers READ modifiers CONSTANT)

    /**
     * inverted: bool
     *
     * Whether the delta values are inverted
     * On some platformsthe returned delta are inverted, so positive values would mean bottom/left
     */
    Q_PROPERTY(bool inverted READ inverted CONSTANT)

    /**
     * accepted: bool
     *
     * If set, the event shouldn't be managed anymore,
     * for instance it can be used to block the handler to manage the scroll of a view on some scenarios
     * @code
     * // This handler handles automatically the scroll of
     * // flickableItem, unless Ctrl is pressed, in this case the
     * // app has custom code to handle Ctrl+wheel zooming
     * Kirigami.WheelHandler {
     *   target: flickableItem
     *   blockTargetWheel: true
     *   scrollFlickableTarget: true
     *   onWheel: {
     *        if (wheel.modifiers & Qt.ControlModifier) {
     *            wheel.accepted = true;
     *            // Handle scaling of the view
     *       }
     *   }
     * }
     * @endcode
     *
     */
    Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)

public:
    KirigamiWheelEvent(QObject *parent = nullptr);
    ~KirigamiWheelEvent() override;

    void initializeFromEvent(QWheelEvent *event);

    qreal x() const;
    qreal y() const;
    QPointF angleDelta() const;
    QPointF pixelDelta() const;
    int buttons() const;
    int modifiers() const;
    bool inverted() const;
    bool isAccepted();
    void setAccepted(bool accepted);

private:
    qreal m_x = 0;
    qreal m_y = 0;
    QPointF m_angleDelta;
    QPointF m_pixelDelta;
    Qt::MouseButtons m_buttons = Qt::NoButton;
    Qt::KeyboardModifiers m_modifiers = Qt::NoModifier;
    bool m_inverted = false;
    bool m_accepted = false;
};

class WheelFilterItem : public QQuickItem
{
    Q_OBJECT
public:
    WheelFilterItem(QQuickItem *parent = nullptr);
};

/**
 * @brief Handles scrolling for a Flickable and 2 attached ScrollBars.
 *
 * WheelHandler filters events from a Flickable, a vertical ScrollBar and a horizontal ScrollBar.
 * Wheel and KeyPress events (when `keyNavigationEnabled` is true) are used to scroll the Flickable.
 * When `filterMouseEvents` is true, WheelHandler blocks mouse button input from reaching the Flickable
 * and sets the `interactive` property of the scrollbars to false when touch input is used.
 *
 * Wheel event handling behavior:
 *
 * - 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.
 * - When using angle delta, scroll using the step increments defined by `verticalStepSize` and `horizontalStepSize`.
 * - When one of the keyboard modifiers in `pageScrollModifiers` is used, scroll by pages.
 * - 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.
 * - 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.
 *
 * Common usage with a Flickable:
 *
 * @include wheelhandler/FlickableUsage.qml
 *
 * Common usage inside of a ScrollView template:
 *
 * @include wheelhandler/ScrollViewUsage.qml
 *
 */
class WheelHandler : public QObject
{
    Q_OBJECT

    /**
     * @brief This property holds the Qt Quick Flickable that the WheelHandler will control.
     */
    Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged FINAL)

    /**
     * @brief This property holds the vertical step size.
     *
     * The default value is equivalent to `20 * Qt.styleHints.wheelScrollLines`. This is consistent with the default increment for QScrollArea.
     *
     * @sa horizontalStepSize
     *
     * @since KDE Frameworks 5.89
     */
    Q_PROPERTY(qreal verticalStepSize READ verticalStepSize
               WRITE setVerticalStepSize RESET resetVerticalStepSize
               NOTIFY verticalStepSizeChanged FINAL)

    /**
     * @brief This property holds the horizontal step size.
     *
     * The default value is equivalent to `20 * Qt.styleHints.wheelScrollLines`. This is consistent with the default increment for QScrollArea.
     *
     * @sa verticalStepSize
     *
     * @since KDE Frameworks 5.89
     */
    Q_PROPERTY(qreal horizontalStepSize READ horizontalStepSize
               WRITE setHorizontalStepSize RESET resetHorizontalStepSize
               NOTIFY horizontalStepSizeChanged FINAL)

    /**
     * @brief This property holds the keyboard modifiers that will be used to start page scrolling.
     *
     * The default value is equivalent to `Qt.ControlModifier | Qt.ShiftModifier`. This matches QScrollBar, which uses QAbstractSlider behavior.
     *
     * @since KDE Frameworks 5.89
     */
    Q_PROPERTY(Qt::KeyboardModifiers pageScrollModifiers READ pageScrollModifiers
               WRITE setPageScrollModifiers RESET resetPageScrollModifiers
               NOTIFY pageScrollModifiersChanged FINAL)

    /**
     * @brief This property holds whether the WheelHandler filters mouse events like a Qt Quick Controls ScrollView would.
     *
     * Touch events are allowed to flick the view and they make the scrollbars not interactive.
     *
     * Mouse events are not allowed to flick the view and they make the scrollbars interactive.
     *
     * Hover events on the scrollbars and wheel events on anything also make the scrollbars interactive when this property is set to true.
     *
     * The default value is `false`.
     *
     * @since KDE Frameworks 5.89
     */
    Q_PROPERTY(bool filterMouseEvents READ filterMouseEvents
               WRITE setFilterMouseEvents NOTIFY filterMouseEventsChanged FINAL)

    /**
     * @brief This property holds whether the WheelHandler handles keyboard scrolling.
     *
     * - Left arrow scrolls a step to the left.
     * - Right arrow scrolls a step to the right.
     * - Up arrow scrolls a step upwards.
     * - Down arrow scrolls a step downwards.
     * - PageUp scrolls to the previous page.
     * - PageDown scrolls to the next page.
     * - Home scrolls to the beginning.
     * - End scrolls to the end.
     * - When Alt is held, scroll horizontally when using PageUp, PageDown, Home or End.
     *
     * The default value is `false`.
     *
     * @since KDE Frameworks 5.89
     */
    Q_PROPERTY(bool keyNavigationEnabled READ keyNavigationEnabled
               WRITE setKeyNavigationEnabled NOTIFY keyNavigationEnabledChanged FINAL)

    /**
     * @brief This property holds whether the WheelHandler blocks all wheel events from reaching the Flickable.
     *
     * 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.
     *
     * 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.
     *
     * The default value is true.
     */
    Q_PROPERTY(bool blockTargetWheel MEMBER m_blockTargetWheel NOTIFY blockTargetWheelChanged)

    /**
     * @brief This property holds whether the WheelHandler can use wheel events to scroll the Flickable.
     *
     * The default value is true.
     */
    Q_PROPERTY(bool scrollFlickableTarget MEMBER m_scrollFlickableTarget NOTIFY scrollFlickableTargetChanged)

public:
    explicit WheelHandler(QObject *parent = nullptr);
    ~WheelHandler() override;

    QQuickItem *target() const;
    void setTarget(QQuickItem *target);

    qreal verticalStepSize() const;
    void setVerticalStepSize(qreal stepSize);
    void resetVerticalStepSize();

    qreal horizontalStepSize() const;
    void setHorizontalStepSize(qreal stepSize);
    void resetHorizontalStepSize();

    Qt::KeyboardModifiers pageScrollModifiers() const;
    void setPageScrollModifiers(Qt::KeyboardModifiers modifiers);
    void resetPageScrollModifiers();

    bool filterMouseEvents() const;
    void setFilterMouseEvents(bool enabled);

    bool keyNavigationEnabled() const;
    void setKeyNavigationEnabled(bool enabled);

    /**
     * Scroll up one step. If the stepSize parameter is less than 0, the verticalStepSize will be used.
     *
     * returns true if the contentItem was moved.
     *
     * @since KDE Frameworks 5.89
     */
    Q_INVOKABLE bool scrollUp(qreal stepSize = -1);

    /**
     * Scroll down one step. If the stepSize parameter is less than 0, the verticalStepSize will be used.
     *
     * returns true if the contentItem was moved.
     *
     * @since KDE Frameworks 5.89
     */
    Q_INVOKABLE bool scrollDown(qreal stepSize = -1);

    /**
     * Scroll left one step. If the stepSize parameter is less than 0, the horizontalStepSize will be used.
     *
     * returns true if the contentItem was moved.
     *
     * @since KDE Frameworks 5.89
     */
    Q_INVOKABLE bool scrollLeft(qreal stepSize = -1);

    /**
     * Scroll right one step. If the stepSize parameter is less than 0, the horizontalStepSize will be used.
     *
     * returns true if the contentItem was moved.
     *
     * @since KDE Frameworks 5.89
     */
    Q_INVOKABLE bool scrollRight(qreal stepSize = -1);

Q_SIGNALS:
    void targetChanged();
    void verticalStepSizeChanged();
    void horizontalStepSizeChanged();
    void pageScrollModifiersChanged();
    void filterMouseEventsChanged();
    void keyNavigationEnabledChanged();
    void blockTargetWheelChanged();
    void scrollFlickableTargetChanged();

    /**
     * @brief This signal is emitted when a wheel event reaches the event filter, just before scrolling is handled.
     *
     * Accepting the wheel event in the `onWheel` signal handler prevents scrolling from happening.
     */
    void wheel(KirigamiWheelEvent *wheel);

protected:
    bool eventFilter(QObject *watched, QEvent *event) override;

private:
    void setScrolling(bool scrolling);
    bool scrollFlickable(QPointF pixelDelta,
                         QPointF angleDelta = {},
                         Qt::KeyboardModifiers modifiers = Qt::NoModifier);

    QPointer<QQuickItem> m_flickable;
    QPointer<QQuickItem> m_verticalScrollBar;
    QPointer<QQuickItem> m_horizontalScrollBar;
    QPointer<QQuickItem> m_filterItem;
    // Matches QScrollArea and QTextEdit
    qreal m_defaultPixelStepSize = 20 * QGuiApplication::styleHints()->wheelScrollLines();
    qreal m_verticalStepSize = m_defaultPixelStepSize;
    qreal m_horizontalStepSize = m_defaultPixelStepSize;
    bool m_explicitVStepSize = false;
    bool m_explicitHStepSize = false;
    bool m_wheelScrolling = false;
    constexpr static qreal m_wheelScrollingDuration = 400;
    bool m_filterMouseEvents = false;
    bool m_keyNavigationEnabled = false;
    bool m_wasTouched = false;
    bool m_blockTargetWheel = true;
    bool m_scrollFlickableTarget = true;
    // Same as QXcbWindow.
    constexpr static Qt::KeyboardModifiers m_defaultHorizontalScrollModifiers = Qt::AltModifier;
    // Same as QScrollBar/QAbstractSlider.
    constexpr static Qt::KeyboardModifiers m_defaultPageScrollModifiers = Qt::ControlModifier | Qt::ShiftModifier;
    Qt::KeyboardModifiers m_pageScrollModifiers = m_defaultPageScrollModifiers;
    QTimer m_wheelScrollingTimer;
    KirigamiWheelEvent m_kirigamiWheelEvent;
};