1 | /* |
2 | * SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org> |
3 | * |
4 | * SPDX-License-Identifier: LGPL-2.0-or-later |
5 | */ |
6 | |
7 | #pragma once |
8 | |
9 | #include <QPointer> |
10 | #include <QQuickItem> |
11 | #include <QVariant> |
12 | |
13 | class ContentItem; |
14 | class ColumnView; |
15 | |
16 | class ScrollIntentionEvent : public QObject |
17 | { |
18 | Q_OBJECT |
19 | Q_PROPERTY(QPointF delta MEMBER delta CONSTANT FINAL) |
20 | Q_PROPERTY(bool accepted MEMBER accepted FINAL) |
21 | public: |
22 | ScrollIntentionEvent() |
23 | { |
24 | } |
25 | ~ScrollIntentionEvent() override |
26 | { |
27 | } |
28 | |
29 | QPointF delta; |
30 | bool accepted = false; |
31 | }; |
32 | |
33 | /** |
34 | * This is an attached property to every item that is inserted in the ColumnView, |
35 | * used to access the view and page information such as the position and information for layouting, such as fillWidth |
36 | * @since 2.7 |
37 | */ |
38 | class ColumnViewAttached : public QObject |
39 | { |
40 | Q_OBJECT |
41 | |
42 | /** |
43 | * The index position of the column in the view, starting from 0 |
44 | */ |
45 | Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged FINAL) |
46 | |
47 | /** |
48 | * If true, the column will expand to take the whole viewport space minus reservedSpace |
49 | */ |
50 | Q_PROPERTY(bool fillWidth READ fillWidth WRITE setFillWidth NOTIFY fillWidthChanged FINAL) |
51 | |
52 | /** |
53 | * When a column is fillWidth, it will keep reservedSpace amount of pixels from going to fill the full viewport width |
54 | */ |
55 | Q_PROPERTY(qreal reservedSpace READ reservedSpace WRITE setReservedSpace NOTIFY reservedSpaceChanged FINAL) |
56 | |
57 | /** |
58 | * Like the same property of MouseArea, when this is true, the column view won't |
59 | * try to manage events by itself when filtering from a child, not |
60 | * disturbing user interaction |
61 | */ |
62 | Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged FINAL) |
63 | |
64 | /** |
65 | * If true the page will never go out of view, but will stay either |
66 | * at the right or left side of the ColumnView |
67 | */ |
68 | Q_PROPERTY(bool pinned READ isPinned WRITE setPinned NOTIFY pinnedChanged FINAL) |
69 | |
70 | /** |
71 | * The view this column belongs to |
72 | */ |
73 | Q_PROPERTY(ColumnView *view READ view NOTIFY viewChanged FINAL) |
74 | |
75 | /** |
76 | * True if this column is at least partly visible in the ColumnView's viewport. |
77 | * @since 5.77 |
78 | */ |
79 | Q_PROPERTY(bool inViewport READ inViewport NOTIFY inViewportChanged FINAL) |
80 | |
81 | Q_PROPERTY(QQuickItem *globalHeader READ globalHeader WRITE setGlobalHeader NOTIFY globalHeaderChanged FINAL) |
82 | Q_PROPERTY(QQuickItem *globalFooter READ globalFooter WRITE setGlobalFooter NOTIFY globalFooterChanged FINAL) |
83 | |
84 | public: |
85 | ColumnViewAttached(QObject *parent = nullptr); |
86 | ~ColumnViewAttached() override; |
87 | |
88 | void setIndex(int index); |
89 | int index() const; |
90 | |
91 | void setFillWidth(bool fill); |
92 | bool fillWidth() const; |
93 | |
94 | qreal reservedSpace() const; |
95 | void setReservedSpace(qreal space); |
96 | |
97 | ColumnView *view(); |
98 | void setView(ColumnView *view); |
99 | |
100 | // Private API, not for QML use |
101 | QQuickItem *originalParent() const; |
102 | void setOriginalParent(QQuickItem *parent); |
103 | |
104 | bool shouldDeleteOnRemove() const; |
105 | void setShouldDeleteOnRemove(bool del); |
106 | |
107 | bool preventStealing() const; |
108 | void setPreventStealing(bool prevent); |
109 | |
110 | bool isPinned() const; |
111 | void setPinned(bool pinned); |
112 | |
113 | bool inViewport() const; |
114 | void setInViewport(bool inViewport); |
115 | |
116 | QQuickItem *globalHeader() const; |
117 | void setGlobalHeader(QQuickItem *); |
118 | |
119 | QQuickItem *globalFooter() const; |
120 | void setGlobalFooter(QQuickItem *); |
121 | |
122 | Q_SIGNALS: |
123 | void indexChanged(); |
124 | void fillWidthChanged(); |
125 | void reservedSpaceChanged(); |
126 | void viewChanged(); |
127 | void preventStealingChanged(); |
128 | void pinnedChanged(); |
129 | void scrollIntention(ScrollIntentionEvent *event); |
130 | void inViewportChanged(); |
131 | void globalHeaderChanged(QQuickItem *, QQuickItem *); |
132 | void globalFooterChanged(QQuickItem *, QQuickItem *); |
133 | |
134 | private: |
135 | int m_index = -1; |
136 | bool m_fillWidth = false; |
137 | qreal m_reservedSpace = 0; |
138 | QPointer<ColumnView> m_view; |
139 | QPointer<QQuickItem> m_originalParent; |
140 | bool m_customFillWidth = false; |
141 | bool m_customReservedSpace = false; |
142 | bool m_shouldDeleteOnRemove = true; |
143 | bool m_preventStealing = false; |
144 | bool m_pinned = false; |
145 | bool m_inViewport = false; |
146 | QPointer<QQuickItem> m_globalHeader; |
147 | QPointer<QQuickItem> m_globalFooter; |
148 | }; |
149 | |
150 | /** |
151 | * ColumnView is a container that lays out items horizontally in a row, |
152 | * when not all items fit in the ColumnView, it will behave like a Flickable and will be a scrollable view which shows only a determined number of columns. |
153 | * The columns can either all have the same fixed size (recommended), |
154 | * size themselves with implicitWidth, or automatically expand to take all the available width: by default the last column will always be the expanding one. |
155 | * Items inside the ColumnView can access info of the view and set layouting hints via the ColumnView attached property. |
156 | * |
157 | * This is the base for the implementation of PageRow |
158 | * @since 2.7 |
159 | */ |
160 | class ColumnView : public QQuickItem |
161 | { |
162 | Q_OBJECT |
163 | QML_ELEMENT |
164 | QML_ATTACHED(ColumnViewAttached) |
165 | |
166 | /** |
167 | * The strategy to follow while automatically resizing the columns, |
168 | * the enum can have the following values: |
169 | * * FixedColumns: every column is fixed at the same width of the columnWidth property |
170 | * * DynamicColumns: columns take their width from their implicitWidth |
171 | * * SingleColumn: only one column at a time is shown, as wide as the viewport, eventual reservedSpace on the column's attached property is ignored |
172 | */ |
173 | Q_PROPERTY(ColumnResizeMode columnResizeMode READ columnResizeMode WRITE setColumnResizeMode NOTIFY columnResizeModeChanged FINAL) |
174 | |
175 | /** |
176 | * The width of all columns when columnResizeMode is FixedColumns |
177 | */ |
178 | Q_PROPERTY(qreal columnWidth READ columnWidth WRITE setColumnWidth NOTIFY columnWidthChanged FINAL) |
179 | |
180 | /** |
181 | * How many columns this view containsItem*/ |
182 | Q_PROPERTY(int count READ count NOTIFY countChanged FINAL) |
183 | |
184 | /** |
185 | * The position of the currently active column. The current column will also have keyboard focus |
186 | */ |
187 | Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged FINAL) |
188 | |
189 | /** |
190 | * The currently active column. The current column will also have keyboard focus |
191 | */ |
192 | Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentItemChanged FINAL) |
193 | |
194 | /** |
195 | * The main content item of this view: it's the parent of the column items |
196 | */ |
197 | Q_PROPERTY(QQuickItem *contentItem READ contentItem CONSTANT FINAL) |
198 | |
199 | /** |
200 | * The value of the horizontal scroll of the view, in pixels |
201 | */ |
202 | Q_PROPERTY(qreal contentX READ contentX WRITE setContentX NOTIFY contentXChanged FINAL) |
203 | |
204 | /** |
205 | * The compound width of all columns in the view |
206 | */ |
207 | Q_PROPERTY(qreal contentWidth READ contentWidth NOTIFY contentWidthChanged FINAL) |
208 | |
209 | /** |
210 | * The padding this will have at the top |
211 | */ |
212 | Q_PROPERTY(qreal topPadding READ topPadding WRITE setTopPadding NOTIFY topPaddingChanged FINAL) |
213 | |
214 | /** |
215 | * The padding this will have at the bottom |
216 | */ |
217 | Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding NOTIFY bottomPaddingChanged FINAL) |
218 | |
219 | /** |
220 | * The duration for scrolling animations |
221 | */ |
222 | Q_PROPERTY(int scrollDuration READ scrollDuration WRITE setScrollDuration NOTIFY scrollDurationChanged FINAL) |
223 | |
224 | /** |
225 | * True if columns should be visually separated by a separator line |
226 | */ |
227 | Q_PROPERTY(bool separatorVisible READ separatorVisible WRITE setSeparatorVisible NOTIFY separatorVisibleChanged FINAL) |
228 | |
229 | /** |
230 | * The list of all visible column items that are at least partially in the viewport at any given moment |
231 | */ |
232 | Q_PROPERTY(QList<QObject *> visibleItems READ visibleItems NOTIFY visibleItemsChanged FINAL) |
233 | |
234 | /** |
235 | * The first of visibleItems provided from convenience |
236 | */ |
237 | Q_PROPERTY(QQuickItem *leadingVisibleItem READ leadingVisibleItem NOTIFY leadingVisibleItemChanged FINAL) |
238 | |
239 | /** |
240 | * The last of visibleItems provided from convenience |
241 | */ |
242 | Q_PROPERTY(QQuickItem *trailingVisibleItem READ trailingVisibleItem NOTIFY trailingVisibleItemChanged FINAL) |
243 | |
244 | // Properties to make it similar to Flickable |
245 | /** |
246 | * True when the user is dragging around with touch gestures the view contents |
247 | */ |
248 | Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged FINAL) |
249 | |
250 | /** |
251 | * True both when the user is dragging around with touch gestures the view contents or the view is animating |
252 | */ |
253 | Q_PROPERTY(bool moving READ moving NOTIFY movingChanged FINAL) |
254 | |
255 | /** |
256 | * True if it supports moving the contents by dragging |
257 | */ |
258 | Q_PROPERTY(bool interactive READ interactive WRITE setInteractive NOTIFY interactiveChanged FINAL) |
259 | |
260 | /** |
261 | * True if the contents can be dragged also with mouse besides touch |
262 | */ |
263 | Q_PROPERTY(bool acceptsMouse READ acceptsMouse WRITE setAcceptsMouse NOTIFY acceptsMouseChanged FINAL) |
264 | |
265 | // Default properties |
266 | /** |
267 | * Every column item the view contains |
268 | */ |
269 | Q_PROPERTY(QQmlListProperty<QQuickItem> contentChildren READ contentChildren NOTIFY contentChildrenChanged FINAL) |
270 | /** |
271 | * every item declared inside the view, both visual and non-visual items |
272 | */ |
273 | Q_PROPERTY(QQmlListProperty<QObject> contentData READ contentData FINAL) |
274 | Q_CLASSINFO("DefaultProperty" , "contentData" ) |
275 | |
276 | public: |
277 | enum ColumnResizeMode { |
278 | FixedColumns = 0, |
279 | DynamicColumns, |
280 | SingleColumn, |
281 | }; |
282 | Q_ENUM(ColumnResizeMode) |
283 | |
284 | ColumnView(QQuickItem *parent = nullptr); |
285 | ~ColumnView() override; |
286 | |
287 | // QML property accessors |
288 | ColumnResizeMode columnResizeMode() const; |
289 | void setColumnResizeMode(ColumnResizeMode mode); |
290 | |
291 | qreal columnWidth() const; |
292 | void setColumnWidth(qreal width); |
293 | |
294 | int currentIndex() const; |
295 | void setCurrentIndex(int index); |
296 | |
297 | int scrollDuration() const; |
298 | void setScrollDuration(int duration); |
299 | |
300 | bool separatorVisible() const; |
301 | void setSeparatorVisible(bool visible); |
302 | |
303 | int count() const; |
304 | |
305 | qreal topPadding() const; |
306 | void setTopPadding(qreal padding); |
307 | |
308 | qreal bottomPadding() const; |
309 | void setBottomPadding(qreal padding); |
310 | |
311 | QQuickItem *currentItem(); |
312 | |
313 | // NOTE: It's a QList<QObject *> as QML can't correctly build an Array out of QList<QQuickItem*> |
314 | QList<QObject *> visibleItems() const; |
315 | QQuickItem *leadingVisibleItem() const; |
316 | QQuickItem *trailingVisibleItem() const; |
317 | |
318 | QQuickItem *contentItem() const; |
319 | |
320 | QQmlListProperty<QQuickItem> contentChildren(); |
321 | QQmlListProperty<QObject> contentData(); |
322 | |
323 | bool dragging() const; |
324 | bool moving() const; |
325 | qreal contentWidth() const; |
326 | |
327 | qreal contentX() const; |
328 | void setContentX(qreal x) const; |
329 | |
330 | bool interactive() const; |
331 | void setInteractive(bool interactive); |
332 | |
333 | bool acceptsMouse() const; |
334 | void setAcceptsMouse(bool accepts); |
335 | |
336 | /** |
337 | * @brief This method removes all the items after the specified item or |
338 | * index from the view and returns the last item that was removed. |
339 | * |
340 | * Note that if the passed value is neither of the values said below, it |
341 | * will return a nullptr. |
342 | * |
343 | * @param item the item to remove. It can be an item, index or not defined |
344 | * in which case it will pop the last item. |
345 | */ |
346 | Q_INVOKABLE QQuickItem *pop(const QVariant &item); |
347 | |
348 | /** |
349 | * @brief This method removes all the items after the specified item from |
350 | * the view and returns the last item that was removed. |
351 | * |
352 | * @see ::removeItem() |
353 | * |
354 | * @param the item where the iteration should stop at |
355 | * @returns the last item that has been removed |
356 | */ |
357 | QQuickItem *pop(QQuickItem *item); |
358 | |
359 | /** |
360 | * @brief This method removes all the items after the specified position |
361 | * from the view and returns the last item that was removed. |
362 | * |
363 | * It starts iterating from the last item to the first item calling |
364 | * removeItem() for each of them until it reaches the specified position. |
365 | * |
366 | * @see ::removeItem() |
367 | * |
368 | * @param the position where the iteration should stop at |
369 | * @returns the last item that has been removed |
370 | */ |
371 | QQuickItem *pop(int index); |
372 | |
373 | /** |
374 | * @brief This method removes the last item from the view and returns it. |
375 | * |
376 | * This method calls removeItem() on the last item. |
377 | * |
378 | * @see ::removeItem() |
379 | * |
380 | * @return the removed item |
381 | */ |
382 | Q_INVOKABLE QQuickItem *pop(); |
383 | |
384 | /** |
385 | * @brief This method removes the specified item from the view. |
386 | * |
387 | * Items will be reparented to their old parent. If they have JavaScript |
388 | * ownership and they didn't have an old parent, they will be destroyed. |
389 | * CurrentIndex may be changed in order to keep the same currentItem |
390 | * |
391 | * @param item pointer to the item to remove |
392 | * @returns the removed item |
393 | */ |
394 | QQuickItem *removeItem(QQuickItem *item); |
395 | |
396 | /** |
397 | * @brief This method removes an item at a given index from the view. |
398 | * |
399 | * This method calls removeItem(QQuickItem *item) to remove the item at |
400 | * the specified index. |
401 | * |
402 | * @see ::removeItem(QQuickItem *item) |
403 | * |
404 | * @param index the index of the item which should be removed |
405 | * @return the removed item |
406 | */ |
407 | QQuickItem *removeItem(int index); |
408 | |
409 | // QML attached property |
410 | static ColumnViewAttached *qmlAttachedProperties(QObject *object); |
411 | |
412 | public Q_SLOTS: |
413 | /** |
414 | * Pushes a new item at the end of the view |
415 | * @param item the new item which will be reparented and managed |
416 | */ |
417 | void addItem(QQuickItem *item); |
418 | |
419 | /** |
420 | * @brief This method removes an item from the view. |
421 | * |
422 | * If the argument is a number, this method dispatches to removeItem(int index) |
423 | * to remove an item by its index. Otherwise the argument should be the item |
424 | * itself to be removed itself, and this method will dispatch to removeItem(QQuickItem *item). |
425 | * |
426 | * @see ::removeItem(QQuickItem *item) |
427 | * |
428 | * @param index the index of the item which should be removed, or the item itself |
429 | * @return the removed item |
430 | */ |
431 | Q_INVOKABLE QQuickItem *removeItem(const QVariant &item); |
432 | |
433 | /** |
434 | * Inserts a new item in the view at a given position. |
435 | * The current Item will not be changed, currentIndex will be adjusted |
436 | * accordingly if needed to keep the same current item. |
437 | * @param pos the position we want the new item to be inserted in |
438 | * @param item the new item which will be reparented and managed |
439 | */ |
440 | void insertItem(int pos, QQuickItem *item); |
441 | |
442 | /** |
443 | * Replaces an item in the view at a given position with a new item. |
444 | * The current Item and currentIndex will not be changed. |
445 | * @param pos the position we want the new item to be placed in |
446 | * @param item the new item which will be reparented and managed |
447 | */ |
448 | void replaceItem(int pos, QQuickItem *item); |
449 | |
450 | /** |
451 | * Move an item inside the view. |
452 | * The currentIndex property may be changed in order to keep currentItem the same. |
453 | * @param from the old position |
454 | * @param to the new position |
455 | */ |
456 | void moveItem(int from, int to); |
457 | |
458 | /** |
459 | * Removes every item in the view. |
460 | * Items will be reparented to their old parent. |
461 | * If they have JavaScript ownership and they didn't have an old parent, they will be destroyed |
462 | */ |
463 | void clear(); |
464 | |
465 | /** |
466 | * @returns true if the view contains the given item |
467 | */ |
468 | bool containsItem(QQuickItem *item); |
469 | |
470 | /** |
471 | * Returns the visible item containing the point x, y in content coordinates. |
472 | * If there is no item at the point specified, or the item is not visible null is returned. |
473 | */ |
474 | QQuickItem *itemAt(qreal x, qreal y); |
475 | |
476 | protected: |
477 | void classBegin() override; |
478 | void componentComplete() override; |
479 | void updatePolish() override; |
480 | void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override; |
481 | void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; |
482 | bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; |
483 | void mousePressEvent(QMouseEvent *event) override; |
484 | void mouseMoveEvent(QMouseEvent *event) override; |
485 | void mouseReleaseEvent(QMouseEvent *event) override; |
486 | void mouseUngrabEvent() override; |
487 | |
488 | Q_SIGNALS: |
489 | /** |
490 | * A new item has been inserted |
491 | * @param position where the page has been inserted |
492 | * @param item a pointer to the new item |
493 | */ |
494 | void itemInserted(int position, QQuickItem *item); |
495 | |
496 | /** |
497 | * An item has just been removed from the view |
498 | * @param item a pointer to the item that has just been removed |
499 | */ |
500 | void itemRemoved(QQuickItem *item); |
501 | |
502 | // Property notifiers |
503 | void contentChildrenChanged(); |
504 | void columnResizeModeChanged(); |
505 | void columnWidthChanged(); |
506 | void currentIndexChanged(); |
507 | void currentItemChanged(); |
508 | void visibleItemsChanged(); |
509 | void countChanged(); |
510 | void draggingChanged(); |
511 | void movingChanged(); |
512 | void contentXChanged(); |
513 | void contentWidthChanged(); |
514 | void interactiveChanged(); |
515 | void acceptsMouseChanged(); |
516 | void scrollDurationChanged(); |
517 | void separatorVisibleChanged(); |
518 | void leadingVisibleItemChanged(); |
519 | void trailingVisibleItemChanged(); |
520 | void topPaddingChanged(); |
521 | void bottomPaddingChanged(); |
522 | |
523 | private: |
524 | static void contentChildren_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *object); |
525 | static qsizetype contentChildren_count(QQmlListProperty<QQuickItem> *prop); |
526 | static QQuickItem *contentChildren_at(QQmlListProperty<QQuickItem> *prop, qsizetype index); |
527 | static void contentChildren_clear(QQmlListProperty<QQuickItem> *prop); |
528 | |
529 | static void contentData_append(QQmlListProperty<QObject> *prop, QObject *object); |
530 | static qsizetype contentData_count(QQmlListProperty<QObject> *prop); |
531 | static QObject *contentData_at(QQmlListProperty<QObject> *prop, qsizetype index); |
532 | static void contentData_clear(QQmlListProperty<QObject> *prop); |
533 | |
534 | QList<QObject *> m_contentData; |
535 | |
536 | ContentItem *m_contentItem; |
537 | QPointer<QQuickItem> m_currentItem; |
538 | |
539 | qreal m_oldMouseX = -1.0; |
540 | qreal m_startMouseX = -1.0; |
541 | qreal m_oldMouseY = -1.0; |
542 | qreal m_startMouseY = -1.0; |
543 | int m_currentIndex = -1; |
544 | qreal m_topPadding = 0; |
545 | qreal m_bottomPadding = 0; |
546 | |
547 | bool m_mouseDown = false; |
548 | bool m_interactive = true; |
549 | bool m_dragging = false; |
550 | bool m_moving = false; |
551 | bool m_separatorVisible = true; |
552 | bool m_complete = false; |
553 | bool m_acceptsMouse = false; |
554 | }; |
555 | |
556 | QML_DECLARE_TYPEINFO(ColumnView, QML_HAS_ATTACHED_PROPERTIES) |
557 | |