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 Q_PROPERTY(QPointF delta MEMBER delta CONSTANT)19 Q_PROPERTY(QPointF delta MEMBER delta CONSTANT) 20 Q_PROPERTY(bool accepted MEMBER accepted) 21 public: 22 ScrollIntentionEvent() 23 { 24 } ~ScrollIntentionEvent()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) 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) 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) 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) 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) 69 70 /** 71 * The view this column belongs to 72 */ 73 Q_PROPERTY(ColumnView *view READ view NOTIFY viewChanged) 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) 80 81 public: 82 ColumnViewAttached(QObject *parent = nullptr); 83 ~ColumnViewAttached() override; 84 85 void setIndex(int index); 86 int index() const; 87 88 void setFillWidth(bool fill); 89 bool fillWidth() const; 90 91 qreal reservedSpace() const; 92 void setReservedSpace(qreal space); 93 94 ColumnView *view(); 95 void setView(ColumnView *view); 96 97 // Private API, not for QML use 98 QQuickItem *originalParent() const; 99 void setOriginalParent(QQuickItem *parent); 100 101 bool shouldDeleteOnRemove() const; 102 void setShouldDeleteOnRemove(bool del); 103 104 bool preventStealing() const; 105 void setPreventStealing(bool prevent); 106 107 bool isPinned() const; 108 void setPinned(bool pinned); 109 110 bool inViewport() const; 111 void setInViewport(bool inViewport); 112 113 Q_SIGNALS: 114 void indexChanged(); 115 void fillWidthChanged(); 116 void reservedSpaceChanged(); 117 void viewChanged(); 118 void preventStealingChanged(); 119 void pinnedChanged(); 120 void scrollIntention(ScrollIntentionEvent *event); 121 void inViewportChanged(); 122 123 private: 124 int m_index = -1; 125 bool m_fillWidth = false; 126 qreal m_reservedSpace = 0; 127 QPointer<ColumnView> m_view; 128 QPointer<QQuickItem> m_originalParent; 129 bool m_customFillWidth = false; 130 bool m_customReservedSpace = false; 131 bool m_shouldDeleteOnRemove = true; 132 bool m_preventStealing = false; 133 bool m_pinned = false; 134 bool m_inViewport = false; 135 }; 136 137 /** 138 * ColumnView is a container that lays out items horizontally in a row, 139 * 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. 140 * The columns can either all have the same fixed size (recommended), 141 * size themselves with implicitWidth, or automatically expand to take all the available width: by default the last column will always be the expanding one. 142 * Items inside the Columnview can access info of the view and set layouting hints via the Columnview attached property. 143 * 144 * This is the base for the implementation of PageRow 145 * @since 2.7 146 */ 147 class ColumnView : public QQuickItem 148 { 149 Q_OBJECT 150 151 /** 152 * The strategy to follow while automatically resizing the columns, 153 * the enum can have the following values: 154 * * FixedColumns: every column is fixed at the same width of the columnWidth property 155 * * DynamicColumns: columns take their width from their implicitWidth 156 * * SingleColumn: only one column at a time is shown, as wide as the viewport, eventual reservedSpace on the column's attached property is ignored 157 */ 158 Q_PROPERTY(ColumnResizeMode columnResizeMode READ columnResizeMode WRITE setColumnResizeMode NOTIFY columnResizeModeChanged) 159 160 /** 161 * The width of all columns when columnResizeMode is FixedColumns 162 */ 163 Q_PROPERTY(qreal columnWidth READ columnWidth WRITE setColumnWidth NOTIFY columnWidthChanged) 164 165 /** 166 * How many columns this view containsItem*/ 167 Q_PROPERTY(int count READ count NOTIFY countChanged) 168 169 /** 170 * The position of the currently active column. The current column will also have keyboard focus 171 */ 172 Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) 173 174 /** 175 * The currently active column. The current column will also have keyboard focus 176 */ 177 Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentItemChanged) 178 179 /** 180 * The main content item of this view: it's the parent of the column items 181 */ 182 Q_PROPERTY(QQuickItem *contentItem READ contentItem CONSTANT) 183 184 /** 185 * The value of the horizontal scroll of the view, in pixels 186 */ 187 Q_PROPERTY(qreal contentX READ contentX WRITE setContentX NOTIFY contentXChanged) 188 189 /** 190 * The compound width of all columns in the view 191 */ 192 Q_PROPERTY(qreal contentWidth READ contentWidth NOTIFY contentWidthChanged) 193 194 /** 195 * The padding this will have at the top 196 */ 197 Q_PROPERTY(qreal topPadding READ topPadding WRITE setTopPadding NOTIFY topPaddingChanged) 198 199 /** 200 * The padding this will have at the bottom 201 */ 202 Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding NOTIFY bottomPaddingChanged) 203 204 /** 205 * The duration for scrolling animations 206 */ 207 Q_PROPERTY(int scrollDuration READ scrollDuration WRITE setScrollDuration NOTIFY scrollDurationChanged) 208 209 /** 210 * True if columns should be visually separated by a separator line 211 */ 212 Q_PROPERTY(bool separatorVisible READ separatorVisible WRITE setSeparatorVisible NOTIFY separatorVisibleChanged) 213 214 /** 215 * The list of all visible column items that are at least partially in the viewport at any given moment 216 */ 217 Q_PROPERTY(QList<QObject *> visibleItems READ visibleItems NOTIFY visibleItemsChanged) 218 219 /** 220 * The first of visibleItems provided from convenience 221 */ 222 Q_PROPERTY(QQuickItem *firstVisibleItem READ firstVisibleItem NOTIFY firstVisibleItemChanged) 223 224 /** 225 * The last of visibleItems provided from convenience 226 */ 227 Q_PROPERTY(QQuickItem *lastVisibleItem READ lastVisibleItem NOTIFY lastVisibleItemChanged) 228 229 // Properties to make it similar to Flickable 230 /** 231 * True when the user is dragging around with touch gestures the view contents 232 */ 233 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged) 234 235 /** 236 * True both when the user is dragging around with touch gestures the view contents or the view is animating 237 */ 238 Q_PROPERTY(bool moving READ moving NOTIFY movingChanged) 239 240 /** 241 * True if it supports moving the contents by dragging 242 */ 243 Q_PROPERTY(bool interactive READ interactive WRITE setInteractive NOTIFY interactiveChanged) 244 245 /** 246 * True if the contents can be dragged also with mouse besides touch 247 */ 248 Q_PROPERTY(bool acceptsMouse READ acceptsMouse WRITE setAcceptsMouse NOTIFY acceptsMouseChanged) 249 250 // Default properties 251 /** 252 * Every column item the view contains 253 */ 254 Q_PROPERTY(QQmlListProperty<QQuickItem> contentChildren READ contentChildren NOTIFY contentChildrenChanged FINAL) 255 /** 256 * every item declared inside the view, both visual and non-visual items 257 */ 258 Q_PROPERTY(QQmlListProperty<QObject> contentData READ contentData FINAL) 259 Q_CLASSINFO("DefaultProperty", "contentData") 260 261 public: 262 enum ColumnResizeMode { 263 FixedColumns = 0, 264 DynamicColumns, 265 SingleColumn, 266 }; 267 Q_ENUM(ColumnResizeMode) 268 269 ColumnView(QQuickItem *parent = nullptr); 270 ~ColumnView() override; 271 272 // QML property accessors 273 ColumnResizeMode columnResizeMode() const; 274 void setColumnResizeMode(ColumnResizeMode mode); 275 276 qreal columnWidth() const; 277 void setColumnWidth(qreal width); 278 279 int currentIndex() const; 280 void setCurrentIndex(int index); 281 282 int scrollDuration() const; 283 void setScrollDuration(int duration); 284 285 bool separatorVisible() const; 286 void setSeparatorVisible(bool visible); 287 288 int count() const; 289 290 qreal topPadding() const; 291 void setTopPadding(qreal padding); 292 293 qreal bottomPadding() const; 294 void setBottomPadding(qreal padding); 295 296 QQuickItem *currentItem(); 297 298 // NOTE: It's a QList<QObject *> as QML can't correctly build an Array out of QList<QQuickItem*> 299 QList<QObject *> visibleItems() const; 300 QQuickItem *firstVisibleItem() const; 301 QQuickItem *lastVisibleItem() const; 302 303 QQuickItem *contentItem() const; 304 305 QQmlListProperty<QQuickItem> contentChildren(); 306 QQmlListProperty<QObject> contentData(); 307 308 bool dragging() const; 309 bool moving() const; 310 qreal contentWidth() const; 311 312 qreal contentX() const; 313 void setContentX(qreal x) const; 314 315 bool interactive() const; 316 void setInteractive(bool interactive); 317 318 bool acceptsMouse() const; 319 void setAcceptsMouse(bool accepts); 320 321 // Api not intended for QML use 322 // can't do overloads in QML 323 QQuickItem *removeItem(QQuickItem *item); 324 QQuickItem *removeItem(int item); 325 326 // QML attached property 327 static ColumnViewAttached *qmlAttachedProperties(QObject *object); 328 329 public Q_SLOTS: 330 /** 331 * Pushes a new item at the end of the view 332 * @param item the new item which will be reparented and managed 333 */ 334 void addItem(QQuickItem *item); 335 336 /** 337 * Inserts a new item in the view at a given position. 338 * The current Item will not be changed, currentIndex will be adjusted 339 * accordingly if needed to keep the same current item. 340 * @param pos the position we want the new item to be inserted in 341 * @param item the new item which will be reparented and managed 342 */ 343 void insertItem(int pos, QQuickItem *item); 344 345 /** 346 * Replaces an item in the view at a given position with a new item. 347 * The current Item and currentIndex will not be changed. 348 * @param pos the position we want the new item to be placed in 349 * @param item the new item which will be reparented and managed 350 */ 351 void replaceItem(int pos, QQuickItem *item); 352 353 /** 354 * Move an item inside the view. 355 * The currentIndex property may be changed in order to keep currentItem the same. 356 * @param from the old position 357 * @param to the new position 358 */ 359 void moveItem(int from, int to); 360 361 /** 362 * Removes an item from the view. 363 * Items will be reparented to their old parent. 364 * If they have JavaScript ownership and they didn't have an old parent, they will be destroyed. 365 * CurrentIndex may be changed in order to keep the same currentItem 366 * @param item it can either be a pointer of an item or an integer specifying the position to remove 367 * @returns the item that has just been removed 368 */ 369 QQuickItem *removeItem(const QVariant &item); 370 371 /** 372 * Removes all the items after item. Starting from the last column, every column will be removed until item is found, which will be left in place. 373 * Items will be reparented to their old parent. 374 * If they have JavaScript ownership and they didn't have an old parent, they will be destroyed 375 * @param item the item which will be the new last one of the row. 376 * @returns the last item that has been removed 377 */ 378 QQuickItem *pop(QQuickItem *item); 379 380 /** 381 * Removes every item in the view. 382 * Items will be reparented to their old parent. 383 * If they have JavaScript ownership and they didn't have an old parent, they will be destroyed 384 */ 385 void clear(); 386 387 /** 388 * @returns true if the view contains the given item 389 */ 390 bool containsItem(QQuickItem *item); 391 392 /** 393 * Returns the visible item containing the point x, y in content coordinates. 394 * If there is no item at the point specified, or the item is not visible null is returned. 395 */ 396 QQuickItem *itemAt(qreal x, qreal y); 397 398 protected: 399 void classBegin() override; 400 void componentComplete() override; 401 void updatePolish() override; 402 void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override; 403 void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; 404 bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; 405 void mousePressEvent(QMouseEvent *event) override; 406 void mouseMoveEvent(QMouseEvent *event) override; 407 void mouseReleaseEvent(QMouseEvent *event) override; 408 void mouseUngrabEvent() override; 409 410 Q_SIGNALS: 411 /** 412 * A new item has been inserted 413 * @param position where the page has been inserted 414 * @param item a pointer to the new item 415 */ 416 void itemInserted(int position, QQuickItem *item); 417 418 /** 419 * An item has just been removed from the view 420 * @param item a pointer to the item that has just been removed 421 */ 422 void itemRemoved(QQuickItem *item); 423 424 // Property notifiers 425 void contentChildrenChanged(); 426 void columnResizeModeChanged(); 427 void columnWidthChanged(); 428 void currentIndexChanged(); 429 void currentItemChanged(); 430 void visibleItemsChanged(); 431 void countChanged(); 432 void draggingChanged(); 433 void movingChanged(); 434 void contentXChanged(); 435 void contentWidthChanged(); 436 void interactiveChanged(); 437 void acceptsMouseChanged(); 438 void scrollDurationChanged(); 439 void separatorVisibleChanged(); 440 void firstVisibleItemChanged(); 441 void lastVisibleItemChanged(); 442 void topPaddingChanged(); 443 void bottomPaddingChanged(); 444 445 private: 446 static void contentChildren_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *object); 447 static int contentChildren_count(QQmlListProperty<QQuickItem> *prop); 448 static QQuickItem *contentChildren_at(QQmlListProperty<QQuickItem> *prop, int index); 449 static void contentChildren_clear(QQmlListProperty<QQuickItem> *prop); 450 451 static void contentData_append(QQmlListProperty<QObject> *prop, QObject *object); 452 static int contentData_count(QQmlListProperty<QObject> *prop); 453 static QObject *contentData_at(QQmlListProperty<QObject> *prop, int index); 454 static void contentData_clear(QQmlListProperty<QObject> *prop); 455 456 QList<QObject *> m_contentData; 457 458 ContentItem *m_contentItem; 459 QPointer<QQuickItem> m_currentItem; 460 461 static QHash<QObject *, ColumnViewAttached *> m_attachedObjects; 462 qreal m_oldMouseX = -1.0; 463 qreal m_startMouseX = -1.0; 464 qreal m_oldMouseY = -1.0; 465 qreal m_startMouseY = -1.0; 466 int m_currentIndex = -1; 467 qreal m_topPadding = 0; 468 qreal m_bottomPadding = 0; 469 470 bool m_mouseDown = false; 471 bool m_interactive = true; 472 bool m_dragging = false; 473 bool m_moving = false; 474 bool m_separatorVisible = true; 475 bool m_complete = false; 476 bool m_acceptsMouse = false; 477 }; 478 479 QML_DECLARE_TYPEINFO(ColumnView, QML_HAS_ATTACHED_PROPERTIES) 480