on xenial help sign in
Organic Grid for the Dash View.
8c3fa90Albert Astals
plugins/DashViews/CMakeLists.txt+2−1
@@ -24,9 +24,10 @@ remove_definitions(-DQT_NO_KEYWORDS)
24set(QMLPLUGIN_SRC
24set(QMLPLUGIN_SRC
25 plugin.cpp
25 plugin.cpp
26 listviewwithpageheader.cpp
26 listviewwithpageheader.cpp
27− abstractjournal.cpp
27+ abstractdashview.cpp
28 verticaljournal.cpp
28 verticaljournal.cpp
29 horizontaljournal.cpp
29 horizontaljournal.cpp
30+ organicgrid.cpp
30 )
31 )
31
32
32add_library(DashViews-qml MODULE
33add_library(DashViews-qml MODULE
plugins/DashViews/abstractjournal.cpp → plugins/DashViews/abstractdashview.cpprenamed+33−33
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013, 2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,11 +14,11 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−#include "abstractjournal.h"
17+#include "abstractdashview.h"
18
18
19static const qreal bufferRatio = 0.5;
19static const qreal bufferRatio = 0.5;
20
20
21−AbstractJournal::AbstractJournal()
21+AbstractDashView::AbstractDashView()
22 : m_delegateModel(nullptr)
22 : m_delegateModel(nullptr)
23 , m_asyncRequestedIndex(-1)
23 , m_asyncRequestedIndex(-1)
24 , m_columnSpacing(0)
24 , m_columnSpacing(0)
@@ -35,12 +35,12 @@ AbstractJournal::AbstractJournal()
35 connect(this, SIGNAL(heightChanged()), this, SLOT(onHeightChanged()));
35 connect(this, SIGNAL(heightChanged()), this, SLOT(onHeightChanged()));
36}
36}
37
37
38−QAbstractItemModel *AbstractJournal::model() const
38+QAbstractItemModel *AbstractDashView::model() const
39{
39{
40 return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
40 return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
41}
41}
42
42
43−void AbstractJournal::setModel(QAbstractItemModel *model)
43+void AbstractDashView::setModel(QAbstractItemModel *model)
44{
44{
45 if (model != this->model()) {
45 if (model != this->model()) {
46 if (!m_delegateModel) {
46 if (!m_delegateModel) {
@@ -65,12 +65,12 @@ void AbstractJournal::setModel(QAbstractItemModel *model)
65 }
65 }
66}
66}
67
67
68−QQmlComponent *AbstractJournal::delegate() const
68+QQmlComponent *AbstractDashView::delegate() const
69{
69{
70 return m_delegateModel ? m_delegateModel->delegate() : nullptr;
70 return m_delegateModel ? m_delegateModel->delegate() : nullptr;
71}
71}
72
72
73−void AbstractJournal::setDelegate(QQmlComponent *delegate)
73+void AbstractDashView::setDelegate(QQmlComponent *delegate)
74{
74{
75 if (delegate != this->delegate()) {
75 if (delegate != this->delegate()) {
76 if (!m_delegateModel) {
76 if (!m_delegateModel) {
@@ -87,12 +87,12 @@ void AbstractJournal::setDelegate(QQmlComponent *delegate)
87 }
87 }
88}
88}
89
89
90−qreal AbstractJournal::columnSpacing() const
90+qreal AbstractDashView::columnSpacing() const
91{
91{
92 return m_columnSpacing;
92 return m_columnSpacing;
93}
93}
94
94
95−void AbstractJournal::setColumnSpacing(qreal columnSpacing)
95+void AbstractDashView::setColumnSpacing(qreal columnSpacing)
96{
96{
97 if (columnSpacing != m_columnSpacing) {
97 if (columnSpacing != m_columnSpacing) {
98 m_columnSpacing = columnSpacing;
98 m_columnSpacing = columnSpacing;
@@ -104,12 +104,12 @@ void AbstractJournal::setColumnSpacing(qreal columnSpacing)
104 }
104 }
105}
105}
106
106
107−qreal AbstractJournal::rowSpacing() const
107+qreal AbstractDashView::rowSpacing() const
108{
108{
109 return m_rowSpacing;
109 return m_rowSpacing;
110}
110}
111
111
112−void AbstractJournal::setRowSpacing(qreal rowSpacing)
112+void AbstractDashView::setRowSpacing(qreal rowSpacing)
113{
113{
114 if (rowSpacing != m_rowSpacing) {
114 if (rowSpacing != m_rowSpacing) {
115 m_rowSpacing = rowSpacing;
115 m_rowSpacing = rowSpacing;
@@ -121,12 +121,12 @@ void AbstractJournal::setRowSpacing(qreal rowSpacing)
121 }
121 }
122}
122}
123
123
124−qreal AbstractJournal::delegateCreationBegin() const
124+qreal AbstractDashView::delegateCreationBegin() const
125{
125{
126 return m_delegateCreationBegin;
126 return m_delegateCreationBegin;
127}
127}
128
128
129−void AbstractJournal::setDelegateCreationBegin(qreal begin)
129+void AbstractDashView::setDelegateCreationBegin(qreal begin)
130{
130{
131 m_delegateCreationBeginValid = true;
131 m_delegateCreationBeginValid = true;
132 if (m_delegateCreationBegin == begin)
132 if (m_delegateCreationBegin == begin)
@@ -138,7 +138,7 @@ void AbstractJournal::setDelegateCreationBegin(qreal begin)
138 emit delegateCreationBeginChanged();
138 emit delegateCreationBeginChanged();
139}
139}
140
140
141−void AbstractJournal::resetDelegateCreationBegin()
141+void AbstractDashView::resetDelegateCreationBegin()
142{
142{
143 m_delegateCreationBeginValid = false;
143 m_delegateCreationBeginValid = false;
144 if (m_delegateCreationBegin == 0)
144 if (m_delegateCreationBegin == 0)
@@ -150,12 +150,12 @@ void AbstractJournal::resetDelegateCreationBegin()
150 emit delegateCreationBeginChanged();
150 emit delegateCreationBeginChanged();
151}
151}
152
152
153−qreal AbstractJournal::delegateCreationEnd() const
153+qreal AbstractDashView::delegateCreationEnd() const
154{
154{
155 return m_delegateCreationEnd;
155 return m_delegateCreationEnd;
156}
156}
157
157
158−void AbstractJournal::setDelegateCreationEnd(qreal end)
158+void AbstractDashView::setDelegateCreationEnd(qreal end)
159{
159{
160 m_delegateCreationEndValid = true;
160 m_delegateCreationEndValid = true;
161 if (m_delegateCreationEnd == end)
161 if (m_delegateCreationEnd == end)
@@ -167,7 +167,7 @@ void AbstractJournal::setDelegateCreationEnd(qreal end)
167 emit delegateCreationEndChanged();
167 emit delegateCreationEndChanged();
168}
168}
169
169
170−void AbstractJournal::resetDelegateCreationEnd()
170+void AbstractDashView::resetDelegateCreationEnd()
171{
171{
172 m_delegateCreationEndValid = false;
172 m_delegateCreationEndValid = false;
173 if (m_delegateCreationEnd == 0)
173 if (m_delegateCreationEnd == 0)
@@ -179,7 +179,7 @@ void AbstractJournal::resetDelegateCreationEnd()
179 emit delegateCreationEndChanged();
179 emit delegateCreationEndChanged();
180}
180}
181
181
182−void AbstractJournal::createDelegateModel()
182+void AbstractDashView::createDelegateModel()
183{
183{
184#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
184#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
185 m_delegateModel = new QQuickVisualDataModel(qmlContext(this), this);
185 m_delegateModel = new QQuickVisualDataModel(qmlContext(this), this);
@@ -192,7 +192,7 @@ void AbstractJournal::createDelegateModel()
192 m_delegateModel->componentComplete();
192 m_delegateModel->componentComplete();
193}
193}
194
194
195−void AbstractJournal::refill()
195+void AbstractDashView::refill()
196{
196{
197 if (!isComponentComplete()) {
197 if (!isComponentComplete()) {
198 return;
198 return;
@@ -215,7 +215,7 @@ void AbstractJournal::refill()
215 }
215 }
216}
216}
217
217
218−bool AbstractJournal::addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous)
218+bool AbstractDashView::addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous)
219{
219{
220 if (!delegate())
220 if (!delegate())
221 return false;
221 return false;
@@ -247,7 +247,7 @@ bool AbstractJournal::addVisibleItems(qreal fillFromY, qreal fillToY, bool async
247 return changed;
247 return changed;
248}
248}
249
249
250−QQuickItem *AbstractJournal::createItem(int modelIndex, bool asynchronous)
250+QQuickItem *AbstractDashView::createItem(int modelIndex, bool asynchronous)
251{
251{
252 if (asynchronous && m_asyncRequestedIndex != -1)
252 if (asynchronous && m_asyncRequestedIndex != -1)
253 return nullptr;
253 return nullptr;
@@ -276,12 +276,12 @@ QQuickItem *AbstractJournal::createItem(int modelIndex, bool asynchronous)
276#endif
276#endif
277 return nullptr;
277 return nullptr;
278 } else {
278 } else {
279− positionItem(modelIndex, item);
279+ addItemToView(modelIndex, item);
280 return item;
280 return item;
281 }
281 }
282}
282}
283
283
284−void AbstractJournal::releaseItem(QQuickItem *item)
284+void AbstractDashView::releaseItem(QQuickItem *item)
285{
285{
286#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
286#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
287 QQuickVisualModel::ReleaseFlags flags = m_delegateModel->release(item);
287 QQuickVisualModel::ReleaseFlags flags = m_delegateModel->release(item);
@@ -294,20 +294,20 @@ void AbstractJournal::releaseItem(QQuickItem *item)
294 }
294 }
295}
295}
296
296
297−void AbstractJournal::setImplicitHeightDirty()
297+void AbstractDashView::setImplicitHeightDirty()
298{
298{
299 m_implicitHeightDirty = true;
299 m_implicitHeightDirty = true;
300}
300}
301
301
302#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
302#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
303−void AbstractJournal::itemCreated(int modelIndex, QQuickItem *item)
303+void AbstractDashView::itemCreated(int modelIndex, QQuickItem *item)
304{
304{
305#else
305#else
306−void AbstractJournal::itemCreated(int modelIndex, QObject *object)
306+void AbstractDashView::itemCreated(int modelIndex, QObject *object)
307{
307{
308 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
308 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
309 if (!item) {
309 if (!item) {
310− qWarning() << "AbstractJournal::itemCreated got a non item for index" << modelIndex;
310+ qWarning() << "AbstractDashView::itemCreated got a non item for index" << modelIndex;
311 return;
311 return;
312 }
312 }
313#endif
313#endif
@@ -326,9 +326,9 @@ void AbstractJournal::itemCreated(int modelIndex, QObject *object)
326}
326}
327
327
328#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
328#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
329−void AbstractJournal::onModelUpdated(const QQuickChangeSet &changeSet, bool reset)
329+void AbstractDashView::onModelUpdated(const QQuickChangeSet &changeSet, bool reset)
330#else
330#else
331−void AbstractJournal::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
331+void AbstractDashView::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
332#endif
332#endif
333{
333{
334 if (reset) {
334 if (reset) {
@@ -340,18 +340,18 @@ void AbstractJournal::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
340}
340}
341
341
342
342
343−void AbstractJournal::relayout()
343+void AbstractDashView::relayout()
344{
344{
345 m_needsRelayout = true;
345 m_needsRelayout = true;
346 polish();
346 polish();
347}
347}
348
348
349−void AbstractJournal::onHeightChanged()
349+void AbstractDashView::onHeightChanged()
350{
350{
351 polish();
351 polish();
352}
352}
353
353
354−void AbstractJournal::updatePolish()
354+void AbstractDashView::updatePolish()
355{
355{
356 if (!model())
356 if (!model())
357 return;
357 return;
@@ -375,7 +375,7 @@ void AbstractJournal::updatePolish()
375 }
375 }
376}
376}
377
377
378−void AbstractJournal::componentComplete()
378+void AbstractDashView::componentComplete()
379{
379{
380 if (m_delegateModel)
380 if (m_delegateModel)
381 m_delegateModel->componentComplete();
381 m_delegateModel->componentComplete();
plugins/DashViews/abstractjournal.h → plugins/DashViews/abstractdashview.hrenamed+7−6
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013, 2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,8 +14,8 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−#ifndef ABSTRACTJOURNAL_H
17+#ifndef ABSTRACTDASHVIEW_H
18−#define ABSTRACTJOURNAL_H
18+#define ABSTRACTDASHVIEW_H
19
19
20#include <QQuickItem>
20#include <QQuickItem>
21
21
@@ -31,7 +31,7 @@ class QQmlComponent;
31#endif
31#endif
32#pragma GCC diagnostic pop
32#pragma GCC diagnostic pop
33
33
34−class AbstractJournal : public QQuickItem
34+class AbstractDashView : public QQuickItem
35{
35{
36 Q_OBJECT
36 Q_OBJECT
37
37
@@ -50,9 +50,10 @@ class AbstractJournal : public QQuickItem
50
50
51friend class VerticalJournalTest;
51friend class VerticalJournalTest;
52friend class HorizontalJournalTest;
52friend class HorizontalJournalTest;
53+friend class OrganicGridTest;
53
54
54public:
55public:
55− AbstractJournal();
56+ AbstractDashView();
56
57
57 QAbstractItemModel *model() const;
58 QAbstractItemModel *model() const;
58 void setModel(QAbstractItemModel *model);
59 void setModel(QAbstractItemModel *model);
@@ -110,7 +111,7 @@ private:
110
111
111 virtual void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) = 0;
112 virtual void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) = 0;
112 virtual void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) = 0;
113 virtual void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) = 0;
113− virtual void positionItem(int modelIndex, QQuickItem *item) = 0;
114+ virtual void addItemToView(int modelIndex, QQuickItem *item) = 0;
114 virtual bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) = 0;
115 virtual bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) = 0;
115 virtual void cleanupExistingItems() = 0;
116 virtual void cleanupExistingItems() = 0;
116 virtual void doRelayout() = 0;
117 virtual void doRelayout() = 0;
plugins/DashViews/horizontaljournal.cpp+3−3
@@ -118,7 +118,7 @@ bool HorizontalJournal::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY
118 return changed;
118 return changed;
119}
119}
120
120
121−void HorizontalJournal::positionItem(int modelIndex, QQuickItem *item)
121+void HorizontalJournal::addItemToView(int modelIndex, QQuickItem *item)
122{
122{
123 if (item->height() != m_rowHeight) {
123 if (item->height() != m_rowHeight) {
124 qWarning() << "Item" << modelIndex << "height is not the one that the rowHeight mandates, resetting it";
124 qWarning() << "Item" << modelIndex << "height is not the one that the rowHeight mandates, resetting it";
@@ -159,7 +159,7 @@ void HorizontalJournal::positionItem(int modelIndex, QQuickItem *item)
159 m_firstVisibleIndex = modelIndex;
159 m_firstVisibleIndex = modelIndex;
160 m_visibleItems.prepend(item);
160 m_visibleItems.prepend(item);
161 } else {
161 } else {
162− qWarning() << "HorizontalJournal::positionItem - Got unexpected modelIndex"
162+ qWarning() << "HorizontalJournal::addItemToView - Got unexpected modelIndex"
163 << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
163 << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
164 }
164 }
165 }
165 }
@@ -232,7 +232,7 @@ void HorizontalJournal::doRelayout()
232 m_visibleItems.clear();
232 m_visibleItems.clear();
233 m_lastInRowIndexPosition.clear();
233 m_lastInRowIndexPosition.clear();
234 Q_FOREACH(QQuickItem *item, allItems) {
234 Q_FOREACH(QQuickItem *item, allItems) {
235− positionItem(i, item);
235+ addItemToView(i, item);
236 ++i;
236 ++i;
237 }
237 }
238 } else {
238 } else {
plugins/DashViews/horizontaljournal.h+3−3
@@ -17,7 +17,7 @@
17#ifndef HORIZONTALJOURNAL_H
17#ifndef HORIZONTALJOURNAL_H
18#define HORIZONTALJOURNAL_H
18#define HORIZONTALJOURNAL_H
19
19
20−#include "abstractjournal.h"
20+#include "abstractdashview.h"
21
21
22 /** A horizontal journal is a view that creates delegates
22 /** A horizontal journal is a view that creates delegates
23 * based on a model and layouts them one after the other
23 * based on a model and layouts them one after the other
@@ -26,7 +26,7 @@
26 *
26 *
27 * All delegates are forced to rowHeight if they don't have it.
27 * All delegates are forced to rowHeight if they don't have it.
28 */
28 */
29− class HorizontalJournal : public AbstractJournal
29+ class HorizontalJournal : public AbstractDashView
30{
30{
31 Q_OBJECT
31 Q_OBJECT
32
32
@@ -47,7 +47,7 @@ private:
47 void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
47 void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
48 void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
48 void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
49 bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
49 bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
50− void positionItem(int modelIndex, QQuickItem *item) override;
50+ void addItemToView(int modelIndex, QQuickItem *item) override;
51 void cleanupExistingItems() override;
51 void cleanupExistingItems() override;
52 void calculateImplicitHeight() override;
52 void calculateImplicitHeight() override;
53 void doRelayout() override;
53 void doRelayout() override;
plugins/DashViews/organicgrid.cppnew file+268
@@ -0,0 +1,268 @@
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+ */
16+
17+#include "organicgrid.h"
18+
19+#pragma GCC diagnostic push
20+#pragma GCC diagnostic ignored "-pedantic"
21+#include <private/qquickitem_p.h>
22+#pragma GCC diagnostic pop
23+
24+static const qreal bufferRatio = 0.5;
25+
26+OrganicGrid::OrganicGrid()
27+ : m_firstVisibleIndex(-1)
28+ , m_numberOfModulesPerRow(-1)
29+{
30+}
31+
32+QSizeF OrganicGrid::smallDelegateSize() const
33+{
34+ return m_smallDelegateSize;
35+}
36+
37+void OrganicGrid::setSmallDelegateSize(const QSizeF &size)
38+{
39+ if (m_smallDelegateSize != size) {
40+ m_smallDelegateSize = size;
41+ Q_EMIT smallDelegateSizeChanged();
42+
43+ if (isComponentComplete()) {
44+ relayout();
45+ }
46+ }
47+}
48+
49+QSizeF OrganicGrid::bigDelegateSize() const
50+{
51+ return m_bigDelegateSize;
52+}
53+
54+void OrganicGrid::setBigDelegateSize(const QSizeF &size)
55+{
56+ if (m_bigDelegateSize != size) {
57+ m_bigDelegateSize = size;
58+ Q_EMIT bigDelegateSizeChanged();
59+
60+ if (isComponentComplete()) {
61+ relayout();
62+ }
63+ }
64+}
65+
66+QPointF OrganicGrid::positionForIndex(int modelIndex) const
67+{
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);
73+
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();
80+ }
81+
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;
91+ }
92+
93+ return QPointF(xPos, yPos);
94+}
95+
96+QSizeF OrganicGrid::sizeForIndex(int modelIndex) const
97+{
98+ const int moduleIndex = modelIndex % 6;
99+ if (moduleIndex == 0 || moduleIndex == 1 || moduleIndex == 3 || moduleIndex == 5) {
100+ return m_smallDelegateSize;
101+ } else {
102+ return m_bigDelegateSize;
103+ }
104+}
105+
106+void OrganicGrid::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
107+{
108+ if (m_visibleItems.isEmpty()) {
109+ *modelIndex = 0;
110+ *yPos = 0;
111+ } else {
112+ *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
113+ // We create stuff in a 6-module basis, so always return back
114+ // the y position of the first item
115+ const int firstModuleIndex = ((*modelIndex) / 6) * 6;
116+ *yPos = positionForIndex(firstModuleIndex).y();
117+ }
118+}
119+
120+void OrganicGrid::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
121+{
122+ if (m_visibleItems.isEmpty()) {
123+ *modelIndex = 0;
124+ *yPos = 0;
125+ } else {
126+ *modelIndex = m_firstVisibleIndex - 1;
127+ // We create stuff in a 6-module basis, so always return back
128+ // the y position of the last item bottom
129+ const int lastModuleIndex = ((*modelIndex) / 6) * 6 + 5;
130+ *yPos = positionForIndex(lastModuleIndex).y();
131+ *yPos += sizeForIndex(lastModuleIndex).height();
132+ }
133+}
134+
135+void OrganicGrid::addItemToView(int modelIndex, QQuickItem *item)
136+{
137+ // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count() or the first
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;
146+ } else {
147+ qWarning() << "OrganicGrid::addItemToView - Got unexpected modelIndex"
148+ << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
149+ return;
150+ }
151+
152+ const QPointF pos = positionForIndex(modelIndex);
153+ item->setPosition(pos);
154+
155+ item->setSize(sizeForIndex(modelIndex));
156+}
157+
158+bool OrganicGrid::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
159+{
160+ bool changed = false;
161+
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++;
169+
170+ lastModuleIndex = (m_firstVisibleIndex / 6) * 6 + 5;
171+ removeIndex = positionForIndex(lastModuleIndex).y() + sizeForIndex(lastModuleIndex).height() < bufferFromY;
172+ }
173+
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;
179+
180+ firstModuleIndex = ((m_firstVisibleIndex + m_visibleItems.count() - 1) / 6) * 6;
181+ removeIndex = positionForIndex(firstModuleIndex).y() > bufferToY;
182+ }
183+
184+ if (m_visibleItems.isEmpty()) {
185+ m_firstVisibleIndex = -1;
186+ }
187+
188+ return changed;
189+}
190+
191+void OrganicGrid::cleanupExistingItems()
192+{
193+ Q_FOREACH(QQuickItem *item, m_visibleItems)
194+ releaseItem(item);
195+ m_visibleItems.clear();
196+ m_firstVisibleIndex = -1;
197+ setImplicitHeightDirty();
198+}
199+
200+void OrganicGrid::doRelayout()
201+{
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);
205+
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);
211+ ++i;
212+ }
213+}
214+
215+void OrganicGrid::updateItemCulling(qreal visibleFromY, qreal visibleToY)
216+{
217+ Q_FOREACH(QQuickItem *item, m_visibleItems) {
218+ QQuickItemPrivate::get(item)->setCulled(item->y() + item->height() <= visibleFromY || item->y() >= visibleToY);
219+ }
220+}
221+
222+void OrganicGrid::calculateImplicitHeight()
223+{
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);
229+
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());
235+ } else {
236+ setImplicitHeight(fullRowsHeight + rowSpacing() + moduleHeight);
237+ }
238+}
239+
240+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
241+void OrganicGrid::processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes)
242+#else
243+void OrganicGrid::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
244+#endif
245+{
246+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
247+ Q_FOREACH(const QQuickChangeSet::Remove &remove, removes) {
248+#else
249+ Q_FOREACH(const QQmlChangeSet::Remove &remove, removes) {
250+#endif
251+ for (int i = remove.count - 1; i >= 0; --i) {
252+ const int indexToRemove = remove.index + i;
253+ // We only support removing from the end
254+ const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
255+ if (indexToRemove == lastIndex) {
256+ releaseItem(m_visibleItems.takeLast());
257+ } else {
258+ if (indexToRemove < lastIndex) {
259+ qFatal("OrganicGrid only supports removal from the end of the model");
260+ }
261+ }
262+ }
263+ }
264+ if (m_visibleItems.isEmpty()) {
265+ m_firstVisibleIndex = -1;
266+ }
267+ setImplicitHeightDirty();
268+}
plugins/DashViews/organicgrid.hnew file+95
@@ -0,0 +1,95 @@
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+ */
16+
17+#ifndef ORGANICGRID_H
18+#define ORGANICGRID_H
19+
20+#include "abstractdashview.h"
21+
22+ /** An Organic Grid is is a view that creates delegates
23+ * based on a model and layouts them in groups of six items (called module).
24+ *
25+ * In each module there are 4 items that are forced to the small delegate size
26+ * and two that are forced to the big delegate size.
27+ *
28+ * Example:
29+ *
30+ * +---+ +---+ +-----+
31+ * | 1 | | 2 | | |
32+ * +---+ +---+ | 5 |
33+ * +-----+ | |
34+ * | | +-----+
35+ * | 3 | +---+ +---+
36+ * | | | 4 | | 6 |
37+ * +-----+ +---+ +---+
38+ *
39+ * Modules are positioned one after the other in a grid like fashion, i.e.
40+ *
41+ * +---+ +---+
42+ * | 1 | | 2 |
43+ * +---+ +---+
44+ * +---+ +---+
45+ * | 3 | | 4 |
46+ * +---+ +---+
47+ */
48+
49+class OrganicGrid : public AbstractDashView
50+{
51+ Q_OBJECT
52+
53+ Q_PROPERTY(QSizeF smallDelegateSize READ smallDelegateSize WRITE setSmallDelegateSize NOTIFY smallDelegateSizeChanged)
54+ Q_PROPERTY(QSizeF bigDelegateSize READ bigDelegateSize WRITE setBigDelegateSize NOTIFY bigDelegateSizeChanged)
55+
56+friend class OrganicGridTest;
57+
58+public:
59+ OrganicGrid();
60+
61+ QSizeF smallDelegateSize() const;
62+ void setSmallDelegateSize(const QSizeF &size);
63+
64+ QSizeF bigDelegateSize() const;
65+ void setBigDelegateSize(const QSizeF &size);
66+
67+Q_SIGNALS:
68+ void smallDelegateSizeChanged();
69+ void bigDelegateSizeChanged();
70+private:
71+ QPointF positionForIndex(int modelIndex) const;
72+ QSizeF sizeForIndex(int modelIndex) const;
73+
74+ void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
75+ void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
76+ void addItemToView(int modelIndex, QQuickItem *item) override;
77+ bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
78+ void cleanupExistingItems() override;
79+ void doRelayout() override;
80+ void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
81+ void calculateImplicitHeight() override;
82+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
83+ void processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes) override;
84+#else
85+ void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) override;
86+#endif
87+
88+ QSizeF m_smallDelegateSize;
89+ QSizeF m_bigDelegateSize;
90+ int m_firstVisibleIndex;
91+ int m_numberOfModulesPerRow;
92+ QList<QQuickItem*> m_visibleItems;
93+};
94+
95+#endif
plugins/DashViews/plugin.cpp+2
@@ -19,6 +19,7 @@
19
19
20#include "horizontaljournal.h"
20#include "horizontaljournal.h"
21#include "listviewwithpageheader.h"
21#include "listviewwithpageheader.h"
22+#include "organicgrid.h"
22#include "verticaljournal.h"
23#include "verticaljournal.h"
23
24
24#include <QAbstractItemModel>
25#include <QAbstractItemModel>
@@ -29,5 +30,6 @@ void DashViewsPlugin::registerTypes(const char *uri)
29 qmlRegisterType<QAbstractItemModel>();
30 qmlRegisterType<QAbstractItemModel>();
30 qmlRegisterType<HorizontalJournal>(uri, 0, 1, "HorizontalJournal");
31 qmlRegisterType<HorizontalJournal>(uri, 0, 1, "HorizontalJournal");
31 qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
32 qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
33+ qmlRegisterType<OrganicGrid>(uri, 0, 1, "OrganicGrid");
32 qmlRegisterType<VerticalJournal>(uri, 0, 1, "VerticalJournal");
34 qmlRegisterType<VerticalJournal>(uri, 0, 1, "VerticalJournal");
33}
35}
plugins/DashViews/verticaljournal.cpp+2−2
@@ -130,7 +130,7 @@ bool VerticalJournal::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
130 return changed;
130 return changed;
131}
131}
132
132
133−void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
133+void VerticalJournal::addItemToView(int modelIndex, QQuickItem *item)
134{
134{
135 if (item->width() != m_columnWidth) {
135 if (item->width() != m_columnWidth) {
136 qWarning() << "Item" << modelIndex << "width is not the one that the columnWidth mandates, resetting it";
136 qWarning() << "Item" << modelIndex << "width is not the one that the columnWidth mandates, resetting it";
@@ -220,7 +220,7 @@ void VerticalJournal::doRelayout()
220 if (!allItems.isEmpty()) {
220 if (!allItems.isEmpty()) {
221 if (allItems.first().m_modelIndex == 0) {
221 if (allItems.first().m_modelIndex == 0) {
222 Q_FOREACH(const ViewItem &item, allItems)
222 Q_FOREACH(const ViewItem &item, allItems)
223− positionItem(item.m_modelIndex, item.m_item);
223+ addItemToView(item.m_modelIndex, item.m_item);
224 } else {
224 } else {
225 Q_FOREACH(const ViewItem &item, allItems)
225 Q_FOREACH(const ViewItem &item, allItems)
226 releaseItem(item.m_item);
226 releaseItem(item.m_item);
plugins/DashViews/verticaljournal.h+3−3
@@ -17,7 +17,7 @@
17#ifndef VERTICALJOURNAL_H
17#ifndef VERTICALJOURNAL_H
18#define VERTICALJOURNAL_H
18#define VERTICALJOURNAL_H
19
19
20−#include "abstractjournal.h"
20+#include "abstractdashview.h"
21
21
22 /** A vertical journal is a view that creates delegates
22 /** A vertical journal is a view that creates delegates
23 * based on a model and layouts them in columns following
23 * based on a model and layouts them in columns following
@@ -51,7 +51,7 @@
51 *
51 *
52 */
52 */
53
53
54−class VerticalJournal : public AbstractJournal
54+class VerticalJournal : public AbstractDashView
55{
55{
56 Q_OBJECT
56 Q_OBJECT
57
57
@@ -85,7 +85,7 @@ private:
85 void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
85 void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
86 void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
86 void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
87 bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
87 bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
88− void positionItem(int modelIndex, QQuickItem *item) override;
88+ void addItemToView(int modelIndex, QQuickItem *item) override;
89 void cleanupExistingItems() override;
89 void cleanupExistingItems() override;
90 void calculateImplicitHeight() override;
90 void calculateImplicitHeight() override;
91 void doRelayout() override;
91 void doRelayout() override;
tests/plugins/DashViews/CMakeLists.txt+12−15
@@ -40,22 +40,17 @@ qt5_use_modules(listviewwithpageheadertestsectionexternalmodelExec Test Core Qml
40target_link_libraries(listviewwithpageheadertestsectionexternalmodelExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
40target_link_libraries(listviewwithpageheadertestsectionexternalmodelExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
41add_custom_target(testlistviewwithpageheadersectionexternalmodel ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestsectionexternalmodelExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheadersectionexternalmodel.xml,xunitxml -o -,txt)
41add_custom_target(testlistviewwithpageheadersectionexternalmodel ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestsectionexternalmodelExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheadersectionexternalmodel.xml,xunitxml -o -,txt)
42
42
43−macro(add_journal_try_test FILENAME)
43+macro(add_dashview_try_test FILENAME TESTNAME)
44− # Capitalite the first letter for the test/try name targets
45− string(SUBSTRING ${FILENAME} 0 1 FIRST_LETTER)
46− string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
47− string(REGEX REPLACE "^.(.*)" "${FIRST_LETTER}\\1" CUSTOM_TARGET_NAME "${FILENAME}")
48−
49 add_executable(${FILENAME}testExec
44 add_executable(${FILENAME}testExec
50 ${FILENAME}test.cpp
45 ${FILENAME}test.cpp
51 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
46 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
52− ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractjournal.cpp
47+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractdashview.cpp
53 )
48 )
54 qt5_use_modules(${FILENAME}testExec Test Core Qml)
49 qt5_use_modules(${FILENAME}testExec Test Core Qml)
55 target_link_libraries(${FILENAME}testExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
50 target_link_libraries(${FILENAME}testExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
56− add_custom_target(test${CUSTOM_TARGET_NAME}
51+ add_custom_target(test${TESTNAME}
57 ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}testExec
52 ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}testExec
58− -o ${CMAKE_BINARY_DIR}/test${CUSTOM_TARGET_NAME}.xml,xunitxml
53+ -o ${CMAKE_BINARY_DIR}/test${TESTNAME}.xml,xunitxml
59 -o -,txt
54 -o -,txt
60 DEPENDS ${FILENAME}testExec DashViews-qml
55 DEPENDS ${FILENAME}testExec DashViews-qml
61 )
56 )
@@ -63,18 +58,19 @@ macro(add_journal_try_test FILENAME)
63 add_executable(${FILENAME}tryExec
58 add_executable(${FILENAME}tryExec
64 ${FILENAME}try.cpp
59 ${FILENAME}try.cpp
65 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
60 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
66− ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractjournal.cpp
61+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractdashview.cpp
67 )
62 )
68 qt5_use_modules(${FILENAME}tryExec Test Core Qml)
63 qt5_use_modules(${FILENAME}tryExec Test Core Qml)
69 target_link_libraries(${FILENAME}tryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
64 target_link_libraries(${FILENAME}tryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
70− add_custom_target(try${CUSTOM_TARGET_NAME}
65+ add_custom_target(try${TESTNAME}
71 ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}tryExec
66 ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}tryExec
72 DEPENDS ${FILENAME}tryExec DashViews-qml
67 DEPENDS ${FILENAME}tryExec DashViews-qml
73 )
68 )
74endmacro()
69endmacro()
75
70
76−add_journal_try_test(verticaljournal)
71+add_dashview_try_test(verticaljournal VerticalJournal)
77−add_journal_try_test(horizontaljournal)
72+add_dashview_try_test(horizontaljournal HorizontalJournal)
73+add_dashview_try_test(organicgrid OrganicGrid)
78
74
79set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
75set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
80add_qml_test(. listviewwithpageheaderqml IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
76add_qml_test(. listviewwithpageheaderqml IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
@@ -83,5 +79,6 @@ add_dependencies(qmluitests testlistviewwithpageheader)
83add_dependencies(qmluitests testlistviewwithpageheadersection)
79add_dependencies(qmluitests testlistviewwithpageheadersection)
84add_dependencies(qmluitests testlistviewwithpageheadersectionexternalmodel)
80add_dependencies(qmluitests testlistviewwithpageheadersectionexternalmodel)
85add_dependencies(qmluitests testlistviewwithpageheaderqml)
81add_dependencies(qmluitests testlistviewwithpageheaderqml)
86−add_dependencies(qmluitests testVerticaljournal)
82+add_dependencies(qmluitests testVerticalJournal)
87−add_dependencies(qmluitests testHorizontaljournal)
83+add_dependencies(qmluitests testHorizontalJournal)
84+add_dependencies(qmluitests testOrganicGrid)
tests/plugins/DashViews/organicgridtest.cppnew file+531
@@ -0,0 +1,531 @@
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+ */
16+
17+#include <QQuickView>
18+#include <QtTestGui>
19+#include <QDebug>
20+#include <QGuiApplication>
21+#include <QQuickView>
22+#include <QtQml/qqml.h>
23+#include <QStringListModel>
24+#include <QQmlContext>
25+#include <QQmlEngine>
26+#pragma GCC diagnostic push
27+#pragma GCC diagnostic ignored "-pedantic"
28+#include <private/qquickitem_p.h>
29+#pragma GCC diagnostic pop
30+
31+#include "organicgrid.h"
32+
33+class DummyModel : public QAbstractListModel {
34+public:
35+ DummyModel() : m_count(0) {}
36+
37+ int rowCount(const QModelIndex & /*parent*/) const
38+ {
39+ return m_count;
40+ }
41+
42+ QVariant data(const QModelIndex & /*index*/, int /*role*/) const
43+ {
44+ return QVariant();
45+ }
46+
47+ void setCount(int count)
48+ {
49+ beginResetModel();
50+ m_count = count;
51+ endResetModel();
52+ }
53+
54+ void addItem()
55+ {
56+ beginInsertRows(QModelIndex(), m_count, m_count);
57+ m_count++;
58+ endInsertRows();
59+ }
60+
61+ void removeLast()
62+ {
63+ beginRemoveRows(QModelIndex(), m_count - 1, m_count - 1);
64+ m_count--;
65+ endRemoveRows();
66+ }
67+
68+private:
69+ int m_count;
70+};
71+
72+class OrganicGridTest : public QObject
73+{
74+ Q_OBJECT
75+
76+private:
77+ void verifyItem(const QQuickItem *item, int modelIndex, qreal x, qreal y, bool visible)
78+ {
79+ QTRY_COMPARE(item->x(), x);
80+ QTRY_COMPARE(item->y(), y);
81+ QTRY_COMPARE(QQuickItemPrivate::get(item)->culled, !visible);
82+ const int inModuleIndex = modelIndex % 6;
83+ if (inModuleIndex == 0 || inModuleIndex == 1 || inModuleIndex == 3 || inModuleIndex == 5) {
84+ QTRY_COMPARE(item->height(), grid->smallDelegateSize().height());
85+ QTRY_COMPARE(item->width(), grid->smallDelegateSize().width());
86+ } else {
87+ QTRY_COMPARE(item->height(), grid->bigDelegateSize().height());
88+ QTRY_COMPARE(item->width(), grid->bigDelegateSize().width());
89+ }
90+ }
91+
92+ void checkInitialPositions()
93+ {
94+ QTRY_COMPARE(grid->m_visibleItems.count(), 18);
95+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
96+ QTRY_COMPARE(grid->m_numberOfModulesPerRow, 1);
97+ verifyItem(grid->m_visibleItems[ 0], 0, 0, 0, true);
98+ verifyItem(grid->m_visibleItems[ 1], 1, 100, 0, true);
99+ verifyItem(grid->m_visibleItems[ 2], 2, 0, 100, true);
100+ verifyItem(grid->m_visibleItems[ 3], 3, 190, 190, true);
101+ verifyItem(grid->m_visibleItems[ 4], 4, 200, 0, true);
102+ verifyItem(grid->m_visibleItems[ 5], 5, 290, 190, true);
103+ verifyItem(grid->m_visibleItems[ 6], 6, 0, 290, true);
104+ verifyItem(grid->m_visibleItems[ 7], 7, 100, 290, true);
105+ verifyItem(grid->m_visibleItems[ 8], 8, 0, 390, true);
106+ verifyItem(grid->m_visibleItems[ 9], 9, 190, 480, false);
107+ verifyItem(grid->m_visibleItems[10], 10, 200, 290, true);
108+ verifyItem(grid->m_visibleItems[11], 11, 290, 480, false);
109+ verifyItem(grid->m_visibleItems[12], 12, 0, 580, false);
110+ verifyItem(grid->m_visibleItems[13], 13, 100, 580, false);
111+ verifyItem(grid->m_visibleItems[14], 14, 0, 680, false);
112+ verifyItem(grid->m_visibleItems[15], 15, 190, 770, false);
113+ verifyItem(grid->m_visibleItems[16], 16, 200, 580, false);
114+ verifyItem(grid->m_visibleItems[17], 17, 290, 770, false);
115+ checkImplicitHeight(1150);
116+ }
117+
118+ void checkImplicitHeight(qreal implicitHeight)
119+ {
120+ QTRY_COMPARE(grid->m_implicitHeightDirty, false);
121+ QCOMPARE(grid->implicitHeight(), implicitHeight);
122+ }
123+
124+private Q_SLOTS:
125+ void init()
126+ {
127+ view = new QQuickView();
128+ view->setResizeMode(QQuickView::SizeRootObjectToView);
129+ view->engine()->addImportPath(BUILT_PLUGINS_DIR);
130+
131+ model = new DummyModel();
132+ model->setCount(21);
133+
134+ view->setSource(QUrl::fromLocalFile(DASHVIEWSTEST_FOLDER "/organicgridtest.qml"));
135+
136+ view->show();
137+ view->resize(470, 400);
138+ QTest::qWaitForWindowExposed(view);
139+
140+ grid = dynamic_cast<OrganicGrid*>(view->rootObject()->findChild<QObject*>("grid"));
141+ grid->setModel(model);
142+
143+ checkInitialPositions();
144+ }
145+
146+ void cleanup()
147+ {
148+ delete view;
149+ delete model;
150+ }
151+
152+ void testInitialPosition()
153+ {
154+ }
155+
156+ void testWidthResize()
157+ {
158+ view->resize(770, 400);
159+ QTRY_COMPARE(grid->width(), 770.);
160+
161+ QTRY_COMPARE(grid->m_visibleItems.count(), 21);
162+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
163+ QTRY_COMPARE(grid->m_numberOfModulesPerRow, 2);
164+ verifyItem(grid->m_visibleItems[ 0], 0, 0, 0, true);
165+ verifyItem(grid->m_visibleItems[ 1], 1, 100, 0, true);
166+ verifyItem(grid->m_visibleItems[ 2], 2, 0, 100, true);
167+ verifyItem(grid->m_visibleItems[ 3], 3, 190, 190, true);
168+ verifyItem(grid->m_visibleItems[ 4], 4, 200, 0, true);
169+ verifyItem(grid->m_visibleItems[ 5], 5, 290, 190, true);
170+
171+ verifyItem(grid->m_visibleItems[ 6], 6, 390, 0, true);
172+ verifyItem(grid->m_visibleItems[ 7], 7, 490, 0, true);
173+ verifyItem(grid->m_visibleItems[ 8], 8, 390, 100, true);
174+ verifyItem(grid->m_visibleItems[ 9], 9, 580, 190, true);
175+ verifyItem(grid->m_visibleItems[10], 10, 590, 0, true);
176+ verifyItem(grid->m_visibleItems[11], 11, 680, 190, true);
177+
178+ verifyItem(grid->m_visibleItems[12], 12, 0, 290, true);
179+ verifyItem(grid->m_visibleItems[13], 13, 100, 290, true);
180+ verifyItem(grid->m_visibleItems[14], 14, 0, 390, true);
181+ verifyItem(grid->m_visibleItems[15], 15, 190, 480, false);
182+ verifyItem(grid->m_visibleItems[16], 16, 200, 290, true);
183+ verifyItem(grid->m_visibleItems[17], 17, 290, 480, false);
184+
185+ verifyItem(grid->m_visibleItems[18], 18, 390, 290, true);
186+ verifyItem(grid->m_visibleItems[19], 19, 490, 290, true);
187+ verifyItem(grid->m_visibleItems[20], 20, 390, 390, true);
188+
189+ checkImplicitHeight(570);
190+
191+ view->resize(769, 400);
192+
193+ // This is exactly the same block as the first again
194+ checkInitialPositions();
195+ }
196+
197+ void testColumnSpacing()
198+ {
199+ grid->setColumnSpacing(11);
200+
201+ QTRY_COMPARE(grid->m_needsRelayout, false);
202+ QTRY_COMPARE(grid->m_visibleItems.count(), 18);
203+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
204+ QTRY_COMPARE(grid->m_numberOfModulesPerRow, 1);
205+ verifyItem(grid->m_visibleItems[ 0], 0, 0, 0, true);
206+ verifyItem(grid->m_visibleItems[ 1], 1, 101, 0, true);
207+ verifyItem(grid->m_visibleItems[ 2], 2, 0, 100, true);
208+ verifyItem(grid->m_visibleItems[ 3], 3, 191, 190, true);
209+ verifyItem(grid->m_visibleItems[ 4], 4, 202, 0, true);
210+ verifyItem(grid->m_visibleItems[ 5], 5, 292, 190, true);
211+ verifyItem(grid->m_visibleItems[ 6], 6, 0, 290, true);
212+ verifyItem(grid->m_visibleItems[ 7], 7, 101, 290, true);
213+ verifyItem(grid->m_visibleItems[ 8], 8, 0, 390, true);
214+ verifyItem(grid->m_visibleItems[ 9], 9, 191, 480, false);
215+ verifyItem(grid->m_visibleItems[10], 10, 202, 290, true);
216+ verifyItem(grid->m_visibleItems[11], 11, 292, 480, false);
217+ verifyItem(grid->m_visibleItems[12], 12, 0, 580, false);
218+ verifyItem(grid->m_visibleItems[13], 13, 101, 580, false);
219+ verifyItem(grid->m_visibleItems[14], 14, 0, 680, false);
220+ verifyItem(grid->m_visibleItems[15], 15, 191, 770, false);
221+ verifyItem(grid->m_visibleItems[16], 16, 202, 580, false);
222+ verifyItem(grid->m_visibleItems[17], 17, 292, 770, false);
223+ checkImplicitHeight(1150);
224+
225+ grid->setColumnSpacing(10);
226+ QTRY_COMPARE(grid->m_needsRelayout, false);
227+
228+ // This is exactly the same block as the first again
229+ checkInitialPositions();
230+ }
231+
232+ void testRowSpacing()
233+ {
234+ grid->setRowSpacing(11);
235+
236+ QTRY_COMPARE(grid->m_needsRelayout, false);
237+ QTRY_COMPARE(grid->m_visibleItems.count(), 18);
238+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
239+ QTRY_COMPARE(grid->m_numberOfModulesPerRow, 1);
240+ verifyItem(grid->m_visibleItems[ 0], 0, 0, 0, true);
241+ verifyItem(grid->m_visibleItems[ 1], 1, 100, 0, true);
242+ verifyItem(grid->m_visibleItems[ 2], 2, 0, 101, true);
243+ verifyItem(grid->m_visibleItems[ 3], 3, 190, 191, true);
244+ verifyItem(grid->m_visibleItems[ 4], 4, 200, 0, true);
245+ verifyItem(grid->m_visibleItems[ 5], 5, 290, 191, true);
246+ verifyItem(grid->m_visibleItems[ 6], 6, 0, 292, true);
247+ verifyItem(grid->m_visibleItems[ 7], 7, 100, 292, true);
248+ verifyItem(grid->m_visibleItems[ 8], 8, 0, 393, true);
249+ verifyItem(grid->m_visibleItems[ 9], 9, 190, 483, false);
250+ verifyItem(grid->m_visibleItems[10], 10, 200, 292, true);
251+ verifyItem(grid->m_visibleItems[11], 11, 290, 483, false);
252+ verifyItem(grid->m_visibleItems[12], 12, 0, 584, false);
253+ verifyItem(grid->m_visibleItems[13], 13, 100, 584, false);
254+ verifyItem(grid->m_visibleItems[14], 14, 0, 685, false);
255+ verifyItem(grid->m_visibleItems[15], 15, 190, 775, false);
256+ verifyItem(grid->m_visibleItems[16], 16, 200, 584, false);
257+ verifyItem(grid->m_visibleItems[17], 17, 290, 775, false);
258+ checkImplicitHeight(1157);
259+
260+ grid->setRowSpacing(10);
261+ QTRY_COMPARE(grid->m_needsRelayout, false);
262+
263+ // This is exactly the same block as the first again
264+ checkInitialPositions();
265+ }
266+
267+ void testDelegateSizeChange()
268+ {
269+ grid->setSmallDelegateSize(QSizeF(30, 30));
270+ grid->setBigDelegateSize(QSizeF(50, 50));
271+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
272+ QTRY_COMPARE(grid->m_visibleItems.count(), 21);
273+ verifyItem(grid->m_visibleItems[ 0], 0, 0, 0, true);
274+ verifyItem(grid->m_visibleItems[ 1], 1, 40, 0, true);
275+ verifyItem(grid->m_visibleItems[ 2], 2, 0, 40, true);
276+ verifyItem(grid->m_visibleItems[ 3], 3, 60, 60, true);
277+ verifyItem(grid->m_visibleItems[ 4], 4, 80, 0, true);
278+ verifyItem(grid->m_visibleItems[ 5], 5, 100, 60, true);
279+
280+ verifyItem(grid->m_visibleItems[ 6], 6, 140, 0, true);
281+ verifyItem(grid->m_visibleItems[ 7], 7, 180, 0, true);
282+ verifyItem(grid->m_visibleItems[ 8], 8, 140, 40, true);
283+ verifyItem(grid->m_visibleItems[ 9], 9, 200, 60, true);
284+ verifyItem(grid->m_visibleItems[10], 10, 220, 0, true);
285+ verifyItem(grid->m_visibleItems[11], 11, 240, 60, true);
286+
287+ verifyItem(grid->m_visibleItems[12], 12, 280, 0, true);
288+ verifyItem(grid->m_visibleItems[13], 13, 320, 0, true);
289+ verifyItem(grid->m_visibleItems[14], 14, 280, 40, true);
290+ verifyItem(grid->m_visibleItems[15], 15, 340, 60, true);
291+ verifyItem(grid->m_visibleItems[16], 16, 360, 0, true);
292+ verifyItem(grid->m_visibleItems[17], 17, 380, 60, true);
293+
294+ verifyItem(grid->m_visibleItems[18], 18, 0, 100, true);
295+ verifyItem(grid->m_visibleItems[19], 19, 40, 100, true);
296+ verifyItem(grid->m_visibleItems[20], 20, 0, 140, true);
297+ checkImplicitHeight(190);
298+
299+ grid->setSmallDelegateSize(QSizeF(90, 90));
300+ grid->setBigDelegateSize(QSizeF(180, 180));
301+
302+ // This is exactly the same block as the first again
303+ checkInitialPositions();
304+ }
305+
306+ void testChangeModel()
307+ {
308+ DummyModel *model2 = new DummyModel();
309+ model2->setCount(6);
310+ grid->setModel(model2);
311+ delete model;
312+ model = model2;
313+
314+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
315+ QTRY_COMPARE(grid->m_visibleItems.count(), 6);
316+ verifyItem(grid->m_visibleItems[ 0], 0, 0, 0, true);
317+ verifyItem(grid->m_visibleItems[ 1], 1, 100, 0, true);
318+ verifyItem(grid->m_visibleItems[ 2], 2, 0, 100, true);
319+ verifyItem(grid->m_visibleItems[ 3], 3, 190, 190, true);
320+ verifyItem(grid->m_visibleItems[ 4], 4, 200, 0, true);
321+ verifyItem(grid->m_visibleItems[ 5], 5, 290, 190, true);
322+ checkImplicitHeight(280);
323+ }
324+
325+ void testModelReset()
326+ {
327+ model->setCount(6);
328+
329+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
330+ QTRY_COMPARE(grid->m_visibleItems.count(), 6);
331+ verifyItem(grid->m_visibleItems[ 0], 0, 0, 0, true);
332+ verifyItem(grid->m_visibleItems[ 1], 1, 100, 0, true);
333+ verifyItem(grid->m_visibleItems[ 2], 2, 0, 100, true);
334+ verifyItem(grid->m_visibleItems[ 3], 3, 190, 190, true);
335+ verifyItem(grid->m_visibleItems[ 4], 4, 200, 0, true);
336+ verifyItem(grid->m_visibleItems[ 5], 5, 290, 190, true);
337+ checkImplicitHeight(280);
338+ }
339+
340+ void testModelRemoveLastNonVisible()
341+ {
342+ model->removeLast();
343+
344+ // This is the same than checkInitialPositions but
345+ // with a different implicitHeight since there's an item less
346+ // in the model
347+ QTRY_COMPARE(grid->m_visibleItems.count(), 18);
348+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
349+ QTRY_COMPARE(grid->m_numberOfModulesPerRow, 1);
350+ verifyItem(grid->m_visibleItems[ 0], 0, 0, 0, true);
351+ verifyItem(grid->m_visibleItems[ 1], 1, 100, 0, true);
352+ verifyItem(grid->m_visibleItems[ 2], 2, 0, 100, true);
353+ verifyItem(grid->m_visibleItems[ 3], 3, 190, 190, true);
354+ verifyItem(grid->m_visibleItems[ 4], 4, 200, 0, true);
355+ verifyItem(grid->m_visibleItems[ 5], 5, 290, 190, true);
356+ verifyItem(grid->m_visibleItems[ 6], 6, 0, 290, true);
357+ verifyItem(grid->m_visibleItems[ 7], 7, 100, 290, true);
358+ verifyItem(grid->m_visibleItems[ 8], 8, 0, 390, true);
359+ verifyItem(grid->m_visibleItems[ 9], 9, 190, 480, false);
360+ verifyItem(grid->m_visibleItems[10], 10, 200, 290, true);
361+ verifyItem(grid->m_visibleItems[11], 11, 290, 480, false);
362+ verifyItem(grid->m_visibleItems[12], 12, 0, 580, false);
363+ verifyItem(grid->m_visibleItems[13], 13, 100, 580, false);
364+ verifyItem(grid->m_visibleItems[14], 14, 0, 680, false);
365+ verifyItem(grid->m_visibleItems[15], 15, 190, 770, false);
366+ verifyItem(grid->m_visibleItems[16], 16, 200, 580, false);
367+ verifyItem(grid->m_visibleItems[17], 17, 290, 770, false);
368+ checkImplicitHeight(960);
369+
370+ model->removeLast();
371+
372+ // Removing this one didn't cause any change since it only
373+ // was the second item of a non visible 6-module
374+ QTRY_COMPARE(grid->m_visibleItems.count(), 18);
375+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
376+ QTRY_COMPARE(grid->m_numberOfModulesPerRow, 1);
377+ verifyItem(grid->m_visibleItems[ 0], 0, 0, 0, true);
378+ verifyItem(grid->m_visibleItems[ 1], 1, 100, 0, true);
379+ verifyItem(grid->m_visibleItems[ 2], 2, 0, 100, true);
380+ verifyItem(grid->m_visibleItems[ 3], 3, 190, 190, true);
381+ verifyItem(grid->m_visibleItems[ 4], 4, 200, 0, true);
382+ verifyItem(grid->m_visibleItems[ 5], 5, 290, 190, true);
383+ verifyItem(grid->m_visibleItems[ 6], 6, 0, 290, true);
384+ verifyItem(grid->m_visibleItems[ 7], 7, 100, 290, true);
385+ verifyItem(grid->m_visibleItems[ 8], 8, 0, 390, true);
386+ verifyItem(grid->m_visibleItems[ 9], 9, 190, 480, false);
387+ verifyItem(grid->m_visibleItems[10], 10, 200, 290, true);
388+ verifyItem(grid->m_visibleItems[11], 11, 290, 480, false);
389+ verifyItem(grid->m_visibleItems[12], 12, 0, 580, false);
390+ verifyItem(grid->m_visibleItems[13], 13, 100, 580, false);
391+ verifyItem(grid->m_visibleItems[14], 14, 0, 680, false);
392+ verifyItem(grid->m_visibleItems[15], 15, 190, 770, false);
393+ verifyItem(grid->m_visibleItems[16], 16, 200, 580, false);
394+ verifyItem(grid->m_visibleItems[17], 17, 290, 770, false);
395+ checkImplicitHeight(960);
396+
397+ model->removeLast();
398+
399+ // Removing this one removes the first of a 6-module so
400+ // it does change height again
401+ QTRY_COMPARE(grid->m_visibleItems.count(), 18);
402+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
403+ QTRY_COMPARE(grid->m_numberOfModulesPerRow, 1);
404+ verifyItem(grid->m_visibleItems[ 0], 0, 0, 0, true);
405+ verifyItem(grid->m_visibleItems[ 1], 1, 100, 0, true);
406+ verifyItem(grid->m_visibleItems[ 2], 2, 0, 100, true);
407+ verifyItem(grid->m_visibleItems[ 3], 3, 190, 190, true);
408+ verifyItem(grid->m_visibleItems[ 4], 4, 200, 0, true);
409+ verifyItem(grid->m_visibleItems[ 5], 5, 290, 190, true);
410+ verifyItem(grid->m_visibleItems[ 6], 6, 0, 290, true);
411+ verifyItem(grid->m_visibleItems[ 7], 7, 100, 290, true);
412+ verifyItem(grid->m_visibleItems[ 8], 8, 0, 390, true);
413+ verifyItem(grid->m_visibleItems[ 9], 9, 190, 480, false);
414+ verifyItem(grid->m_visibleItems[10], 10, 200, 290, true);
415+ verifyItem(grid->m_visibleItems[11], 11, 290, 480, false);
416+ verifyItem(grid->m_visibleItems[12], 12, 0, 580, false);
417+ verifyItem(grid->m_visibleItems[13], 13, 100, 580, false);
418+ verifyItem(grid->m_visibleItems[14], 14, 0, 680, false);
419+ verifyItem(grid->m_visibleItems[15], 15, 190, 770, false);
420+ verifyItem(grid->m_visibleItems[16], 16, 200, 580, false);
421+ verifyItem(grid->m_visibleItems[17], 17, 290, 770, false);
422+ checkImplicitHeight(860);
423+
424+ }
425+
426+ void testModelAppendRemoveLast()
427+ {
428+ DummyModel *model2 = new DummyModel();
429+ model2->setCount(6);
430+ grid->setModel(model2);
431+ delete model;
432+ model = model2;
433+
434+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
435+ QTRY_COMPARE(grid->m_visibleItems.count(), 6);
436+ verifyItem(grid->m_visibleItems[0], 0, 0, 0, true);
437+ verifyItem(grid->m_visibleItems[1], 1, 100, 0, true);
438+ verifyItem(grid->m_visibleItems[2], 2, 0, 100, true);
439+ verifyItem(grid->m_visibleItems[3], 3, 190, 190, true);
440+ verifyItem(grid->m_visibleItems[4], 4, 200, 0, true);
441+ verifyItem(grid->m_visibleItems[5], 5, 290, 190, true);
442+ checkImplicitHeight(280);
443+
444+ model2->addItem();
445+
446+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
447+ QTRY_COMPARE(grid->m_visibleItems.count(), 7);
448+ verifyItem(grid->m_visibleItems[0], 0, 0, 0, true);
449+ verifyItem(grid->m_visibleItems[1], 1, 100, 0, true);
450+ verifyItem(grid->m_visibleItems[2], 2, 0, 100, true);
451+ verifyItem(grid->m_visibleItems[3], 3, 190, 190, true);
452+ verifyItem(grid->m_visibleItems[4], 4, 200, 0, true);
453+ verifyItem(grid->m_visibleItems[5], 5, 290, 190, true);
454+ verifyItem(grid->m_visibleItems[6], 6, 0, 290, true);
455+ checkImplicitHeight(380);
456+
457+ model2->addItem();
458+ model2->addItem();
459+
460+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
461+ QTRY_COMPARE(grid->m_visibleItems.count(), 9);
462+ verifyItem(grid->m_visibleItems[0], 0, 0, 0, true);
463+ verifyItem(grid->m_visibleItems[1], 1, 100, 0, true);
464+ verifyItem(grid->m_visibleItems[2], 2, 0, 100, true);
465+ verifyItem(grid->m_visibleItems[3], 3, 190, 190, true);
466+ verifyItem(grid->m_visibleItems[4], 4, 200, 0, true);
467+ verifyItem(grid->m_visibleItems[5], 5, 290, 190, true);
468+ verifyItem(grid->m_visibleItems[6], 6, 0, 290, true);
469+ verifyItem(grid->m_visibleItems[7], 7, 100, 290, true);
470+ verifyItem(grid->m_visibleItems[8], 8, 0, 390, true);
471+ checkImplicitHeight(570);
472+
473+ model2->removeLast();
474+
475+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
476+ QTRY_COMPARE(grid->m_visibleItems.count(), 8);
477+ verifyItem(grid->m_visibleItems[0], 0, 0, 0, true);
478+ verifyItem(grid->m_visibleItems[1], 1, 100, 0, true);
479+ verifyItem(grid->m_visibleItems[2], 2, 0, 100, true);
480+ verifyItem(grid->m_visibleItems[3], 3, 190, 190, true);
481+ verifyItem(grid->m_visibleItems[4], 4, 200, 0, true);
482+ verifyItem(grid->m_visibleItems[5], 5, 290, 190, true);
483+ verifyItem(grid->m_visibleItems[6], 6, 0, 290, true);
484+ verifyItem(grid->m_visibleItems[7], 7, 100, 290, true);
485+ checkImplicitHeight(380);
486+ }
487+
488+ void testDelegateCreationRanges()
489+ {
490+ grid->setSmallDelegateSize(QSizeF(30, 30));
491+ grid->setBigDelegateSize(QSizeF(50, 50));
492+ model->setCount(400);
493+ checkImplicitHeight(2290);
494+
495+ view->resize(470, 700);
496+ QTRY_COMPARE(grid->height(), 700.);
497+ checkImplicitHeight(2290);
498+ QTRY_COMPARE(grid->m_firstVisibleIndex, 0);
499+ QTRY_COMPARE(grid->m_visibleItems.count(), 198);
500+
501+ grid->setDelegateCreationBegin(300);
502+ grid->setDelegateCreationEnd(400);
503+ checkImplicitHeight(2290);
504+
505+ QTRY_COMPARE(grid->m_firstVisibleIndex, 36);
506+ QTRY_COMPARE(grid->m_visibleItems.count(), 54);
507+ checkImplicitHeight(2290);
508+
509+ grid->resetDelegateCreationBegin();
510+ grid->resetDelegateCreationEnd();
511+
512+ view->resize(470, 470);
513+
514+ grid->setSmallDelegateSize(QSizeF(90, 90));
515+ grid->setBigDelegateSize(QSizeF(180, 180));
516+
517+ model->setCount(21);
518+
519+ // This is exactly the same block as the first again
520+ checkInitialPositions();
521+ }
522+
523+private:
524+ QQuickView *view;
525+ OrganicGrid *grid;
526+ DummyModel *model;
527+};
528+
529+QTEST_MAIN(OrganicGridTest)
530+
531+#include "organicgridtest.moc"
tests/plugins/DashViews/organicgridtest.qmlnew file+43
@@ -0,0 +1,43 @@
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+ */
16+
17+import QtQuick 2.1
18+import DashViews 0.1
19+
20+Item {
21+
22+ OrganicGrid {
23+ id: vj
24+ objectName: "grid"
25+ anchors.fill: parent
26+ columnSpacing: 10
27+ rowSpacing: 10
28+ smallDelegateSize: Qt.size(90, 90)
29+ bigDelegateSize: Qt.size(180, 180)
30+
31+ delegate: Rectangle {
32+ property real randomValue: Math.random()
33+ color: randomValue < 0.3 ? "green" : randomValue < 0.6 ? "blue" : "red";
34+ border.width: 3
35+
36+ Text {
37+ text: index
38+ x: 10
39+ y: 10
40+ }
41+ }
42+ }
43+}
tests/plugins/DashViews/organicgridtry.cppnew file+98
@@ -0,0 +1,98 @@
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+ */
16+
17+#include <QQuickView>
18+#include <QDebug>
19+#include <QGuiApplication>
20+#include <QQuickView>
21+#include <QtQml/qqml.h>
22+#include <QStringListModel>
23+#include <QQmlContext>
24+#include <QQmlEngine>
25+#include <QGuiApplication>
26+
27+#include "verticaljournal.h"
28+
29+class DummyModel : public QAbstractListModel {
30+ Q_OBJECT
31+public:
32+ DummyModel() : m_count(0) {}
33+
34+ int rowCount(const QModelIndex & /*parent*/) const
35+ {
36+ return m_count;
37+ }
38+
39+ QVariant data(const QModelIndex & /*index*/, int /*role*/) const
40+ {
41+ return QVariant();
42+ }
43+
44+ void setCount(int count)
45+ {
46+ beginResetModel();
47+ m_count = count;
48+ endResetModel();
49+ }
50+
51+public slots:
52+ void addItem()
53+ {
54+ beginInsertRows(QModelIndex(), m_count, m_count);
55+ m_count++;
56+ endInsertRows();
57+ }
58+
59+ void removeLast()
60+ {
61+ beginRemoveRows(QModelIndex(), m_count - 1, m_count - 1);
62+ m_count--;
63+ endRemoveRows();
64+ }
65+
66+private:
67+ int m_count;
68+};
69+
70+int main(int argc, char *argv[])
71+{
72+ QGuiApplication a(argc, argv);
73+
74+ QQuickView *view = new QQuickView();
75+ view->setResizeMode(QQuickView::SizeRootObjectToView);
76+ view->engine()->addImportPath(BUILT_PLUGINS_DIR);
77+
78+ DummyModel model;
79+ model.setCount(3);
80+
81+ QStringListModel listModel;
82+ listModel.setStringList(QStringList() << QString());
83+
84+ view->rootContext()->setContextProperty("listModel", &listModel);
85+ view->rootContext()->setContextProperty("gridModel", &model);
86+
87+ view->setSource(QUrl::fromLocalFile(DASHVIEWSTEST_FOLDER "/organicgridtry.qml"));
88+
89+ view->show();
90+ view->resize(530, 400);
91+
92+ QObject::connect(view->rootObject(), SIGNAL(add()), &model, SLOT(addItem()));
93+ QObject::connect(view->rootObject(), SIGNAL(remove()), &model, SLOT(removeLast()));
94+
95+ return a.exec();
96+}
97+
98+#include "organicgridtry.moc"
tests/plugins/DashViews/organicgridtry.qmlnew file+94
@@ -0,0 +1,94 @@
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+ */
16+
17+import QtQuick 2.1
18+import DashViews 0.1
19+
20+Item {
21+ id: root
22+ signal add()
23+ signal remove()
24+
25+ ListViewWithPageHeader {
26+ id: lvwph
27+ model: listModel
28+ height: parent.height
29+ width: parent.width - controls.width
30+
31+ delegate: OrganicGrid {
32+ id: grid
33+ model: gridModel
34+ smallDelegateSize: Qt.size(90, 90)
35+ bigDelegateSize: Qt.size(180, 180)
36+ columnSpacing: 10
37+ rowSpacing: 10
38+ width: parent.width
39+ height: implicitHeight > 100 ? implicitHeight : 100
40+ delegateCreationBegin: lvwph.contentY
41+ delegateCreationEnd: lvwph.contentY + lvwph.height
42+
43+ delegate: Rectangle {
44+ width: 100
45+ color: "red";
46+ border.width: 3
47+
48+ Text {
49+ text: index
50+ x: 10
51+ y: 10
52+ }
53+ }
54+ }
55+ }
56+
57+ Rectangle {
58+ id: controls
59+ height: parent.height
60+ width: 100
61+ anchors.right: parent.right
62+ color: "gray"
63+ opacity: 0.4
64+
65+ Rectangle {
66+ id: addButton
67+ height: 50
68+ width: parent.width
69+ color: "blue"
70+ Text {
71+ text: "Add"
72+ anchors.centerIn: parent
73+ }
74+ MouseArea {
75+ anchors.fill: parent
76+ onClicked: root.add()
77+ }
78+ }
79+ Rectangle {
80+ anchors.top: addButton.bottom
81+ height: 50
82+ width: parent.width
83+ color: "red"
84+ Text {
85+ text: "Remove"
86+ anchors.centerIn: parent
87+ }
88+ MouseArea {
89+ anchors.fill: parent
90+ onClicked: root.remove()
91+ }
92+ }
93+ }
94+}