QmlMaterial 0.1.0
Loading...
Searching...
No Matches
flickable.hpp
1#pragma once
2
3#include <QBasicTimer>
4#include <QElapsedTimer>
5#include <QObject>
6#include <QPointer>
7#include <QQmlEngine>
8#include <QQmlListProperty>
9#include <QQuickItem>
10#include <QVector>
11
12namespace qml_material
13{
14
15class Flickable;
16
17class FlickableVisibleArea : public QObject {
18 Q_OBJECT
19 QML_ANONYMOUS
20
21 Q_PROPERTY(qreal xPosition READ xPosition NOTIFY xPositionChanged FINAL)
22 Q_PROPERTY(qreal yPosition READ yPosition NOTIFY yPositionChanged FINAL)
23 Q_PROPERTY(qreal widthRatio READ widthRatio NOTIFY widthRatioChanged FINAL)
24 Q_PROPERTY(qreal heightRatio READ heightRatio NOTIFY heightRatioChanged FINAL)
25
26public:
27 explicit FlickableVisibleArea(Flickable* parent = nullptr);
28
29 auto xPosition() const -> qreal;
30 auto yPosition() const -> qreal;
31 auto widthRatio() const -> qreal;
32 auto heightRatio() const -> qreal;
33
34 auto updateVisible() -> void;
35
36 Q_SIGNAL void xPositionChanged(qreal value);
37 Q_SIGNAL void yPositionChanged(qreal value);
38 Q_SIGNAL void widthRatioChanged(qreal value);
39 Q_SIGNAL void heightRatioChanged(qreal value);
40
41private:
42 QPointer<Flickable> m_flickable;
43 qreal m_xPosition { 0 };
44 qreal m_yPosition { 0 };
45 qreal m_widthRatio { 1 };
46 qreal m_heightRatio { 1 };
47};
48
49class Flickable : public QQuickItem {
50 Q_OBJECT
51 QML_NAMED_ELEMENT(Flickable2)
52
53 Q_PROPERTY(
54 qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged)
55 Q_PROPERTY(
56 qreal contentHeight READ contentHeight WRITE setContentHeight NOTIFY contentHeightChanged)
57 Q_PROPERTY(qreal contentX READ contentX WRITE setContentX NOTIFY contentXChanged)
58 Q_PROPERTY(qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged)
59 Q_PROPERTY(QQuickItem* contentItem READ contentItem CONSTANT)
60
61 Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged)
62 Q_PROPERTY(
63 qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged)
64 Q_PROPERTY(qreal originY READ originY NOTIFY originYChanged)
65 Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged)
66 Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged)
67 Q_PROPERTY(qreal originX READ originX NOTIFY originXChanged)
68
69 Q_PROPERTY(qreal horizontalVelocity READ horizontalVelocity NOTIFY horizontalVelocityChanged)
70 Q_PROPERTY(qreal verticalVelocity READ verticalVelocity NOTIFY verticalVelocityChanged)
71 Q_PROPERTY(qreal maximumFlickVelocity READ maximumFlickVelocity WRITE setMaximumFlickVelocity
72 NOTIFY maximumFlickVelocityChanged)
73 Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY
74 flickDecelerationChanged)
75 Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged)
76 Q_PROPERTY(bool movingHorizontally READ isMovingHorizontally NOTIFY movingHorizontallyChanged)
77 Q_PROPERTY(bool movingVertically READ isMovingVertically NOTIFY movingVerticallyChanged)
78 Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged)
79 Q_PROPERTY(
80 bool flickingHorizontally READ isFlickingHorizontally NOTIFY flickingHorizontallyChanged)
81 Q_PROPERTY(bool flickingVertically READ isFlickingVertically NOTIFY flickingVerticallyChanged)
82 Q_PROPERTY(bool dragging READ isDragging NOTIFY draggingChanged)
83 Q_PROPERTY(
84 bool draggingHorizontally READ isDraggingHorizontally NOTIFY draggingHorizontallyChanged)
85 Q_PROPERTY(bool draggingVertically READ isDraggingVertically NOTIFY draggingVerticallyChanged)
86 Q_PROPERTY(FlickableDirection flickableDirection READ flickableDirection WRITE
87 setFlickableDirection NOTIFY flickableDirectionChanged)
88
89 Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged)
90 Q_PROPERTY(int pressDelay READ pressDelay WRITE setPressDelay NOTIFY pressDelayChanged)
91 Q_PROPERTY(bool atXEnd READ isAtXEnd NOTIFY atXEndChanged)
92 Q_PROPERTY(bool atYEnd READ isAtYEnd NOTIFY atYEndChanged)
93 Q_PROPERTY(bool atXBeginning READ isAtXBeginning NOTIFY atXBeginningChanged)
94 Q_PROPERTY(bool atYBeginning READ isAtYBeginning NOTIFY atYBeginningChanged)
95 Q_PROPERTY(FlickableVisibleArea* visibleArea READ visibleArea CONSTANT)
96 Q_PROPERTY(bool pixelAligned READ pixelAligned WRITE setPixelAligned NOTIFY pixelAlignedChanged)
97 Q_PROPERTY(bool synchronousDrag READ synchronousDrag WRITE setSynchronousDrag NOTIFY
98 synchronousDragChanged)
99 Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY
100 acceptedButtonsChanged FINAL)
101 Q_PROPERTY(InputMaskMode inputMaskMode READ inputMaskMode WRITE setInputMaskMode NOTIFY
102 inputMaskModeChanged FINAL)
103 Q_PROPERTY(QQuickItem* interactionItem READ interactionItem WRITE setInteractionItem RESET
104 resetInteractionItem NOTIFY interactionItemChanged FINAL)
105 Q_PROPERTY(QQmlListProperty<QObject> flickableData READ flickableData)
106 Q_PROPERTY(QQmlListProperty<QQuickItem> flickableChildren READ flickableChildren)
107 Q_CLASSINFO("DefaultProperty", "flickableData")
108
109public:
110 explicit Flickable(QQuickItem* parent = nullptr);
111 ~Flickable() override;
112
113 auto flickableData() -> QQmlListProperty<QObject>;
114 auto flickableChildren() -> QQmlListProperty<QQuickItem>;
115
116 enum FlickableDirection
117 {
118 AutoFlickDirection = 0x0,
119 HorizontalFlick = 0x1,
120 VerticalFlick = 0x2,
121 HorizontalAndVerticalFlick = 0x3,
122 AutoFlickIfNeeded = 0xc,
123 };
124 Q_ENUM(FlickableDirection)
125
126 enum InputMaskMode
127 {
128 Viewport,
129 ContentOnly,
130 CustomItem,
131 };
132 Q_ENUM(InputMaskMode)
133
134 auto contentWidth() const -> qreal;
135 auto setContentWidth(qreal value) -> void;
136
137 auto contentHeight() const -> qreal;
138 auto setContentHeight(qreal value) -> void;
139
140 auto contentX() const -> qreal;
141 virtual auto setContentX(qreal position) -> void;
142
143 auto contentY() const -> qreal;
144 virtual auto setContentY(qreal position) -> void;
145
146 auto topMargin() const -> qreal;
147 auto setTopMargin(qreal value) -> void;
148
149 auto bottomMargin() const -> qreal;
150 auto setBottomMargin(qreal value) -> void;
151
152 auto leftMargin() const -> qreal;
153 auto setLeftMargin(qreal value) -> void;
154
155 auto rightMargin() const -> qreal;
156 auto setRightMargin(qreal value) -> void;
157
158 virtual auto originY() const -> qreal;
159 virtual auto originX() const -> qreal;
160
161 auto isMoving() const -> bool;
162 auto isMovingHorizontally() const -> bool;
163 auto isMovingVertically() const -> bool;
164 auto isFlicking() const -> bool;
165 auto isFlickingHorizontally() const -> bool;
166 auto isFlickingVertically() const -> bool;
167 auto isDragging() const -> bool;
168 auto isDraggingHorizontally() const -> bool;
169 auto isDraggingVertically() const -> bool;
170
171 auto pressDelay() const -> int;
172 auto setPressDelay(int delay) -> void;
173
174 auto maximumFlickVelocity() const -> qreal;
175 auto setMaximumFlickVelocity(qreal value) -> void;
176
177 auto flickDeceleration() const -> qreal;
178 auto setFlickDeceleration(qreal value) -> void;
179
180 auto isInteractive() const -> bool;
181 auto setInteractive(bool value) -> void;
182
183 auto horizontalVelocity() const -> qreal;
184 auto verticalVelocity() const -> qreal;
185
186 auto isAtXEnd() const -> bool;
187 auto isAtXBeginning() const -> bool;
188 auto isAtYEnd() const -> bool;
189 auto isAtYBeginning() const -> bool;
190
191 auto contentItem() const -> QQuickItem*;
192
193 auto flickableDirection() const -> FlickableDirection;
194 auto setFlickableDirection(FlickableDirection direction) -> void;
195
196 auto pixelAligned() const -> bool;
197 auto setPixelAligned(bool align) -> void;
198
199 auto synchronousDrag() const -> bool;
200 auto setSynchronousDrag(bool value) -> void;
201
202 auto acceptedButtons() const -> Qt::MouseButtons;
203 auto setAcceptedButtons(Qt::MouseButtons buttons) -> void;
204
205 auto inputMaskMode() const -> InputMaskMode;
206 auto setInputMaskMode(InputMaskMode mode) -> void;
207
208 auto interactionItem() const -> QQuickItem*;
209 auto setInteractionItem(QQuickItem* item) -> void;
210 auto resetInteractionItem() -> void;
211
212 auto visibleArea() -> FlickableVisibleArea*;
213
214 Q_INVOKABLE auto resizeContent(qreal width, qreal height, QPointF center) -> void;
215 Q_INVOKABLE auto flick(qreal xVelocity, qreal yVelocity) -> void;
216 Q_INVOKABLE auto cancelFlick() -> void;
217
218 Q_SIGNAL void contentWidthChanged();
219 Q_SIGNAL void contentHeightChanged();
220 Q_SIGNAL void contentXChanged();
221 Q_SIGNAL void contentYChanged();
222 Q_SIGNAL void topMarginChanged();
223 Q_SIGNAL void bottomMarginChanged();
224 Q_SIGNAL void leftMarginChanged();
225 Q_SIGNAL void rightMarginChanged();
226 Q_SIGNAL void originYChanged();
227 Q_SIGNAL void originXChanged();
228 Q_SIGNAL void movingChanged();
229 Q_SIGNAL void movingHorizontallyChanged();
230 Q_SIGNAL void movingVerticallyChanged();
231 Q_SIGNAL void flickingChanged();
232 Q_SIGNAL void flickingHorizontallyChanged();
233 Q_SIGNAL void flickingVerticallyChanged();
234 Q_SIGNAL void draggingChanged();
235 Q_SIGNAL void draggingHorizontallyChanged();
236 Q_SIGNAL void draggingVerticallyChanged();
237 Q_SIGNAL void horizontalVelocityChanged();
238 Q_SIGNAL void verticalVelocityChanged();
239 Q_SIGNAL void isAtBoundaryChanged();
240 Q_SIGNAL void flickableDirectionChanged();
241 Q_SIGNAL void interactiveChanged();
242 Q_SIGNAL void maximumFlickVelocityChanged();
243 Q_SIGNAL void flickDecelerationChanged();
244 Q_SIGNAL void pressDelayChanged();
245 Q_SIGNAL void movementStarted();
246 Q_SIGNAL void movementEnded();
247 Q_SIGNAL void flickStarted();
248 Q_SIGNAL void flickEnded();
249 Q_SIGNAL void dragStarted();
250 Q_SIGNAL void dragEnded();
251 Q_SIGNAL void pixelAlignedChanged();
252 Q_SIGNAL void synchronousDragChanged();
253 Q_SIGNAL void atXEndChanged();
254 Q_SIGNAL void atYEndChanged();
255 Q_SIGNAL void atXBeginningChanged();
256 Q_SIGNAL void atYBeginningChanged();
257 Q_SIGNAL void acceptedButtonsChanged();
258 Q_SIGNAL void inputMaskModeChanged();
259 Q_SIGNAL void interactionItemChanged();
260
261protected:
262 auto childMouseEventFilter(QQuickItem* item, QEvent* event) -> bool override;
263 auto mousePressEvent(QMouseEvent* event) -> void override;
264 auto mouseMoveEvent(QMouseEvent* event) -> void override;
265 auto mouseReleaseEvent(QMouseEvent* event) -> void override;
266 auto touchEvent(QTouchEvent* event) -> void override;
267 auto wheelEvent(QWheelEvent* event) -> void override;
268 auto timerEvent(QTimerEvent* event) -> void override;
269 auto geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) -> void override;
270 auto componentComplete() -> void override;
271 auto mouseUngrabEvent() -> void override;
272
273protected:
274 virtual auto minXExtent() const -> qreal;
275 virtual auto minYExtent() const -> qreal;
276 virtual auto maxXExtent() const -> qreal;
277 virtual auto maxYExtent() const -> qreal;
278 auto vWidth() const -> qreal;
279 auto vHeight() const -> qreal;
280 virtual auto viewportMoved(Qt::Orientations orientation) -> void;
281 auto xflick() const -> bool;
282 auto yflick() const -> bool;
283
284private:
285 friend class FlickableVisibleArea;
286
287 enum Axis
288 {
289 HorizontalAxis,
290 VerticalAxis,
291 };
292
293 enum MotionMode
294 {
295 NoMotion,
296 FlickMotion,
297 };
298
299 struct AxisData {
300 qreal viewSize { -1 };
301 qreal startMargin { 0 };
302 qreal endMargin { 0 };
303 qreal pressPos { 0 };
304 qreal pressContentPos { 0 };
305 qreal lastPos { 0 };
306 qreal dragStartOffset { 0 };
307 qreal previousDragDelta { 0 };
308 qreal velocity { 0 };
309 qreal smoothVelocity { 0 };
310 qreal motionStart { 0 };
311 qreal motionTarget { 0 };
312 qreal motionVelocity { 0 };
313 qreal motionDuration { 0 };
314 qreal motionElapsed { 0 };
315 QVector<QPair<qint64, qreal>> velocityBuffer;
316 MotionMode motionMode { NoMotion };
317 bool atEnd { false };
318 bool atBeginning { true };
319 bool moving { false };
320 bool flicking { false };
321 bool dragging { false };
322
323 auto resetDrag() -> void;
324 auto addVelocitySample(qint64 timestamp, qreal position, qreal maxVelocity) -> void;
325 auto updateVelocity() -> void;
326 };
327
328 static auto dataAppend(QQmlListProperty<QObject>* property, QObject* object) -> void;
329 static auto dataCount(QQmlListProperty<QObject>* property) -> qsizetype;
330 static auto dataAt(QQmlListProperty<QObject>* property, qsizetype index) -> QObject*;
331 static auto dataClear(QQmlListProperty<QObject>* property) -> void;
332 static auto childrenAppend(QQmlListProperty<QQuickItem>* property, QQuickItem* item) -> void;
333 static auto childrenCount(QQmlListProperty<QQuickItem>* property) -> qsizetype;
334 static auto childrenAt(QQmlListProperty<QQuickItem>* property, qsizetype index) -> QQuickItem*;
335 static auto childrenClear(QQmlListProperty<QQuickItem>* property) -> void;
336
337 auto axisData(Axis axis) -> AxisData&;
338 auto axisData(Axis axis) const -> const AxisData&;
339 auto axisPosition(Axis axis) const -> qreal;
340 auto setAxisPosition(Axis axis, qreal position) -> void;
341 auto minExtent(Axis axis) const -> qreal;
342 auto maxExtent(Axis axis) const -> qreal;
343 auto viewportSize(Axis axis) const -> qreal;
344 auto axisCanFlick(Axis axis) const -> bool;
345 auto aligned(qreal value) const -> qreal;
346 auto boundedPosition(Axis axis, qreal position) const -> qreal;
347 auto updateContentSize(Axis axis) -> void;
348 auto updateBeginningEnd() -> void;
349 auto updateVisibleArea() -> void;
350
351 auto setAxisMoving(Axis axis, bool value) -> void;
352 auto setAxisDragging(Axis axis, bool value) -> void;
353 auto setAxisFlicking(Axis axis, bool value) -> void;
354 auto setAxisVelocity(Axis axis, qreal value) -> void;
355 auto movementStarting() -> void;
356 auto movementEnding() -> void;
357 auto flickingStarted(bool horizontal, bool vertical) -> void;
358 auto draggingStarting(bool horizontal, bool vertical) -> void;
359 auto draggingEnding() -> void;
360
361 auto startAxisFlick(Axis axis, qreal velocity) -> void;
362 auto stopAxisMotion(Axis axis) -> void;
363 auto ensureMotionTimer() -> void;
364 auto advanceAxis(Axis axis, qreal deltaSeconds) -> void;
365 auto fixup(Axis axis) -> void;
366 auto isAxisAnimating(Axis axis) const -> bool;
367
368 auto handlePress(const QPointF& position, qint64 timestamp) -> void;
369 auto handleMove(const QPointF& position, qint64 timestamp, Qt::MouseButtons buttons) -> void;
370 auto handleRelease(const QPointF& position, qint64 timestamp) -> void;
371 auto cancelInteraction() -> void;
372 auto buttonsAccepted(const QSinglePointEvent* event) const -> bool;
373 auto pointerAccepted(QQuickItem* receiver, QEvent* event) const -> bool;
374 auto acceptsPoint(const QPointF& point) const -> bool;
375 auto eventPosition(QQuickItem* receiver, QEvent* event) const -> QPointF;
376 auto ignorePointerEvent(QEvent* event) const -> void;
377
378private:
379 QQuickItem* m_contentItem { nullptr };
380 QList<QObject*> m_data;
381 AxisData m_hData;
382 AxisData m_vData;
383 FlickableVisibleArea* m_visibleArea { nullptr };
384 QPointer<QQuickItem> m_interactionItem;
385 QBasicTimer m_motionTimer;
386 QElapsedTimer m_motionClock;
387 QBasicTimer m_pressDelayTimer;
388 QPointF m_pressPos;
389 QPointF m_lastPos;
390 qint64 m_lastPosTime { -1 };
391 qint64 m_lastPressTime { -1 };
392 FlickableDirection m_flickableDirection { AutoFlickDirection };
393 InputMaskMode m_inputMaskMode { Viewport };
394 qreal m_deceleration { 5000 };
395 qreal m_wheelDeceleration { 15000 };
396 qreal m_maxVelocity { 2500 };
397 int m_pressDelay { 0 };
398 bool m_pressed { false };
399 bool m_stealMouse { false };
400 bool m_interactive { true };
401 bool m_pixelAligned { false };
402 bool m_syncDrag { false };
403 Qt::MouseButtons m_acceptedButtons { Qt::LeftButton };
404};
405
406} // namespace qml_material