on 18deb76 help sign in
unity8/plugins/Dash/organicgrid.cpp
257 lines 8.8 kB history raw
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17#include "organicgrid.h"
19#include <private/qquickitem_p.h>
21OrganicGrid::OrganicGrid()
22 : m_firstVisibleIndex(-1)
23 , m_numberOfModulesPerRow(-1)
27QSizeF OrganicGrid::smallDelegateSize() const
29 return m_smallDelegateSize;
32void OrganicGrid::setSmallDelegateSize(const QSizeF size)
34 if (m_smallDelegateSize != size) {
35 m_smallDelegateSize = size;
36 Q_EMIT smallDelegateSizeChanged();
38 if (isComponentComplete()) {
39 relayout();
40 }
41 }
44QSizeF OrganicGrid::bigDelegateSize() const
46 return m_bigDelegateSize;
49void OrganicGrid::setBigDelegateSize(const QSizeF size)
51 if (m_bigDelegateSize != size) {
52 m_bigDelegateSize = size;
53 Q_EMIT bigDelegateSizeChanged();
55 if (isComponentComplete()) {
56 relayout();
57 }
58 }
61QPointF OrganicGrid::positionForIndex(int modelIndex) const
63 const qreal moduleHeight = m_smallDelegateSize.height() + rowSpacing() + m_bigDelegateSize.height();
64 const qreal moduleWidth = m_smallDelegateSize.width() * 2 + columnSpacing() * 2 + m_bigDelegateSize.width();
65 const int itemsPerRow = m_numberOfModulesPerRow * 6;
66 const int rowIndex = floor(modelIndex / itemsPerRow);
67 const int columnIndex = floor((modelIndex - rowIndex * itemsPerRow) / 6);
69 qreal yPos = (moduleHeight + rowSpacing()) * rowIndex;
70 const int moduleIndex = modelIndex % 6;
71 if (moduleIndex == 2) {
72 yPos += m_smallDelegateSize.height() + rowSpacing();
73 } else if (moduleIndex == 3 || moduleIndex == 5) {
74 yPos += m_bigDelegateSize.height() + rowSpacing();
75 }
77 qreal xPos = (moduleWidth + columnSpacing()) * columnIndex;
78 if (moduleIndex == 1) {
79 xPos += m_smallDelegateSize.width() + columnSpacing();
80 } else if (moduleIndex == 3) {
81 xPos += m_bigDelegateSize.width() + columnSpacing();
82 } else if (moduleIndex == 4) {
83 xPos += (m_smallDelegateSize.width() + columnSpacing()) * 2;
84 } else if (moduleIndex == 5) {
85 xPos += m_bigDelegateSize.width() + m_smallDelegateSize.width() + columnSpacing() * 2;
86 }
88 return QPointF(xPos, yPos);
91QSizeF OrganicGrid::sizeForIndex(int modelIndex) const
93 const int moduleIndex = modelIndex % 6;
94 if (moduleIndex == 0 || moduleIndex == 1 || moduleIndex == 3 || moduleIndex == 5) {
95 return m_smallDelegateSize;
96 } else {
97 return m_bigDelegateSize;
98 }
101void OrganicGrid::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
103 if (m_visibleItems.isEmpty()) {
104 *modelIndex = 0;
105 *yPos = 0;
106 } else {
107 *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
108 // We create stuff in a 6-module basis, so always return back
109 // the y position of the first item
110 const int firstModuleIndex = ((*modelIndex) / 6) * 6;
111 *yPos = positionForIndex(firstModuleIndex).y();
112 }
115void OrganicGrid::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
117 if (m_visibleItems.isEmpty()) {
118 *modelIndex = 0;
119 *yPos = 0;
120 } else {
121 *modelIndex = m_firstVisibleIndex - 1;
122 // We create stuff in a 6-module basis, so always return back
123 // the y position of the last item bottom
124 const int lastModuleIndex = ((*modelIndex) / 6) * 6 + 5;
125 *yPos = positionForIndex(lastModuleIndex).y();
126 *yPos += sizeForIndex(lastModuleIndex).height();
127 }
130void OrganicGrid::addItemToView(int modelIndex, QQuickItem *item)
132 // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count() or the first
133 if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
134 m_visibleItems << item;
135 } else if (modelIndex == m_firstVisibleIndex - 1) {
136 m_firstVisibleIndex = modelIndex;
137 m_visibleItems.prepend(item);
138 } else if (modelIndex == 0) {
139 m_firstVisibleIndex = 0;
140 m_visibleItems << item;
141 } else {
142 qWarning() << "OrganicGrid::addItemToView - Got unexpected modelIndex"
143 << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
144 return;
145 }
147 const QPointF pos = positionForIndex(modelIndex);
148 item->setPosition(pos);
150 item->setSize(sizeForIndex(modelIndex));
153bool OrganicGrid::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
155 bool changed = false;
157 // As adding, we also remove in a 6-module basis
158 int lastModuleIndex = (m_firstVisibleIndex / 6) * 6 + 5;
159 bool removeIndex = positionForIndex(lastModuleIndex).y() + sizeForIndex(lastModuleIndex).height() < bufferFromY;
160 while (removeIndex && !m_visibleItems.isEmpty()) {
161 releaseItem(m_visibleItems.takeFirst());
162 changed = true;
163 m_firstVisibleIndex++;
165 lastModuleIndex = (m_firstVisibleIndex / 6) * 6 + 5;
166 removeIndex = positionForIndex(lastModuleIndex).y() + sizeForIndex(lastModuleIndex).height() < bufferFromY;
167 }
169 int firstModuleIndex = ((m_firstVisibleIndex + m_visibleItems.count() - 1) / 6) * 6;
170 removeIndex = positionForIndex(firstModuleIndex).y() > bufferToY;
171 while (removeIndex && !m_visibleItems.isEmpty()) {
172 releaseItem(m_visibleItems.takeLast());
173 changed = true;
175 firstModuleIndex = ((m_firstVisibleIndex + m_visibleItems.count() - 1) / 6) * 6;
176 removeIndex = positionForIndex(firstModuleIndex).y() > bufferToY;
177 }
179 if (m_visibleItems.isEmpty()) {
180 m_firstVisibleIndex = -1;
181 }
183 return changed;
186void OrganicGrid::cleanupExistingItems()
188 Q_FOREACH(QQuickItem *item, m_visibleItems)
189 releaseItem(item);
190 m_visibleItems.clear();
191 m_firstVisibleIndex = -1;
192 setImplicitHeightDirty();
195void OrganicGrid::doRelayout()
197 const qreal moduleWidth = m_smallDelegateSize.width() * 2 + columnSpacing() * 2 + m_bigDelegateSize.width();
198 m_numberOfModulesPerRow = floor((width() + columnSpacing()) / (moduleWidth + columnSpacing()));
199 m_numberOfModulesPerRow = qMax(1, m_numberOfModulesPerRow);
201 int i = m_firstVisibleIndex;
202 const QList<QQuickItem*> allItems = m_visibleItems;
203 m_visibleItems.clear();
204 Q_FOREACH(QQuickItem *item, allItems) {
205 addItemToView(i, item);
206 ++i;
207 }
210void OrganicGrid::updateItemCulling(qreal visibleFromY, qreal visibleToY)
212 Q_FOREACH(QQuickItem *item, m_visibleItems) {
213 QQuickItemPrivate::get(item)->setCulled(item->y() + item->height() <= visibleFromY || item->y() >= visibleToY);
214 }
217void OrganicGrid::calculateImplicitHeight()
219 const qreal moduleHeight = m_smallDelegateSize.height() + rowSpacing() + m_bigDelegateSize.height();
220 const int itemCount = !model() ? 0 : model()->rowCount();
221 const int itemsPerRow = m_numberOfModulesPerRow * 6;
222 const int fullRows = floor(itemCount / itemsPerRow);
223 const qreal fullRowsHeight = fullRows == 0 ? 0 : fullRows * moduleHeight + rowSpacing() * (fullRows - 1);
225 const int remainingItems = itemCount - fullRows * itemsPerRow;
226 if (remainingItems == 0) {
227 setImplicitHeight(fullRowsHeight);
228 } else if (remainingItems <= 2) {
229 setImplicitHeight(fullRowsHeight + m_smallDelegateSize.height() + rowSpacing());
230 } else {
231 setImplicitHeight(fullRowsHeight + rowSpacing() + moduleHeight);
232 }
235void OrganicGrid::processModelRemoves(const QVector<QQmlChangeSet::Change> &removes)
237 Q_FOREACH(const QQmlChangeSet::Change remove, removes) {
238 for (int i = remove.count - 1; i >= 0; --i) {
239 const int indexToRemove = remove.index + i;
240 // We only support removing from the end
241 const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
242 if (indexToRemove == lastIndex) {
243 releaseItem(m_visibleItems.takeLast());
244 } else {
245 if (indexToRemove < lastIndex) {
246 qDebug() << "OrganicGrid only supports removal from the end of the model, resetting instead";
247 cleanupExistingItems();
248 break;
249 }
250 }
251 }
252 }
253 if (m_visibleItems.isEmpty()) {
254 m_firstVisibleIndex = -1;
255 }
256 setImplicitHeightDirty();