on master help sign in
unity8/plugins/Dash/organicgrid.cpp
259 lines 8.9 kB history raw
1/*
2 * Copyright (C) 2014, 2016 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 <math.h>
21#include <private/qquickitem_p.h>
23OrganicGrid::OrganicGrid()
24 : m_firstVisibleIndex(-1)
25 , m_numberOfModulesPerRow(-1)
29QSizeF OrganicGrid::smallDelegateSize() const
31 return m_smallDelegateSize;
34void OrganicGrid::setSmallDelegateSize(const QSizeF size)
36 if (m_smallDelegateSize != size) {
37 m_smallDelegateSize = size;
38 Q_EMIT smallDelegateSizeChanged();
40 if (isComponentComplete()) {
41 relayout();
42 }
43 }
46QSizeF OrganicGrid::bigDelegateSize() const
48 return m_bigDelegateSize;
51void OrganicGrid::setBigDelegateSize(const QSizeF size)
53 if (m_bigDelegateSize != size) {
54 m_bigDelegateSize = size;
55 Q_EMIT bigDelegateSizeChanged();
57 if (isComponentComplete()) {
58 relayout();
59 }
60 }
63QPointF OrganicGrid::positionForIndex(int modelIndex) const
65 const qreal moduleHeight = m_smallDelegateSize.height() + rowSpacing() + m_bigDelegateSize.height();
66 const qreal moduleWidth = m_smallDelegateSize.width() * 2 + columnSpacing() * 2 + m_bigDelegateSize.width();
67 const int itemsPerRow = m_numberOfModulesPerRow * 6;
68 const int rowIndex = floor(modelIndex / itemsPerRow);
69 const int columnIndex = floor((modelIndex - rowIndex * itemsPerRow) / 6);
71 qreal yPos = (moduleHeight + rowSpacing()) * rowIndex;
72 const int moduleIndex = modelIndex % 6;
73 if (moduleIndex == 2) {
74 yPos += m_smallDelegateSize.height() + rowSpacing();
75 } else if (moduleIndex == 3 || moduleIndex == 5) {
76 yPos += m_bigDelegateSize.height() + rowSpacing();
77 }
79 qreal xPos = (moduleWidth + columnSpacing()) * columnIndex;
80 if (moduleIndex == 1) {
81 xPos += m_smallDelegateSize.width() + columnSpacing();
82 } else if (moduleIndex == 3) {
83 xPos += m_bigDelegateSize.width() + columnSpacing();
84 } else if (moduleIndex == 4) {
85 xPos += (m_smallDelegateSize.width() + columnSpacing()) * 2;
86 } else if (moduleIndex == 5) {
87 xPos += m_bigDelegateSize.width() + m_smallDelegateSize.width() + columnSpacing() * 2;
88 }
90 return QPointF(xPos, yPos);
93QSizeF OrganicGrid::sizeForIndex(int modelIndex) const
95 const int moduleIndex = modelIndex % 6;
96 if (moduleIndex == 0 || moduleIndex == 1 || moduleIndex == 3 || moduleIndex == 5) {
97 return m_smallDelegateSize;
98 } else {
99 return m_bigDelegateSize;
100 }
103void OrganicGrid::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
105 if (m_visibleItems.isEmpty()) {
106 *modelIndex = 0;
107 *yPos = 0;
108 } else {
109 *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
110 // We create stuff in a 6-module basis, so always return back
111 // the y position of the first item
112 const int firstModuleIndex = ((*modelIndex) / 6) * 6;
113 *yPos = positionForIndex(firstModuleIndex).y();
114 }
117void OrganicGrid::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
119 if (m_visibleItems.isEmpty()) {
120 *modelIndex = 0;
121 *yPos = 0;
122 } else {
123 *modelIndex = m_firstVisibleIndex - 1;
124 // We create stuff in a 6-module basis, so always return back
125 // the y position of the last item bottom
126 const int lastModuleIndex = ((*modelIndex) / 6) * 6 + 5;
127 *yPos = positionForIndex(lastModuleIndex).y();
128 *yPos += sizeForIndex(lastModuleIndex).height();
129 }
132void OrganicGrid::addItemToView(int modelIndex, QQuickItem *item)
134 // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count() or the first
135 if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
136 m_visibleItems << item;
137 } else if (modelIndex == m_firstVisibleIndex - 1) {
138 m_firstVisibleIndex = modelIndex;
139 m_visibleItems.prepend(item);
140 } else if (modelIndex == 0) {
141 m_firstVisibleIndex = 0;
142 m_visibleItems << item;
143 } else {
144 qWarning() << "OrganicGrid::addItemToView - Got unexpected modelIndex"
145 << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
146 return;
147 }
149 const QPointF pos = positionForIndex(modelIndex);
150 item->setPosition(pos);
152 item->setSize(sizeForIndex(modelIndex));
155bool OrganicGrid::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
157 bool changed = false;
159 // As adding, we also remove in a 6-module basis
160 int lastModuleIndex = (m_firstVisibleIndex / 6) * 6 + 5;
161 bool removeIndex = positionForIndex(lastModuleIndex).y() + sizeForIndex(lastModuleIndex).height() < bufferFromY;
162 while (removeIndex && !m_visibleItems.isEmpty()) {
163 releaseItem(m_visibleItems.takeFirst());
164 changed = true;
165 m_firstVisibleIndex++;
167 lastModuleIndex = (m_firstVisibleIndex / 6) * 6 + 5;
168 removeIndex = positionForIndex(lastModuleIndex).y() + sizeForIndex(lastModuleIndex).height() < bufferFromY;
169 }
171 int firstModuleIndex = ((m_firstVisibleIndex + m_visibleItems.count() - 1) / 6) * 6;
172 removeIndex = positionForIndex(firstModuleIndex).y() > bufferToY;
173 while (removeIndex && !m_visibleItems.isEmpty()) {
174 releaseItem(m_visibleItems.takeLast());
175 changed = true;
177 firstModuleIndex = ((m_firstVisibleIndex + m_visibleItems.count() - 1) / 6) * 6;
178 removeIndex = positionForIndex(firstModuleIndex).y() > bufferToY;
179 }
181 if (m_visibleItems.isEmpty()) {
182 m_firstVisibleIndex = -1;
183 }
185 return changed;
188void OrganicGrid::cleanupExistingItems()
190 Q_FOREACH(QQuickItem *item, m_visibleItems)
191 releaseItem(item);
192 m_visibleItems.clear();
193 m_firstVisibleIndex = -1;
194 setImplicitHeightDirty();
197void OrganicGrid::doRelayout()
199 const qreal moduleWidth = m_smallDelegateSize.width() * 2 + columnSpacing() * 2 + m_bigDelegateSize.width();
200 m_numberOfModulesPerRow = floor((width() + columnSpacing()) / (moduleWidth + columnSpacing()));
201 m_numberOfModulesPerRow = qMax(1, m_numberOfModulesPerRow);
203 int i = m_firstVisibleIndex;
204 const QList<QQuickItem*> allItems = m_visibleItems;
205 m_visibleItems.clear();
206 Q_FOREACH(QQuickItem *item, allItems) {
207 addItemToView(i, item);
208 ++i;
209 }
212void OrganicGrid::updateItemCulling(qreal visibleFromY, qreal visibleToY)
214 Q_FOREACH(QQuickItem *item, m_visibleItems) {
215 QQuickItemPrivate::get(item)->setCulled(item->y() + item->height() <= visibleFromY || item->y() >= visibleToY);
216 }
219void OrganicGrid::calculateImplicitHeight()
221 const qreal moduleHeight = m_smallDelegateSize.height() + rowSpacing() + m_bigDelegateSize.height();
222 const int itemCount = !model() ? 0 : model()->rowCount();
223 const int itemsPerRow = m_numberOfModulesPerRow * 6;
224 const int fullRows = floor(itemCount / itemsPerRow);
225 const qreal fullRowsHeight = fullRows == 0 ? 0 : fullRows * moduleHeight + rowSpacing() * (fullRows - 1);
227 const int remainingItems = itemCount - fullRows * itemsPerRow;
228 if (remainingItems == 0) {
229 setImplicitHeight(fullRowsHeight);
230 } else if (remainingItems <= 2) {
231 setImplicitHeight(fullRowsHeight + m_smallDelegateSize.height() + rowSpacing());
232 } else {
233 setImplicitHeight(fullRowsHeight + rowSpacing() + moduleHeight);
234 }
237void OrganicGrid::processModelRemoves(const QVector<QQmlChangeSet::Change> &removes)
239 Q_FOREACH(const QQmlChangeSet::Change remove, removes) {
240 for (int i = remove.count - 1; i >= 0; --i) {
241 const int indexToRemove = remove.index + i;
242 // We only support removing from the end
243 const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
244 if (indexToRemove == lastIndex) {
245 releaseItem(m_visibleItems.takeLast());
246 } else {
247 if (indexToRemove < lastIndex) {
248 qDebug() << "OrganicGrid only supports removal from the end of the model, resetting instead";
249 cleanupExistingItems();
250 break;
251 }
252 }
253 }
254 }
255 if (m_visibleItems.isEmpty()) {
256 m_firstVisibleIndex = -1;
257 }
258 setImplicitHeightDirty();