17 #include "organicgrid.h"
19 #pragma GCC diagnostic push
20 #pragma GCC diagnostic ignored "-pedantic"
21 #include <private/qquickitem_p.h>
22 #pragma GCC diagnostic pop
24 static const qreal bufferRatio = 0.5;
26 OrganicGrid::OrganicGrid()
27 : m_firstVisibleIndex(-1)
28 , m_numberOfModulesPerRow(-1)
32 QSizeF OrganicGrid::smallDelegateSize()
const
34 return m_smallDelegateSize;
37 void OrganicGrid::setSmallDelegateSize(
const QSizeF &size)
39 if (m_smallDelegateSize != size) {
40 m_smallDelegateSize = size;
41 Q_EMIT smallDelegateSizeChanged();
43 if (isComponentComplete()) {
49 QSizeF OrganicGrid::bigDelegateSize()
const
51 return m_bigDelegateSize;
54 void OrganicGrid::setBigDelegateSize(
const QSizeF &size)
56 if (m_bigDelegateSize != size) {
57 m_bigDelegateSize = size;
58 Q_EMIT bigDelegateSizeChanged();
60 if (isComponentComplete()) {
66 QPointF OrganicGrid::positionForIndex(
int modelIndex)
const
68 const qreal moduleHeight = m_smallDelegateSize.height() + rowSpacing() + m_bigDelegateSize.height();
69 const qreal moduleWidth = m_smallDelegateSize.width() * 2 + columnSpacing() * 2 + m_bigDelegateSize.width();
70 const int itemsPerRow = m_numberOfModulesPerRow * 6;
71 const int rowIndex = floor(modelIndex / itemsPerRow);
72 const int columnIndex = floor((modelIndex - rowIndex * itemsPerRow) / 6);
74 qreal yPos = (moduleHeight + rowSpacing()) * rowIndex;
75 const int moduleIndex = modelIndex % 6;
76 if (moduleIndex == 2) {
77 yPos += m_smallDelegateSize.height() + rowSpacing();
78 }
else if (moduleIndex == 3 || moduleIndex == 5) {
79 yPos += m_bigDelegateSize.height() + rowSpacing();
82 qreal xPos = (moduleWidth + columnSpacing()) * columnIndex;
83 if (moduleIndex == 1) {
84 xPos += m_smallDelegateSize.width() + columnSpacing();
85 }
else if (moduleIndex == 3) {
86 xPos += m_bigDelegateSize.width() + columnSpacing();
87 }
else if (moduleIndex == 4) {
88 xPos += (m_smallDelegateSize.width() + columnSpacing()) * 2;
89 }
else if (moduleIndex == 5) {
90 xPos += m_bigDelegateSize.width() + m_smallDelegateSize.width() + columnSpacing() * 2;
93 return QPointF(xPos, yPos);
96 QSizeF OrganicGrid::sizeForIndex(
int modelIndex)
const
98 const int moduleIndex = modelIndex % 6;
99 if (moduleIndex == 0 || moduleIndex == 1 || moduleIndex == 3 || moduleIndex == 5) {
100 return m_smallDelegateSize;
102 return m_bigDelegateSize;
106 void OrganicGrid::findBottomModelIndexToAdd(
int *modelIndex, qreal *yPos)
108 if (m_visibleItems.isEmpty()) {
112 *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
115 const int firstModuleIndex = ((*modelIndex) / 6) * 6;
116 *yPos = positionForIndex(firstModuleIndex).y();
120 void OrganicGrid::findTopModelIndexToAdd(
int *modelIndex, qreal *yPos)
122 if (m_visibleItems.isEmpty()) {
126 *modelIndex = m_firstVisibleIndex - 1;
129 const int lastModuleIndex = ((*modelIndex) / 6) * 6 + 5;
130 *yPos = positionForIndex(lastModuleIndex).y();
131 *yPos += sizeForIndex(lastModuleIndex).height();
135 void OrganicGrid::addItemToView(
int modelIndex, QQuickItem *item)
138 if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
139 m_visibleItems << item;
140 }
else if (modelIndex == m_firstVisibleIndex - 1) {
141 m_firstVisibleIndex = modelIndex;
142 m_visibleItems.prepend(item);
143 }
else if (modelIndex == 0) {
144 m_firstVisibleIndex = 0;
145 m_visibleItems << item;
147 qWarning() <<
"OrganicGrid::addItemToView - Got unexpected modelIndex"
148 << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
152 const QPointF pos = positionForIndex(modelIndex);
153 item->setPosition(pos);
155 item->setSize(sizeForIndex(modelIndex));
158 bool OrganicGrid::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
160 bool changed =
false;
163 int lastModuleIndex = (m_firstVisibleIndex / 6) * 6 + 5;
164 bool removeIndex = positionForIndex(lastModuleIndex).y() + sizeForIndex(lastModuleIndex).height() < bufferFromY;
165 while (removeIndex && !m_visibleItems.isEmpty()) {
166 releaseItem(m_visibleItems.takeFirst());
168 m_firstVisibleIndex++;
170 lastModuleIndex = (m_firstVisibleIndex / 6) * 6 + 5;
171 removeIndex = positionForIndex(lastModuleIndex).y() + sizeForIndex(lastModuleIndex).height() < bufferFromY;
174 int firstModuleIndex = ((m_firstVisibleIndex + m_visibleItems.count() - 1) / 6) * 6;
175 removeIndex = positionForIndex(firstModuleIndex).y() > bufferToY;
176 while (removeIndex && !m_visibleItems.isEmpty()) {
177 releaseItem(m_visibleItems.takeLast());
180 firstModuleIndex = ((m_firstVisibleIndex + m_visibleItems.count() - 1) / 6) * 6;
181 removeIndex = positionForIndex(firstModuleIndex).y() > bufferToY;
184 if (m_visibleItems.isEmpty()) {
185 m_firstVisibleIndex = -1;
191 void OrganicGrid::cleanupExistingItems()
193 Q_FOREACH(QQuickItem *item, m_visibleItems)
195 m_visibleItems.clear();
196 m_firstVisibleIndex = -1;
197 setImplicitHeightDirty();
202 const qreal moduleWidth = m_smallDelegateSize.width() * 2 + columnSpacing() * 2 + m_bigDelegateSize.width();
203 m_numberOfModulesPerRow = floor((width() + columnSpacing()) / (moduleWidth + columnSpacing()));
204 m_numberOfModulesPerRow = qMax(1, m_numberOfModulesPerRow);
206 int i = m_firstVisibleIndex;
207 const QList<QQuickItem*> allItems = m_visibleItems;
208 m_visibleItems.clear();
209 Q_FOREACH(QQuickItem *item, allItems) {
210 addItemToView(i, item);
215 void OrganicGrid::updateItemCulling(qreal visibleFromY, qreal visibleToY)
217 Q_FOREACH(QQuickItem *item, m_visibleItems) {
218 QQuickItemPrivate::get(item)->setCulled(item->y() + item->height() <= visibleFromY || item->y() >= visibleToY);
222 void OrganicGrid::calculateImplicitHeight()
224 const qreal moduleHeight = m_smallDelegateSize.height() + rowSpacing() + m_bigDelegateSize.height();
225 const int itemCount = !model() ? 0 : model()->rowCount();
226 const int itemsPerRow = m_numberOfModulesPerRow * 6;
227 const int fullRows = floor(itemCount / itemsPerRow);
228 const qreal fullRowsHeight = fullRows == 0 ? 0 : fullRows * moduleHeight + rowSpacing() * (fullRows - 1);
230 const int remainingItems = itemCount - fullRows * itemsPerRow;
231 if (remainingItems == 0) {
232 setImplicitHeight(fullRowsHeight);
233 }
else if (remainingItems <= 2) {
234 setImplicitHeight(fullRowsHeight + m_smallDelegateSize.height() + rowSpacing());
236 setImplicitHeight(fullRowsHeight + rowSpacing() + moduleHeight);
240 #if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
241 void OrganicGrid::processModelRemoves(
const QVector<QQmlChangeSet::Remove> &removes)
243 void OrganicGrid::processModelRemoves(
const QVector<QQmlChangeSet::Change> &removes)
246 Q_FOREACH(
const QQmlChangeSet::Change &
remove, removes) {
247 for (
int i =
remove.count - 1; i >= 0; --i) {
248 const int indexToRemove =
remove.index + i;
250 const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
251 if (indexToRemove == lastIndex) {
252 releaseItem(m_visibleItems.takeLast());
254 if (indexToRemove < lastIndex) {
255 qFatal(
"OrganicGrid only supports removal from the end of the model");
260 if (m_visibleItems.isEmpty()) {
261 m_firstVisibleIndex = -1;
263 setImplicitHeightDirty();