on 73bdb21 help sign in
unity8/plugins/Dash/organicgrid.cpp
260 lines 8.9 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#pragma GCC diagnostic push
20#pragma GCC diagnostic ignored "-pedantic"
21#include <private/qquickitem_p.h>
22#pragma GCC diagnostic pop
24OrganicGrid::OrganicGrid()
25 : m_firstVisibleIndex(-1)
26 , m_numberOfModulesPerRow(-1)
30QSizeF OrganicGrid::smallDelegateSize() const
32 return m_smallDelegateSize;
35void OrganicGrid::setSmallDelegateSize(const QSizeF size)
37 if (m_smallDelegateSize != size) {
38 m_smallDelegateSize = size;
39 Q_EMIT smallDelegateSizeChanged();
41 if (isComponentComplete()) {
42 relayout();
43 }
44 }
47QSizeF OrganicGrid::bigDelegateSize() const
49 return m_bigDelegateSize;
52void OrganicGrid::setBigDelegateSize(const QSizeF size)
54 if (m_bigDelegateSize != size) {
55 m_bigDelegateSize = size;
56 Q_EMIT bigDelegateSizeChanged();
58 if (isComponentComplete()) {
59 relayout();
60 }
61 }
64QPointF OrganicGrid::positionForIndex(int modelIndex) const
66 const qreal moduleHeight = m_smallDelegateSize.height() + rowSpacing() + m_bigDelegateSize.height();
67 const qreal moduleWidth = m_smallDelegateSize.width() * 2 + columnSpacing() * 2 + m_bigDelegateSize.width();
68 const int itemsPerRow = m_numberOfModulesPerRow * 6;
69 const int rowIndex = floor(modelIndex / itemsPerRow);
70 const int columnIndex = floor((modelIndex - rowIndex * itemsPerRow) / 6);
72 qreal yPos = (moduleHeight + rowSpacing()) * rowIndex;
73 const int moduleIndex = modelIndex % 6;
74 if (moduleIndex == 2) {
75 yPos += m_smallDelegateSize.height() + rowSpacing();
76 } else if (moduleIndex == 3 || moduleIndex == 5) {
77 yPos += m_bigDelegateSize.height() + rowSpacing();
78 }
80 qreal xPos = (moduleWidth + columnSpacing()) * columnIndex;
81 if (moduleIndex == 1) {
82 xPos += m_smallDelegateSize.width() + columnSpacing();
83 } else if (moduleIndex == 3) {
84 xPos += m_bigDelegateSize.width() + columnSpacing();
85 } else if (moduleIndex == 4) {
86 xPos += (m_smallDelegateSize.width() + columnSpacing()) * 2;
87 } else if (moduleIndex == 5) {
88 xPos += m_bigDelegateSize.width() + m_smallDelegateSize.width() + columnSpacing() * 2;
89 }
91 return QPointF(xPos, yPos);
94QSizeF OrganicGrid::sizeForIndex(int modelIndex) const
96 const int moduleIndex = modelIndex % 6;
97 if (moduleIndex == 0 || moduleIndex == 1 || moduleIndex == 3 || moduleIndex == 5) {
98 return m_smallDelegateSize;
99 } else {
100 return m_bigDelegateSize;
101 }
104void OrganicGrid::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
106 if (m_visibleItems.isEmpty()) {
107 *modelIndex = 0;
108 *yPos = 0;
109 } else {
110 *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
111 // We create stuff in a 6-module basis, so always return back
112 // the y position of the first item
113 const int firstModuleIndex = ((*modelIndex) / 6) * 6;
114 *yPos = positionForIndex(firstModuleIndex).y();
115 }
118void OrganicGrid::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
120 if (m_visibleItems.isEmpty()) {
121 *modelIndex = 0;
122 *yPos = 0;
123 } else {
124 *modelIndex = m_firstVisibleIndex - 1;
125 // We create stuff in a 6-module basis, so always return back
126 // the y position of the last item bottom
127 const int lastModuleIndex = ((*modelIndex) / 6) * 6 + 5;
128 *yPos = positionForIndex(lastModuleIndex).y();
129 *yPos += sizeForIndex(lastModuleIndex).height();
130 }
133void OrganicGrid::addItemToView(int modelIndex, QQuickItem *item)
135 // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count() or the first
136 if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
137 m_visibleItems << item;
138 } else if (modelIndex == m_firstVisibleIndex - 1) {
139 m_firstVisibleIndex = modelIndex;
140 m_visibleItems.prepend(item);
141 } else if (modelIndex == 0) {
142 m_firstVisibleIndex = 0;
143 m_visibleItems << item;
144 } else {
145 qWarning() << "OrganicGrid::addItemToView - Got unexpected modelIndex"
146 << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
147 return;
148 }
150 const QPointF pos = positionForIndex(modelIndex);
151 item->setPosition(pos);
153 item->setSize(sizeForIndex(modelIndex));
156bool OrganicGrid::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
158 bool changed = false;
160 // As adding, we also remove in a 6-module basis
161 int lastModuleIndex = (m_firstVisibleIndex / 6) * 6 + 5;
162 bool removeIndex = positionForIndex(lastModuleIndex).y() + sizeForIndex(lastModuleIndex).height() < bufferFromY;
163 while (removeIndex && !m_visibleItems.isEmpty()) {
164 releaseItem(m_visibleItems.takeFirst());
165 changed = true;
166 m_firstVisibleIndex++;
168 lastModuleIndex = (m_firstVisibleIndex / 6) * 6 + 5;
169 removeIndex = positionForIndex(lastModuleIndex).y() + sizeForIndex(lastModuleIndex).height() < bufferFromY;
170 }
172 int firstModuleIndex = ((m_firstVisibleIndex + m_visibleItems.count() - 1) / 6) * 6;
173 removeIndex = positionForIndex(firstModuleIndex).y() > bufferToY;
174 while (removeIndex && !m_visibleItems.isEmpty()) {
175 releaseItem(m_visibleItems.takeLast());
176 changed = true;
178 firstModuleIndex = ((m_firstVisibleIndex + m_visibleItems.count() - 1) / 6) * 6;
179 removeIndex = positionForIndex(firstModuleIndex).y() > bufferToY;
180 }
182 if (m_visibleItems.isEmpty()) {
183 m_firstVisibleIndex = -1;
184 }
186 return changed;
189void OrganicGrid::cleanupExistingItems()
191 Q_FOREACH(QQuickItem *item, m_visibleItems)
192 releaseItem(item);
193 m_visibleItems.clear();
194 m_firstVisibleIndex = -1;
195 setImplicitHeightDirty();
198void OrganicGrid::doRelayout()
200 const qreal moduleWidth = m_smallDelegateSize.width() * 2 + columnSpacing() * 2 + m_bigDelegateSize.width();
201 m_numberOfModulesPerRow = floor((width() + columnSpacing()) / (moduleWidth + columnSpacing()));
202 m_numberOfModulesPerRow = qMax(1, m_numberOfModulesPerRow);
204 int i = m_firstVisibleIndex;
205 const QList<QQuickItem*> allItems = m_visibleItems;
206 m_visibleItems.clear();
207 Q_FOREACH(QQuickItem *item, allItems) {
208 addItemToView(i, item);
209 ++i;
210 }
213void OrganicGrid::updateItemCulling(qreal visibleFromY, qreal visibleToY)
215 Q_FOREACH(QQuickItem *item, m_visibleItems) {
216 QQuickItemPrivate::get(item)->setCulled(item->y() + item->height() <= visibleFromY || item->y() >= visibleToY);
217 }
220void OrganicGrid::calculateImplicitHeight()
222 const qreal moduleHeight = m_smallDelegateSize.height() + rowSpacing() + m_bigDelegateSize.height();
223 const int itemCount = !model() ? 0 : model()->rowCount();
224 const int itemsPerRow = m_numberOfModulesPerRow * 6;
225 const int fullRows = floor(itemCount / itemsPerRow);
226 const qreal fullRowsHeight = fullRows == 0 ? 0 : fullRows * moduleHeight + rowSpacing() * (fullRows - 1);
228 const int remainingItems = itemCount - fullRows * itemsPerRow;
229 if (remainingItems == 0) {
230 setImplicitHeight(fullRowsHeight);
231 } else if (remainingItems <= 2) {
232 setImplicitHeight(fullRowsHeight + m_smallDelegateSize.height() + rowSpacing());
233 } else {
234 setImplicitHeight(fullRowsHeight + rowSpacing() + moduleHeight);
235 }
238void OrganicGrid::processModelRemoves(const QVector<QQmlChangeSet::Change> &removes)
240 Q_FOREACH(const QQmlChangeSet::Change remove, removes) {
241 for (int i = remove.count - 1; i >= 0; --i) {
242 const int indexToRemove = remove.index + i;
243 // We only support removing from the end
244 const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
245 if (indexToRemove == lastIndex) {
246 releaseItem(m_visibleItems.takeLast());
247 } else {
248 if (indexToRemove < lastIndex) {
249 qDebug() << "OrganicGrid only supports removal from the end of the model, resetting instead";
250 cleanupExistingItems();
251 break;
252 }
253 }
254 }
255 }
256 if (m_visibleItems.isEmpty()) {
257 m_firstVisibleIndex = -1;
258 }
259 setImplicitHeightDirty();