1 /*
2  * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
3  *
4  * SPDX-License-Identifier: GPL-2.0-or-later
5  */
6 
7 #ifndef KITEMLISTVIEWLAYOUTER_H
8 #define KITEMLISTVIEWLAYOUTER_H
9 
10 #include "dolphin_export.h"
11 
12 #include <QObject>
13 #include <QRectF>
14 #include <QSet>
15 #include <QSizeF>
16 #include <QVector>
17 
18 class KItemModelBase;
19 class KItemListSizeHintResolver;
20 
21 /**
22  * @brief Internal helper class for KItemListView to layout the items.
23  *
24  * The layouter is capable to align the items within a grid. If the
25  * scroll-direction is horizontal the column-width of the grid can be
26  * variable. If the scroll-direction is vertical the row-height of
27  * the grid can be variable.
28  *
29  * The layouter is implemented in a way that it postpones the expensive
30  * layout operation until a property is read the first time after
31  * marking the layouter as dirty (see markAsDirty()). This means that
32  * changing properties of the layouter is not expensive, only the
33  * first read of a property can get expensive.
34  */
35 class DOLPHIN_EXPORT KItemListViewLayouter : public QObject
36 {
37     Q_OBJECT
38 
39 public:
40     explicit KItemListViewLayouter(KItemListSizeHintResolver* sizeHintResolver, QObject* parent = nullptr);
41     ~KItemListViewLayouter() override;
42 
43     void setScrollOrientation(Qt::Orientation orientation);
44     Qt::Orientation scrollOrientation() const;
45 
46     void setSize(const QSizeF& size);
47     QSizeF size() const;
48 
49     void setItemSize(const QSizeF& size);
50     QSizeF itemSize() const;
51 
52     /**
53      * Margin between the rows and columns of items.
54      */
55     void setItemMargin(const QSizeF& margin);
56     QSizeF itemMargin() const;
57 
58     /**
59      * Sets the height of the header that is always aligned
60      * at the top. A height of <= 0.0 means that no header is
61      * used.
62      */
63     void setHeaderHeight(qreal height);
64     qreal headerHeight() const;
65 
66     /**
67      * Sets the height of the group header that is used
68      * to indicate a new item group.
69      */
70     void setGroupHeaderHeight(qreal height);
71     qreal groupHeaderHeight() const;
72 
73     /**
74      * Sets the margin between the last items of the group n and
75      * the group header for the group n + 1.
76      */
77     void setGroupHeaderMargin(qreal margin);
78     qreal groupHeaderMargin() const;
79 
80     void setScrollOffset(qreal scrollOffset);
81     qreal scrollOffset() const;
82 
83     qreal maximumScrollOffset() const;
84 
85     void setItemOffset(qreal scrollOffset);
86     qreal itemOffset() const;
87 
88     qreal maximumItemOffset() const;
89 
90     void setModel(const KItemModelBase* model);
91     const KItemModelBase* model() const;
92 
93     /**
94      * @return The first (at least partly) visible index. -1 is returned
95      *         if the item count is 0.
96      */
97     int firstVisibleIndex() const;
98 
99     /**
100      * @return The last (at least partly) visible index. -1 is returned
101      *         if the item count is 0.
102      */
103     int lastVisibleIndex() const;
104 
105     /**
106      * @return Rectangle of the item with the index \a index.
107      *         The top/left of the bounding rectangle is related to
108      *         the top/left of the KItemListView. An empty rectangle
109      *         is returned if an invalid index is given.
110      */
111     QRectF itemRect(int index) const;
112 
113     /**
114      * @return Rectangle of the group header for the item with the
115      *         index \a index. Note that the layouter does not check
116      *         whether the item really has a header: Usually only
117      *         the first item of a group gets a header (see
118      *         isFirstGroupItem()).
119      */
120     QRectF groupHeaderRect(int index) const;
121 
122     /**
123      * @return Column of the item with the index \a index.
124      *         -1 is returned if an invalid index is given.
125      */
126     int itemColumn(int index) const;
127 
128     /**
129      * @return Row of the item with the index \a index.
130      *         -1 is returned if an invalid index is given.
131      */
132     int itemRow(int index) const;
133 
134     /**
135      * @return Maximum number of (at least partly) visible items for
136      *         the given size.
137      */
138     int maximumVisibleItems() const;
139 
140     /**
141      * @return True if the item with the index \p itemIndex
142      *         is the first item within a group.
143      */
144     bool isFirstGroupItem(int itemIndex) const;
145 
146     /**
147      * Marks the layouter as dirty. This means as soon as a property of
148      * the layouter gets read, an expensive relayout will be done.
149      */
150     void markAsDirty();
151 
columnCount()152     inline int columnCount() const
153     {
154         return m_columnCount;
155     }
156 
157 #ifndef QT_NO_DEBUG
158     /**
159      * @return True if the layouter has been marked as dirty and hence has
160      *         not called yet doLayout(). Is enabled only in the debugging
161      *         mode, as it is not useful to check the dirty state otherwise.
162      */
163     bool isDirty();
164 #endif
165 
166 private:
167     void doLayout();
168     void updateVisibleIndexes();
169     bool createGroupHeaders();
170 
171     /**
172      * @return Minimum width of group headers when grouping is enabled in the horizontal
173      *         alignment mode. The header alignment is done like this:
174      *         Header-1 Header-2 Header-3
175      *         Item 1   Item 4   Item 7
176      *         Item 2   Item 5   Item 8
177      *         Item 3   Item 6   Item 9
178      */
179     qreal minimumGroupHeaderWidth() const;
180 
181 private:
182     bool m_dirty;
183     bool m_visibleIndexesDirty;
184 
185     Qt::Orientation m_scrollOrientation;
186     QSizeF m_size;
187 
188     QSizeF m_itemSize;
189     QSizeF m_itemMargin;
190     qreal m_headerHeight;
191     const KItemModelBase* m_model;
192     KItemListSizeHintResolver* m_sizeHintResolver;
193 
194     qreal m_scrollOffset;
195     qreal m_maximumScrollOffset;
196 
197     qreal m_itemOffset;
198     qreal m_maximumItemOffset;
199 
200     int m_firstVisibleIndex;
201     int m_lastVisibleIndex;
202 
203     qreal m_columnWidth;
204     qreal m_xPosInc;
205     int m_columnCount;
206 
207     QVector<qreal> m_rowOffsets;
208     QVector<qreal> m_columnOffsets;
209 
210     // Stores all item indexes that are the first item of a group.
211     // Assures fast access for KItemListViewLayouter::isFirstGroupItem().
212     QSet<int> m_groupItemIndexes;
213     qreal m_groupHeaderHeight;
214     qreal m_groupHeaderMargin;
215 
216     struct ItemInfo {
217         int column;
218         int row;
219     };
220     QVector<ItemInfo> m_itemInfos;
221 
222     friend class KItemListControllerTest;
223 };
224 
225 #endif
226 
227 
228