on c78b47f help sign in
unity8/plugins/DashViews/organicgrid.cpp
267 lines 9.2 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
24static const qreal bufferRatio = 0.5;
26OrganicGrid::OrganicGrid()
27 : m_firstVisibleIndex(-1)
28 , m_numberOfModulesPerRow(-1)
32QSizeF OrganicGrid::smallDelegateSize() const
34 return m_smallDelegateSize;
37void OrganicGrid::setSmallDelegateSize(const QSizeF &size)
39 if (m_smallDelegateSize != size) {
40 m_smallDelegateSize = size;
41 Q_EMIT smallDelegateSizeChanged();
43 if (isComponentComplete()) {
44 relayout();
45 }
46 }
49QSizeF OrganicGrid::bigDelegateSize() const
51 return m_bigDelegateSize;
54void OrganicGrid::setBigDelegateSize(const QSizeF &size)
56 if (m_bigDelegateSize != size) {
57 m_bigDelegateSize = size;
58 Q_EMIT bigDelegateSizeChanged();
60 if (isComponentComplete()) {
61 relayout();
62 }
63 }
66QPointF 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 rowIndex = floor(modelIndex / (m_numberOfModulesPerRow * 6));
71 const int columnIndex = floor((modelIndex - rowIndex * m_numberOfModulesPerRow * 6) / 6);
73 qreal yPos = (moduleHeight + rowSpacing()) * rowIndex;
74 const int moduleIndex = modelIndex % 6;
75 if (moduleIndex == 2) {
76 yPos += m_smallDelegateSize.height() + rowSpacing();
77 } else if (moduleIndex == 3 || moduleIndex == 5) {
78 yPos += m_bigDelegateSize.height() + rowSpacing();
79 }
81 qreal xPos = (moduleWidth + columnSpacing()) * columnIndex;
82 if (moduleIndex == 1) {
83 xPos += m_smallDelegateSize.width() + columnSpacing();
84 } else if (moduleIndex == 3) {
85 xPos += m_bigDelegateSize.width() + columnSpacing();
86 } else if (moduleIndex == 4) {
87 xPos += (m_smallDelegateSize.width() + columnSpacing()) * 2;
88 } else if (moduleIndex == 5) {
89 xPos += m_bigDelegateSize.width() + m_smallDelegateSize.width() + columnSpacing() * 2;
90 }
92 return QPointF(xPos, yPos);
95QSizeF OrganicGrid::sizeForIndex(int modelIndex) const
97 const int moduleIndex = modelIndex % 6;
98 if (moduleIndex == 0 || moduleIndex == 1 || moduleIndex == 3 || moduleIndex == 5) {
99 return m_smallDelegateSize;
100 } else {
101 return m_bigDelegateSize;
102 }
105void OrganicGrid::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
107 if (m_visibleItems.isEmpty()) {
108 *modelIndex = 0;
109 *yPos = 0;
110 } else {
111 *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
112 // We create stuff in a 6-module basis, so always return back
113 // the y position of the first item
114 const int firstModuleIndex = ((*modelIndex) / 6) * 6;
115 *yPos = positionForIndex(firstModuleIndex).y();
116 }
119void OrganicGrid::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
121 if (m_visibleItems.isEmpty()) {
122 *modelIndex = 0;
123 *yPos = 0;
124 } else {
125 *modelIndex = m_firstVisibleIndex - 1;
126 // We create stuff in a 6-module basis, so always return back
127 // the y position of the last item bottom
128 const int lastModuleIndex = ((*modelIndex) / 6) * 6 + 5;
129 *yPos = positionForIndex(lastModuleIndex).y();
130 *yPos += sizeForIndex(lastModuleIndex).height();
131 }
134void OrganicGrid::positionItem(int modelIndex, QQuickItem *item)
136 // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count() or the first
137 if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
138 m_visibleItems << item;
139 } else if (modelIndex == m_firstVisibleIndex - 1) {
140 m_firstVisibleIndex = modelIndex;
141 m_visibleItems.prepend(item);
142 } else if (modelIndex == 0) {
143 m_firstVisibleIndex = 0;
144 m_visibleItems << item;
145 } else {
146 qWarning() << "OrganicGrid::positionItem - Got unexpected modelIndex"
147 << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
148 return;
149 }
151 const QPointF pos = positionForIndex(modelIndex);
152 item->setPosition(pos);
154 // TODO Do we want warnings here in case the sizes are not the one we want like we have in the journals?
155 item->setSize(sizeForIndex(modelIndex));
158bool OrganicGrid::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
160 bool changed = false;
162 // As adding, we also remove in a 6-module basis
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());
167 changed = true;
168 m_firstVisibleIndex++;
170 lastModuleIndex = (m_firstVisibleIndex / 6) * 6 + 5;
171 removeIndex = positionForIndex(lastModuleIndex).y() + sizeForIndex(lastModuleIndex).height() < bufferFromY;
172 }
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());
178 changed = true;
180 firstModuleIndex = ((m_firstVisibleIndex + m_visibleItems.count() - 1) / 6) * 6;
181 removeIndex = positionForIndex(firstModuleIndex).y() > bufferToY;
182 }
184 if (m_visibleItems.isEmpty()) {
185 m_firstVisibleIndex = -1;
186 }
188 return changed;
191void OrganicGrid::cleanupExistingItems()
193 Q_FOREACH(QQuickItem *item, m_visibleItems)
194 releaseItem(item);
195 m_visibleItems.clear();
196 m_firstVisibleIndex = -1;
197 setImplicitHeightDirty();
200void OrganicGrid::doRelayout()
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 positionItem(i, item);
211 ++i;
212 }
215void 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);
219 }
222void OrganicGrid::calculateImplicitHeight()
224 const qreal moduleHeight = m_smallDelegateSize.height() + rowSpacing() + m_bigDelegateSize.height();
225 const int itemCount = !model() ? 0 : model()->rowCount();
226 const int fullRows = floor(itemCount / (m_numberOfModulesPerRow * 6));
227 const qreal fullRowsHeight = fullRows == 0 ? 0 : fullRows * moduleHeight + rowSpacing() * (fullRows - 1);
229 const int remainingItems = itemCount - fullRows * m_numberOfModulesPerRow * 6;
230 if (remainingItems == 0) {
231 setImplicitHeight(fullRowsHeight);
232 } else if (remainingItems <= 2) {
233 setImplicitHeight(fullRowsHeight + m_smallDelegateSize.height() + rowSpacing());
234 } else {
235 setImplicitHeight(fullRowsHeight + rowSpacing() + moduleHeight);
236 }
239#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
240void OrganicGrid::processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes)
241#else
242void OrganicGrid::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
243#endif
245#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
246 Q_FOREACH(const QQuickChangeSet::Remove &remove, removes) {
247#else
248 Q_FOREACH(const QQmlChangeSet::Remove &remove, removes) {
249#endif
250 for (int i = remove.count - 1; i >= 0; --i) {
251 const int indexToRemove = remove.index + i;
252 // We only support removing from the end
253 const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
254 if (indexToRemove == lastIndex) {
255 releaseItem(m_visibleItems.takeLast());
256 } else {
257 if (indexToRemove < lastIndex) {
258 qFatal("OrganicGrid only supports removal from the end of the model");
259 }
260 }
261 }
262 }
263 if (m_visibleItems.isEmpty()) {
264 m_firstVisibleIndex = -1;
265 }
266 setImplicitHeightDirty();