on xenial help sign in
Merged with trunk.
commita5e5a2b8aff72c6cde5b88b1b0d1fb94efa6871e authorLeo Arias <leo.arias@canonical.com> dateThu 9 Jan 2014, 00:02 (13 years ago) parent250c999dc8aa4d
19 files changed+1705−562
plugins/DashViews/CMakeLists.txt+2 plugins/DashViews/abstractjournal.cpp+388 plugins/DashViews/abstractjournal.h+145 plugins/DashViews/horizontaljournal.cpp+253 plugins/DashViews/horizontaljournal.h+67 plugins/DashViews/plugin.cpp+2 plugins/DashViews/verticaljournal.cpp+78−420 plugins/DashViews/verticaljournal.h+13−91 po/cs.po+9−7 po/hu.po+4−4 po/ru.po+1−1 src/CMakeLists.txt+4−1 tests/plugins/DashViews/CMakeLists.txt+37−8 tests/plugins/DashViews/horizontaljournaltest.cpp+465 tests/plugins/DashViews/horizontaljournaltest.qml+43 tests/plugins/DashViews/horizontaljournaltry.cpp+90 tests/plugins/DashViews/horizontaljournaltry.qml+99 tests/plugins/DashViews/verticaljournaltest.cpp+5−5 unity8.qmlproject−25
plugins/DashViews/CMakeLists.txt+2
@@ -24,7 +24,9 @@ remove_definitions(-DQT_NO_KEYWORDS)
2424set(QMLPLUGIN_SRC
2525 plugin.cpp
2626 listviewwithpageheader.cpp
27+ abstractjournal.cpp
2728 verticaljournal.cpp
29+ horizontaljournal.cpp
2830 )
2931
3032add_library(DashViews-qml MODULE
plugins/DashViews/abstractjournal.cppnew file+388
@@ -0,0 +1,388 @@
1+/*
2+ * Copyright (C) 2013 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 "abstractjournal.h"
18+
19+static const qreal bufferRatio = 0.5;
20+
21+AbstractJournal::AbstractJournal()
22+ : m_delegateModel(nullptr)
23+ , m_asyncRequestedIndex(-1)
24+ , m_columnSpacing(0)
25+ , m_rowSpacing(0)
26+ , m_delegateCreationBegin(0)
27+ , m_delegateCreationEnd(0)
28+ , m_delegateCreationBeginValid(false)
29+ , m_delegateCreationEndValid(false)
30+ , m_needsRelayout(false)
31+ , m_delegateValidated(false)
32+ , m_implicitHeightDirty(false)
33+{
34+ connect(this, SIGNAL(widthChanged()), this, SLOT(relayout()));
35+ connect(this, SIGNAL(heightChanged()), this, SLOT(onHeightChanged()));
36+}
37+
38+QAbstractItemModel *AbstractJournal::model() const
39+{
40+ return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
41+}
42+
43+void AbstractJournal::setModel(QAbstractItemModel *model)
44+{
45+ if (model != this->model()) {
46+ if (!m_delegateModel) {
47+ createDelegateModel();
48+ } else {
49+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
50+ disconnect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
51+ }
52+ m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
53+ connect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
54+#else
55+ disconnect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
56+ }
57+ m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
58+ connect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
59+#endif
60+
61+ cleanupExistingItems();
62+
63+ Q_EMIT modelChanged();
64+ polish();
65+ }
66+}
67+
68+QQmlComponent *AbstractJournal::delegate() const
69+{
70+ return m_delegateModel ? m_delegateModel->delegate() : nullptr;
71+}
72+
73+void AbstractJournal::setDelegate(QQmlComponent *delegate)
74+{
75+ if (delegate != this->delegate()) {
76+ if (!m_delegateModel) {
77+ createDelegateModel();
78+ }
79+
80+ cleanupExistingItems();
81+
82+ m_delegateModel->setDelegate(delegate);
83+
84+ Q_EMIT delegateChanged();
85+ m_delegateValidated = false;
86+ polish();
87+ }
88+}
89+
90+qreal AbstractJournal::columnSpacing() const
91+{
92+ return m_columnSpacing;
93+}
94+
95+void AbstractJournal::setColumnSpacing(qreal columnSpacing)
96+{
97+ if (columnSpacing != m_columnSpacing) {
98+ m_columnSpacing = columnSpacing;
99+ Q_EMIT columnSpacingChanged();
100+
101+ if (isComponentComplete()) {
102+ relayout();
103+ }
104+ }
105+}
106+
107+qreal AbstractJournal::rowSpacing() const
108+{
109+ return m_rowSpacing;
110+}
111+
112+void AbstractJournal::setRowSpacing(qreal rowSpacing)
113+{
114+ if (rowSpacing != m_rowSpacing) {
115+ m_rowSpacing = rowSpacing;
116+ Q_EMIT rowSpacingChanged();
117+
118+ if (isComponentComplete()) {
119+ relayout();
120+ }
121+ }
122+}
123+
124+qreal AbstractJournal::delegateCreationBegin() const
125+{
126+ return m_delegateCreationBegin;
127+}
128+
129+void AbstractJournal::setDelegateCreationBegin(qreal begin)
130+{
131+ m_delegateCreationBeginValid = true;
132+ if (m_delegateCreationBegin == begin)
133+ return;
134+ m_delegateCreationBegin = begin;
135+ if (isComponentComplete()) {
136+ polish();
137+ }
138+ emit delegateCreationBeginChanged();
139+}
140+
141+void AbstractJournal::resetDelegateCreationBegin()
142+{
143+ m_delegateCreationBeginValid = false;
144+ if (m_delegateCreationBegin == 0)
145+ return;
146+ m_delegateCreationBegin = 0;
147+ if (isComponentComplete()) {
148+ polish();
149+ }
150+ emit delegateCreationBeginChanged();
151+}
152+
153+qreal AbstractJournal::delegateCreationEnd() const
154+{
155+ return m_delegateCreationEnd;
156+}
157+
158+void AbstractJournal::setDelegateCreationEnd(qreal end)
159+{
160+ m_delegateCreationEndValid = true;
161+ if (m_delegateCreationEnd == end)
162+ return;
163+ m_delegateCreationEnd = end;
164+ if (isComponentComplete()) {
165+ polish();
166+ }
167+ emit delegateCreationEndChanged();
168+}
169+
170+void AbstractJournal::resetDelegateCreationEnd()
171+{
172+ m_delegateCreationEndValid = false;
173+ if (m_delegateCreationEnd == 0)
174+ return;
175+ m_delegateCreationEnd = 0;
176+ if (isComponentComplete()) {
177+ polish();
178+ }
179+ emit delegateCreationEndChanged();
180+}
181+
182+void AbstractJournal::createDelegateModel()
183+{
184+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
185+ m_delegateModel = new QQuickVisualDataModel(qmlContext(this), this);
186+ connect(m_delegateModel, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(itemCreated(int,QQuickItem*)));
187+#else
188+ m_delegateModel = new QQmlDelegateModel(qmlContext(this), this);
189+ connect(m_delegateModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(itemCreated(int,QObject*)));
190+#endif
191+ if (isComponentComplete())
192+ m_delegateModel->componentComplete();
193+}
194+
195+void AbstractJournal::refill()
196+{
197+ if (!isComponentComplete()) {
198+ return;
199+ }
200+
201+ const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
202+ const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
203+ const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
204+ const qreal buffer = (to - from) * bufferRatio;
205+ const qreal bufferFrom = from - buffer;
206+ const qreal bufferTo = to + buffer;
207+
208+ bool added = addVisibleItems(from, to, false);
209+ bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
210+ added |= addVisibleItems(bufferFrom, bufferTo, true);
211+
212+ if (added || removed) {
213+ m_implicitHeightDirty = true;
214+ polish();
215+ }
216+}
217+
218+bool AbstractJournal::addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous)
219+{
220+ if (!delegate())
221+ return false;
222+
223+ if (m_delegateModel->count() == 0)
224+ return false;
225+
226+ int modelIndex;
227+ qreal yPos;
228+ findBottomModelIndexToAdd(&modelIndex, &yPos);
229+ bool changed = false;
230+ while (modelIndex < m_delegateModel->count() && yPos <= fillToY) {
231+ if (!createItem(modelIndex, asynchronous))
232+ break;
233+
234+ changed = true;
235+ findBottomModelIndexToAdd(&modelIndex, &yPos);
236+ }
237+
238+ findTopModelIndexToAdd(&modelIndex, &yPos);
239+ while (modelIndex >= 0 && yPos > fillFromY) {
240+ if (!createItem(modelIndex, asynchronous))
241+ break;
242+
243+ changed = true;
244+ findTopModelIndexToAdd(&modelIndex, &yPos);
245+ }
246+
247+ return changed;
248+}
249+
250+QQuickItem *AbstractJournal::createItem(int modelIndex, bool asynchronous)
251+{
252+ if (asynchronous && m_asyncRequestedIndex != -1)
253+ return nullptr;
254+
255+ m_asyncRequestedIndex = -1;
256+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
257+ QQuickItem *item = m_delegateModel->item(modelIndex, asynchronous);
258+#else
259+ QObject* object = m_delegateModel->object(modelIndex, asynchronous);
260+ QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
261+#endif
262+ if (!item) {
263+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
264+ m_asyncRequestedIndex = modelIndex;
265+#else
266+ if (object) {
267+ m_delegateModel->release(object);
268+ if (!m_delegateValidated) {
269+ m_delegateValidated = true;
270+ QObject* delegateObj = delegate();
271+ qmlInfo(delegateObj ? delegateObj : this) << "Delegate must be of Item type";
272+ }
273+ } else {
274+ m_asyncRequestedIndex = modelIndex;
275+ }
276+#endif
277+ return nullptr;
278+ } else {
279+ positionItem(modelIndex, item);
280+ return item;
281+ }
282+}
283+
284+void AbstractJournal::releaseItem(QQuickItem *item)
285+{
286+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
287+ QQuickVisualModel::ReleaseFlags flags = m_delegateModel->release(item);
288+ if (flags & QQuickVisualModel::Destroyed) {
289+#else
290+ QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item);
291+ if (flags & QQmlDelegateModel::Destroyed) {
292+#endif
293+ item->setParentItem(nullptr);
294+ }
295+}
296+
297+void AbstractJournal::setImplicitHeightDirty()
298+{
299+ m_implicitHeightDirty = true;
300+}
301+
302+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
303+void AbstractJournal::itemCreated(int modelIndex, QQuickItem *item)
304+{
305+#else
306+void AbstractJournal::itemCreated(int modelIndex, QObject *object)
307+{
308+ QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
309+ if (!item) {
310+ qWarning() << "AbstractJournal::itemCreated got a non item for index" << modelIndex;
311+ return;
312+ }
313+#endif
314+ item->setParentItem(this);
315+
316+ // We only need to call createItem if we are here because of an asynchronous generation
317+ // otherwise we are in this slot because createItem is creating the item sync
318+ // and thus we don't need to call createItem again, nor need to set m_implicitHeightDirty
319+ // and call polish because the sync createItem was called from addVisibleItems that
320+ // is called from refill that will already do those if an item was added
321+ if (modelIndex == m_asyncRequestedIndex) {
322+ createItem(modelIndex, false);
323+ m_implicitHeightDirty = true;
324+ polish();
325+ }
326+}
327+
328+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
329+void AbstractJournal::onModelUpdated(const QQuickChangeSet &changeSet, bool reset)
330+#else
331+void AbstractJournal::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
332+#endif
333+{
334+ if (reset) {
335+ cleanupExistingItems();
336+ } else {
337+ processModelRemoves(changeSet.removes());
338+ }
339+ polish();
340+}
341+
342+
343+void AbstractJournal::relayout()
344+{
345+ m_needsRelayout = true;
346+ polish();
347+}
348+
349+void AbstractJournal::onHeightChanged()
350+{
351+ polish();
352+}
353+
354+void AbstractJournal::updatePolish()
355+{
356+ if (!model())
357+ return;
358+
359+ if (m_needsRelayout) {
360+ doRelayout();
361+ m_needsRelayout = false;
362+ m_implicitHeightDirty = true;
363+ }
364+
365+ refill();
366+
367+ const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
368+ const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
369+ const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
370+ updateItemCulling(from, to);
371+
372+ if (m_implicitHeightDirty) {
373+ calculateImplicitHeight();
374+ m_implicitHeightDirty = false;
375+ }
376+}
377+
378+void AbstractJournal::componentComplete()
379+{
380+ if (m_delegateModel)
381+ m_delegateModel->componentComplete();
382+
383+ QQuickItem::componentComplete();
384+
385+ m_needsRelayout = true;
386+
387+ polish();
388+}
plugins/DashViews/abstractjournal.hnew file+145
@@ -0,0 +1,145 @@
1+/*
2+ * Copyright (C) 2013 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 ABSTRACTJOURNAL_H
18+#define ABSTRACTJOURNAL_H
19+
20+#include <QQuickItem>
21+
22+class QAbstractItemModel;
23+class QQmlComponent;
24+#pragma GCC diagnostic push
25+#pragma GCC diagnostic ignored "-pedantic"
26+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
27+#include <private/qquickvisualdatamodel_p.h>
28+#else
29+#include <private/qqmldelegatemodel_p.h>
30+#include <qqmlinfo.h>
31+#endif
32+#pragma GCC diagnostic pop
33+
34+class AbstractJournal : public QQuickItem
35+{
36+ Q_OBJECT
37+
38+ Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
39+ Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
40+ Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
41+ Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
42+ Q_PROPERTY(qreal delegateCreationBegin READ delegateCreationBegin
43+ WRITE setDelegateCreationBegin
44+ NOTIFY delegateCreationBeginChanged
45+ RESET resetDelegateCreationBegin)
46+ Q_PROPERTY(qreal delegateCreationEnd READ delegateCreationEnd
47+ WRITE setDelegateCreationEnd
48+ NOTIFY delegateCreationEndChanged
49+ RESET resetDelegateCreationEnd)
50+
51+friend class VerticalJournalTest;
52+friend class HorizontalJournalTest;
53+
54+public:
55+ AbstractJournal();
56+
57+ QAbstractItemModel *model() const;
58+ void setModel(QAbstractItemModel *model);
59+
60+ QQmlComponent *delegate() const;
61+ void setDelegate(QQmlComponent *delegate);
62+
63+ qreal columnSpacing() const;
64+ void setColumnSpacing(qreal columnSpacing);
65+
66+ qreal rowSpacing() const;
67+ void setRowSpacing(qreal rowSpacing);
68+
69+ qreal delegateCreationBegin() const;
70+ void setDelegateCreationBegin(qreal);
71+ void resetDelegateCreationBegin();
72+
73+ qreal delegateCreationEnd() const;
74+ void setDelegateCreationEnd(qreal);
75+ void resetDelegateCreationEnd();
76+
77+Q_SIGNALS:
78+ void modelChanged();
79+ void delegateChanged();
80+ void columnSpacingChanged();
81+ void rowSpacingChanged();
82+ void delegateCreationBeginChanged();
83+ void delegateCreationEndChanged();
84+
85+protected Q_SLOTS:
86+ void relayout();
87+
88+protected:
89+ void updatePolish() override;
90+ void componentComplete() override;
91+
92+ void releaseItem(QQuickItem *item);
93+ void setImplicitHeightDirty();
94+
95+private Q_SLOTS:
96+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
97+ void itemCreated(int modelIndex, QQuickItem *item);
98+ void onModelUpdated(const QQuickChangeSet &changeSet, bool reset);
99+#else
100+ void itemCreated(int modelIndex, QObject *object);
101+ void onModelUpdated(const QQmlChangeSet &changeSet, bool reset);
102+#endif
103+ void onHeightChanged();
104+
105+private:
106+ void createDelegateModel();
107+ void refill();
108+ bool addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous);
109+ QQuickItem *createItem(int modelIndex, bool asynchronous);
110+
111+ virtual void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) = 0;
112+ virtual void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) = 0;
113+ virtual void positionItem(int modelIndex, QQuickItem *item) = 0;
114+ virtual bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) = 0;
115+ virtual void cleanupExistingItems() = 0;
116+ virtual void doRelayout() = 0;
117+ virtual void updateItemCulling(qreal visibleFromY, qreal visibleToY) = 0;
118+ virtual void calculateImplicitHeight() = 0;
119+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
120+ virtual void processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes) = 0;
121+#else
122+ virtual void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) = 0;
123+#endif
124+
125+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
126+ QQuickVisualDataModel *m_delegateModel;
127+#else
128+ QQmlDelegateModel *m_delegateModel;
129+#endif
130+
131+ // Index we are waiting because we requested it asynchronously
132+ int m_asyncRequestedIndex;
133+
134+ int m_columnSpacing;
135+ int m_rowSpacing;
136+ qreal m_delegateCreationBegin;
137+ qreal m_delegateCreationEnd;
138+ bool m_delegateCreationBeginValid;
139+ bool m_delegateCreationEndValid;
140+ bool m_needsRelayout;
141+ bool m_delegateValidated;
142+ bool m_implicitHeightDirty;
143+};
144+
145+#endif
plugins/DashViews/horizontaljournal.cppnew file+253
@@ -0,0 +1,253 @@
1+/*
2+ * Copyright (C) 2013 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+/*
18+ * The implementation is centered around m_visibleItems
19+ * that a list for each of the items in the view.
20+ * m_firstVisibleIndex is the index of the first item in m_visibleItems
21+ * m_lastInRowIndexPosition is a map that contains the x position
22+ * of items that are the last ones of a row so we can reconstruct the rows
23+ * when building back
24+ */
25+
26+#include "horizontaljournal.h"
27+
28+#include <qqmlengine.h>
29+#pragma GCC diagnostic push
30+#pragma GCC diagnostic ignored "-pedantic"
31+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
32+#include <private/qquickvisualdatamodel_p.h>
33+#else
34+#include <private/qqmldelegatemodel_p.h>
35+#include <qqmlinfo.h>
36+#endif
37+#include <private/qquickitem_p.h>
38+#pragma GCC diagnostic pop
39+
40+static const qreal bufferRatio = 0.5;
41+
42+HorizontalJournal::HorizontalJournal()
43+ : m_firstVisibleIndex(-1)
44+ , m_rowHeight(0)
45+{
46+}
47+
48+qreal HorizontalJournal::rowHeight() const
49+{
50+ return m_rowHeight;
51+}
52+
53+void HorizontalJournal::setRowHeight(qreal rowHeight)
54+{
55+ if (rowHeight != m_rowHeight) {
56+ m_rowHeight = rowHeight;
57+ Q_EMIT rowHeightChanged();
58+
59+ if (isComponentComplete()) {
60+ Q_FOREACH(QQuickItem *item, m_visibleItems) {
61+ item->setHeight(rowHeight);
62+ }
63+ relayout();
64+ }
65+ }
66+}
67+
68+void HorizontalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
69+{
70+ if (m_visibleItems.isEmpty()) {
71+ *modelIndex = 0;
72+ *yPos = 0;
73+ } else {
74+ *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
75+ if (m_lastInRowIndexPosition.contains(*modelIndex - 1)) {
76+ *yPos = m_visibleItems.last()->y() + m_rowHeight + rowSpacing();
77+ } else {
78+ *yPos = m_visibleItems.last()->y();
79+ }
80+ }
81+}
82+
83+void HorizontalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
84+{
85+ if (m_visibleItems.isEmpty()) {
86+ *modelIndex = -1;
87+ *yPos = INT_MIN;
88+ } else {
89+ *modelIndex = m_firstVisibleIndex - 1;
90+ if (m_lastInRowIndexPosition.contains(*modelIndex)) {
91+ *yPos = m_visibleItems.first()->y() - rowSpacing() - m_rowHeight;
92+ } else {
93+ *yPos = m_visibleItems.first()->y();
94+ }
95+ }
96+}
97+
98+bool HorizontalJournal::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
99+{
100+ bool changed = false;
101+
102+ while (!m_visibleItems.isEmpty() && m_visibleItems.first()->y() + m_rowHeight < bufferFromY) {
103+ releaseItem(m_visibleItems.takeFirst());
104+ changed = true;
105+ m_firstVisibleIndex++;
106+ }
107+
108+ while (!m_visibleItems.isEmpty() && m_visibleItems.last()->y() > bufferToY) {
109+ releaseItem(m_visibleItems.takeLast());
110+ changed = true;
111+ m_lastInRowIndexPosition.remove(m_firstVisibleIndex + m_visibleItems.count());
112+ }
113+
114+ if (m_visibleItems.isEmpty()) {
115+ m_firstVisibleIndex = -1;
116+ }
117+
118+ return changed;
119+}
120+
121+void HorizontalJournal::positionItem(int modelIndex, QQuickItem *item)
122+{
123+ if (item->height() != m_rowHeight) {
124+ qWarning() << "Item" << modelIndex << "height is not the one that the rowHeight mandates, resetting it";
125+ item->setHeight(m_rowHeight);
126+ }
127+
128+ if (m_visibleItems.isEmpty()) {
129+ Q_ASSERT(modelIndex == 0);
130+ item->setY(0);
131+ item->setX(0);
132+ m_visibleItems << item;
133+ m_firstVisibleIndex = 0;
134+ } else {
135+ // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count()
136+ if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
137+ QQuickItem *lastItem = m_visibleItems.last();
138+ if (lastItem->x() + lastItem->width() + columnSpacing() + item->width() <= width()) {
139+ // Fits in the row
140+ item->setY(lastItem->y());
141+ item->setX(lastItem->x() + lastItem->width() + columnSpacing());
142+ } else {
143+ // Starts a new row
144+ item->setY(lastItem->y() + m_rowHeight + rowSpacing());
145+ item->setX(0);
146+ m_lastInRowIndexPosition[modelIndex - 1] = lastItem->x();
147+ }
148+ m_visibleItems << item;
149+ } else if (modelIndex == m_firstVisibleIndex - 1) {
150+ QQuickItem *firstItem = m_visibleItems.first();
151+ if (m_lastInRowIndexPosition.contains(modelIndex)) {
152+ // It is the last item of its row, so start a new one since we're going back
153+ item->setY(firstItem->y() - rowSpacing() - m_rowHeight);
154+ item->setX(m_lastInRowIndexPosition[modelIndex]);
155+ } else {
156+ item->setY(firstItem->y());
157+ item->setX(firstItem->x() - columnSpacing() - item->width());
158+ }
159+ m_firstVisibleIndex = modelIndex;
160+ m_visibleItems.prepend(item);
161+ } else {
162+ qWarning() << "HorizontalJournal::positionItem - Got unexpected modelIndex"
163+ << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
164+ }
165+ }
166+}
167+
168+void HorizontalJournal::cleanupExistingItems()
169+{
170+ // Cleanup the existing items
171+ Q_FOREACH(QQuickItem *item, m_visibleItems)
172+ releaseItem(item);
173+ m_visibleItems.clear();
174+ m_lastInRowIndexPosition.clear();
175+ m_firstVisibleIndex = 0;
176+}
177+
178+void HorizontalJournal::calculateImplicitHeight()
179+{
180+ if (m_firstVisibleIndex >= 0) {
181+ const int nIndexes = m_firstVisibleIndex + m_visibleItems.count();
182+ const double bottomMostY = m_visibleItems.last()->y() + m_rowHeight;
183+ const double averageHeight = bottomMostY / nIndexes;
184+ setImplicitHeight(bottomMostY + averageHeight * (model()->rowCount() - nIndexes));
185+ } else {
186+ setImplicitHeight(0);
187+ }
188+}
189+
190+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
191+void HorizontalJournal::processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes)
192+#else
193+void HorizontalJournal::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
194+#endif
195+{
196+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
197+ Q_FOREACH(const QQuickChangeSet::Remove &remove, removes) {
198+#else
199+ Q_FOREACH(const QQmlChangeSet::Remove &remove, removes) {
200+#endif
201+ for (int i = remove.count - 1; i >= 0; --i) {
202+ const int indexToRemove = remove.index + i;
203+ // We only support removing from the end so
204+ // any of the last items of a column has to be indexToRemove
205+ const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
206+ if (indexToRemove == lastIndex) {
207+ releaseItem(m_visibleItems.takeLast());
208+ m_lastInRowIndexPosition.remove(indexToRemove);
209+ } else {
210+ if (indexToRemove < lastIndex) {
211+ qFatal("HorizontalJournal only supports removal from the end of the model");
212+ } else {
213+ setImplicitHeightDirty();
214+ }
215+ }
216+ }
217+ }
218+ if (m_visibleItems.isEmpty()) {
219+ m_firstVisibleIndex = -1;
220+ }
221+}
222+
223+
224+void HorizontalJournal::doRelayout()
225+{
226+ // If m_firstVisibleIndex is not 0 we need to drop all the delegates
227+ // since we can't consistently relayout without the first item being there
228+
229+ if (m_firstVisibleIndex == 0) {
230+ int i = 0;
231+ const QList<QQuickItem*> allItems = m_visibleItems;
232+ m_visibleItems.clear();
233+ m_lastInRowIndexPosition.clear();
234+ Q_FOREACH(QQuickItem *item, allItems) {
235+ positionItem(i, item);
236+ ++i;
237+ }
238+ } else {
239+ Q_FOREACH(QQuickItem *item, m_visibleItems) {
240+ releaseItem(item);
241+ }
242+ m_visibleItems.clear();
243+ m_lastInRowIndexPosition.clear();
244+ m_firstVisibleIndex = 0;
245+ }
246+}
247+
248+void HorizontalJournal::updateItemCulling(qreal visibleFromY, qreal visibleToY)
249+{
250+ Q_FOREACH(QQuickItem *item, m_visibleItems) {
251+ QQuickItemPrivate::get(item)->setCulled(item->y() + m_rowHeight <= visibleFromY || item->y() >= visibleToY);
252+ }
253+}
plugins/DashViews/horizontaljournal.hnew file+67
@@ -0,0 +1,67 @@
1+/*
2+ * Copyright (C) 2013 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 HORIZONTALJOURNAL_H
18+#define HORIZONTALJOURNAL_H
19+
20+#include "abstractjournal.h"
21+
22+ /** A horizontal journal is a view that creates delegates
23+ * based on a model and layouts them one after the other
24+ * in the same row until there is no more free space for the next item
25+ * and so that item is layouted in the next row
26+ *
27+ * All delegates are forced to rowHeight if they don't have it.
28+ */
29+ class HorizontalJournal : public AbstractJournal
30+{
31+ Q_OBJECT
32+
33+ Q_PROPERTY(qreal rowHeight READ rowHeight WRITE setRowHeight NOTIFY rowHeightChanged)
34+
35+friend class HorizontalJournalTest;
36+
37+public:
38+ HorizontalJournal();
39+
40+ qreal rowHeight() const;
41+ void setRowHeight(qreal rowHeight);
42+
43+Q_SIGNALS:
44+ void rowHeightChanged();
45+
46+private:
47+ void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
48+ void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
49+ bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
50+ void positionItem(int modelIndex, QQuickItem *item) override;
51+ void cleanupExistingItems() override;
52+ void calculateImplicitHeight() override;
53+ void doRelayout() override;
54+ void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
55+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
56+ void processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes) override;
57+#else
58+ void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) override;
59+#endif
60+
61+ int m_firstVisibleIndex;
62+ QList<QQuickItem*> m_visibleItems;
63+ QMap<int, double> m_lastInRowIndexPosition;
64+ int m_rowHeight;
65+};
66+
67+#endif
plugins/DashViews/plugin.cpp+2
@@ -17,6 +17,7 @@
1717
1818#include "plugin.h"
1919
20+#include "horizontaljournal.h"
2021#include "listviewwithpageheader.h"
2122#include "verticaljournal.h"
2223
@@ -26,6 +27,7 @@ void DashViewsPlugin::registerTypes(const char *uri)
2627{
2728 Q_ASSERT(uri == QLatin1String("DashViews"));
2829 qmlRegisterType<QAbstractItemModel>();
30+ qmlRegisterType<HorizontalJournal>(uri, 0, 1, "HorizontalJournal");
2931 qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
3032 qmlRegisterType<VerticalJournal>(uri, 0, 1, "VerticalJournal");
3133}
plugins/DashViews/verticaljournal.cpp+78−420
@@ -27,88 +27,16 @@
2727 */
2828#include "verticaljournal.h"
2929
30−#include <qqmlengine.h>
3130#pragma GCC diagnostic push
3231#pragma GCC diagnostic ignored "-pedantic"
33−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
34−#include <private/qquickvisualdatamodel_p.h>
35−#else
36−#include <private/qqmldelegatemodel_p.h>
37−#include <qqmlinfo.h>
38−#endif
3932#include <private/qquickitem_p.h>
4033#pragma GCC diagnostic pop
4134
4235static const qreal bufferRatio = 0.5;
4336
4437VerticalJournal::VerticalJournal()
45− : m_delegateModel(nullptr)
46− , m_asyncRequestedIndex(-1)
47− , m_columnWidth(0)
48− , m_columnSpacing(0)
49− , m_rowSpacing(0)
50− , m_delegateCreationBegin(0)
51− , m_delegateCreationEnd(0)
52− , m_delegateCreationBeginValid(false)
53− , m_delegateCreationEndValid(false)
54− , m_needsRelayout(false)
55− , m_delegateValidated(false)
56− , m_implicitHeightDirty(false)
57−{
58− connect(this, SIGNAL(widthChanged()), this, SLOT(relayout()));
59− connect(this, SIGNAL(heightChanged()), this, SLOT(onHeightChanged()));
60−}
61−
62−QAbstractItemModel *VerticalJournal::model() const
38+ : m_columnWidth(0)
6339{
64− return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
65−}
66−
67−void VerticalJournal::setModel(QAbstractItemModel *model)
68−{
69− if (model != this->model()) {
70− if (!m_delegateModel) {
71− createDelegateModel();
72− } else {
73−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
74− disconnect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
75− }
76− m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
77− connect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
78−#else
79− disconnect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
80− }
81− m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
82− connect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
83−#endif
84−
85− cleanupExistingItems();
86−
87− Q_EMIT modelChanged();
88− polish();
89− }
90−}
91−
92−QQmlComponent *VerticalJournal::delegate() const
93−{
94− return m_delegateModel ? m_delegateModel->delegate() : nullptr;
95−}
96−
97−void VerticalJournal::setDelegate(QQmlComponent *delegate)
98−{
99− if (delegate != this->delegate()) {
100− if (!m_delegateModel) {
101− createDelegateModel();
102− }
103−
104− cleanupExistingItems();
105−
106− m_delegateModel->setDelegate(delegate);
107−
108− Q_EMIT delegateChanged();
109− m_delegateValidated = false;
110− polish();
111− }
11240}
11341
11442qreal VerticalJournal::columnWidth() const
@@ -133,133 +61,6 @@ void VerticalJournal::setColumnWidth(qreal columnWidth)
13361 }
13462}
13563
136−qreal VerticalJournal::columnSpacing() const
137−{
138− return m_columnSpacing;
139−}
140−
141−void VerticalJournal::setColumnSpacing(qreal columnSpacing)
142−{
143− if (columnSpacing != m_columnSpacing) {
144− m_columnSpacing = columnSpacing;
145− Q_EMIT columnSpacingChanged();
146−
147− if (isComponentComplete()) {
148− relayout();
149− }
150− }
151−}
152−
153−qreal VerticalJournal::rowSpacing() const
154−{
155− return m_rowSpacing;
156−}
157−
158−void VerticalJournal::setRowSpacing(qreal rowSpacing)
159−{
160− if (rowSpacing != m_rowSpacing) {
161− m_rowSpacing = rowSpacing;
162− Q_EMIT rowSpacingChanged();
163−
164− if (isComponentComplete()) {
165− relayout();
166− }
167− }
168−}
169−
170−qreal VerticalJournal::delegateCreationBegin() const
171−{
172− return m_delegateCreationBegin;
173−}
174−
175−void VerticalJournal::setDelegateCreationBegin(qreal begin)
176−{
177− m_delegateCreationBeginValid = true;
178− if (m_delegateCreationBegin == begin)
179− return;
180− m_delegateCreationBegin = begin;
181− if (isComponentComplete()) {
182− polish();
183− }
184− emit delegateCreationBeginChanged();
185−}
186−
187−void VerticalJournal::resetDelegateCreationBegin()
188−{
189− m_delegateCreationBeginValid = false;
190− if (m_delegateCreationBegin == 0)
191− return;
192− m_delegateCreationBegin = 0;
193− if (isComponentComplete()) {
194− polish();
195− }
196− emit delegateCreationBeginChanged();
197−}
198−
199−qreal VerticalJournal::delegateCreationEnd() const
200−{
201− return m_delegateCreationEnd;
202−}
203−
204−void VerticalJournal::setDelegateCreationEnd(qreal end)
205−{
206− m_delegateCreationEndValid = true;
207− if (m_delegateCreationEnd == end)
208− return;
209− m_delegateCreationEnd = end;
210− if (isComponentComplete()) {
211− polish();
212− }
213− emit delegateCreationEndChanged();
214−}
215−
216−void VerticalJournal::resetDelegateCreationEnd()
217−{
218− m_delegateCreationEndValid = false;
219− if (m_delegateCreationEnd == 0)
220− return;
221− m_delegateCreationEnd = 0;
222− if (isComponentComplete()) {
223− polish();
224− }
225− emit delegateCreationEndChanged();
226−}
227−
228−void VerticalJournal::createDelegateModel()
229−{
230−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
231− m_delegateModel = new QQuickVisualDataModel(qmlContext(this), this);
232− connect(m_delegateModel, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(itemCreated(int,QQuickItem*)));
233−#else
234− m_delegateModel = new QQmlDelegateModel(qmlContext(this), this);
235− connect(m_delegateModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(itemCreated(int,QObject*)));
236−#endif
237− if (isComponentComplete())
238− m_delegateModel->componentComplete();
239−}
240−
241−void VerticalJournal::refill()
242−{
243− if (!isComponentComplete()) {
244− return;
245− }
246−
247− const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
248− const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
249− const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
250− const qreal buffer = (to - from) * bufferRatio;
251− const qreal bufferFrom = from - buffer;
252− const qreal bufferTo = to + buffer;
253−
254− bool added = addVisibleItems(from, to, false);
255− bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
256− added |= addVisibleItems(bufferFrom, bufferTo, true);
257−
258− if (added || removed) {
259− m_implicitHeightDirty = true;
260− }
261−}
262−
26364void VerticalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
26465{
26566 *modelIndex = 0;
@@ -268,7 +69,7 @@ void VerticalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
26869 Q_FOREACH(const auto &column, m_columnVisibleItems) {
26970 if (!column.isEmpty()) {
27071 const ViewItem &item = column.last();
271− *yPos = qMin(*yPos, item.y() + item.height() + m_rowSpacing);
72+ *yPos = qMin(*yPos, item.y() + item.height() + rowSpacing());
27273 *modelIndex = qMax(*modelIndex, item.m_modelIndex + 1);
27374 } else {
27475 *yPos = 0;
@@ -287,7 +88,7 @@ void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
28788 const auto &column = m_columnVisibleItems[i];
28889 if (!column.isEmpty()) {
28990 const ViewItem &item = column.first();
290− const auto itemTopPos = item.y() - m_rowSpacing;
91+ const auto itemTopPos = item.y() - rowSpacing();
29192 if (itemTopPos > *yPos) {
29293 *yPos = itemTopPos;
29394 *modelIndex = item.m_modelIndex - 1;
@@ -309,51 +110,19 @@ void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
309110 }
310111}
311112
312−bool VerticalJournal::addVisibleItems(qreal fillFrom, qreal fillTo, bool asynchronous)
313−{
314− if (!delegate())
315− return false;
316−
317− if (m_delegateModel->count() == 0)
318− return false;
319−
320− int modelIndex;
321− qreal yPos;
322− findBottomModelIndexToAdd(&modelIndex, &yPos);
323− bool changed = false;
324− while (modelIndex < m_delegateModel->count() && yPos <= fillTo) {
325− if (!createItem(modelIndex, asynchronous))
326− break;
327−
328− changed = true;
329− findBottomModelIndexToAdd(&modelIndex, &yPos);
330− }
331−
332− findTopModelIndexToAdd(&modelIndex, &yPos);
333− while (modelIndex >= 0 && yPos > fillFrom) {
334− if (!createItem(modelIndex, asynchronous))
335− break;
336−
337− changed = true;
338− findTopModelIndexToAdd(&modelIndex, &yPos);
339− }
340−
341− return changed;
342−}
343−
344−bool VerticalJournal::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
113+bool VerticalJournal::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
345114{
346115 bool changed = false;
347116
348117 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
349118 QList<ViewItem> &column = m_columnVisibleItems[i];
350− while (!column.isEmpty() && column.first().y() + column.first().height() < bufferFrom) {
351− releaseItem(column.takeFirst());
119+ while (!column.isEmpty() && column.first().y() + column.first().height() < bufferFromY) {
120+ releaseItem(column.takeFirst().m_item);
352121 changed = true;
353122 }
354123
355− while (!column.isEmpty() && column.last().y() > bufferTo) {
356− releaseItem(column.takeLast());
124+ while (!column.isEmpty() && column.last().y() > bufferToY) {
125+ releaseItem(column.takeLast().m_item);
357126 changed = true;
358127 }
359128 }
@@ -361,53 +130,20 @@ bool VerticalJournal::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
361130 return changed;
362131}
363132
364−QQuickItem *VerticalJournal::createItem(int modelIndex, bool asynchronous)
133+void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
365134{
366− if (asynchronous && m_asyncRequestedIndex != -1)
367− return nullptr;
368−
369− m_asyncRequestedIndex = -1;
370−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
371− QQuickItem *item = m_delegateModel->item(modelIndex, asynchronous);
372−#else
373− QObject* object = m_delegateModel->object(modelIndex, asynchronous);
374− QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
375−#endif
376− if (!item) {
377−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
378− m_asyncRequestedIndex = modelIndex;
379−#else
380− if (object) {
381− m_delegateModel->release(object);
382− if (!m_delegateValidated) {
383− m_delegateValidated = true;
384− QObject* delegateObj = delegate();
385− qmlInfo(delegateObj ? delegateObj : this) << "Delegate must be of Item type";
386− }
387− } else {
388− m_asyncRequestedIndex = modelIndex;
389− }
390−#endif
391− return nullptr;
392− } else {
393− if (item->width() != m_columnWidth) {
394− qWarning() << "Item" << modelIndex << "width is not the one that the columnWidth mandates, resetting it";
395− item->setWidth(m_columnWidth);
396− }
397− positionItem(modelIndex, item);
398− return item;
135+ if (item->width() != m_columnWidth) {
136+ qWarning() << "Item" << modelIndex << "width is not the one that the columnWidth mandates, resetting it";
137+ item->setWidth(m_columnWidth);
399138 }
400−}
401139
402−void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
403−{
404140 // Check if we add it to the bottom of existing column items
405141 const QList<ViewItem> &firstColumn = m_columnVisibleItems[0];
406− qreal columnToAddY = !firstColumn.isEmpty() ? firstColumn.last().y() + firstColumn.last().height() : -m_rowSpacing;
142+ qreal columnToAddY = !firstColumn.isEmpty() ? firstColumn.last().y() + firstColumn.last().height() : -rowSpacing();
407143 int columnToAddTo = 0;
408144 for (int i = 1; i < m_columnVisibleItems.count(); ++i) {
409145 const QList<ViewItem> &column = m_columnVisibleItems[i];
410− const qreal iY = !column.isEmpty() ? column.last().y() + column.last().height() : -m_rowSpacing;
146+ const qreal iY = !column.isEmpty() ? column.last().y() + column.last().height() : -rowSpacing();
411147 if (iY < columnToAddY) {
412148 columnToAddTo = i;
413149 columnToAddY = iY;
@@ -416,8 +152,8 @@ void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
416152
417153 const QList<ViewItem> &columnToAdd = m_columnVisibleItems[columnToAddTo];
418154 if (columnToAdd.isEmpty() || columnToAdd.last().m_modelIndex < modelIndex) {
419− item->setX(columnToAddTo * (m_columnWidth + m_columnSpacing));
420− item->setY(columnToAddY + m_rowSpacing);
155+ item->setX(columnToAddTo * (m_columnWidth + columnSpacing()));
156+ item->setY(columnToAddY + rowSpacing());
421157
422158 m_columnVisibleItems[columnToAddTo] << ViewItem(item, modelIndex);
423159 m_indexColumnMap[modelIndex] = columnToAddTo;
@@ -426,8 +162,8 @@ void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
426162 columnToAddTo = m_indexColumnMap[modelIndex];
427163 columnToAddY = m_columnVisibleItems[columnToAddTo].first().y();
428164
429− item->setX(columnToAddTo * (m_columnWidth + m_columnSpacing));
430− item->setY(columnToAddY - m_rowSpacing - item->height());
165+ item->setX(columnToAddTo * (m_columnWidth + columnSpacing()));
166+ item->setY(columnToAddY - rowSpacing() - item->height());
431167
432168 m_columnVisibleItems[columnToAddTo].prepend(ViewItem(item, modelIndex));
433169 }
@@ -439,29 +175,14 @@ void VerticalJournal::cleanupExistingItems()
439175 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
440176 QList<ViewItem> &column = m_columnVisibleItems[i];
441177 Q_FOREACH(const ViewItem &item, column)
442− releaseItem(item);
178+ releaseItem(item.m_item);
443179 column.clear();
444180 }
445181 m_indexColumnMap.clear();
446182}
447183
448−void VerticalJournal::releaseItem(const ViewItem &item)
449−{
450−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
451− QQuickVisualModel::ReleaseFlags flags = m_delegateModel->release(item.m_item);
452− if (flags & QQuickVisualModel::Destroyed) {
453−#else
454− QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item.m_item);
455− if (flags & QQmlDelegateModel::Destroyed) {
456−#endif
457− item.m_item->setParentItem(nullptr);
458− }
459−}
460−
461184void VerticalJournal::calculateImplicitHeight()
462185{
463− m_implicitHeightDirty = false;
464−
465186 int lastModelIndex = -1;
466187 qreal bottomMostY = 0;
467188 Q_FOREACH(const auto &column, m_columnVisibleItems) {
@@ -479,144 +200,81 @@ void VerticalJournal::calculateImplicitHeight()
479200 }
480201}
481202
482−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
483−void VerticalJournal::itemCreated(int modelIndex, QQuickItem *item)
484−{
485−#else
486−void VerticalJournal::itemCreated(int modelIndex, QObject *object)
203+void VerticalJournal::doRelayout()
487204{
488− QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
489− if (!item) {
490− qWarning() << "VerticalJournal::itemCreated got a non item for index" << modelIndex;
491− return;
205+ QList<ViewItem> allItems;
206+ Q_FOREACH(const auto &column, m_columnVisibleItems)
207+ allItems << column;
208+
209+ qSort(allItems);
210+
211+ const int nColumns = qMax(1., floor((double)(width() + columnSpacing()) / (m_columnWidth + columnSpacing())));
212+ m_columnVisibleItems.resize(nColumns);
213+ m_indexColumnMap.clear();
214+ for (int i = 0; i < nColumns; ++i)
215+ m_columnVisibleItems[i].clear();
216+
217+ // If the first of allItems doesn't contain index 0 we need to drop them
218+ // all since we can't consistently relayout without the first item being there
219+
220+ if (!allItems.isEmpty()) {
221+ if (allItems.first().m_modelIndex == 0) {
222+ Q_FOREACH(const ViewItem &item, allItems)
223+ positionItem(item.m_modelIndex, item.m_item);
224+ } else {
225+ Q_FOREACH(const ViewItem &item, allItems)
226+ releaseItem(item.m_item);
227+ }
492228 }
493−#endif
494− item->setParentItem(this);
495− if (modelIndex == m_asyncRequestedIndex) {
496− createItem(modelIndex, false);
497− m_implicitHeightDirty = true;
498− polish();
229+}
230+
231+void VerticalJournal::updateItemCulling(qreal visibleFromY, qreal visibleToY)
232+{
233+ Q_FOREACH(const auto &column, m_columnVisibleItems) {
234+ Q_FOREACH(const ViewItem &item, column) {
235+ const bool cull = item.y() + item.height() <= visibleFromY || item.y() >= visibleToY;
236+ QQuickItemPrivate::get(item.m_item)->setCulled(cull);
237+ }
499238 }
500239}
501240
502241#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
503−void VerticalJournal::onModelUpdated(const QQuickChangeSet &changeSet, bool reset)
242+void VerticalJournal::processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes)
504243#else
505−void VerticalJournal::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
244+void VerticalJournal::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
506245#endif
507246{
508− if (reset) {
509− cleanupExistingItems();
510− } else {
511247#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
512− Q_FOREACH(const QQuickChangeSet::Remove &remove, changeSet.removes()) {
248+ Q_FOREACH(const QQuickChangeSet::Remove &remove, removes) {
513249#else
514− Q_FOREACH(const QQmlChangeSet::Remove &remove, changeSet.removes()) {
250+ Q_FOREACH(const QQmlChangeSet::Remove &remove, removes) {
515251#endif
516− for (int i = remove.count - 1; i >= 0; --i) {
517− const int indexToRemove = remove.index + i;
518− // Since we only support removing from the end, indexToRemove
519− // must refer to the last item of one of the columns or
520− // be bigger than them (because it's not in the viewport and
521− // thus we have not created a delegate for it)
522− bool found = false;
523− int lastCreatedIndex = INT_MIN;
524− for (int i = 0; !found && i < m_columnVisibleItems.count(); ++i) {
525− QList<ViewItem> &column = m_columnVisibleItems[i];
526− if (!column.isEmpty()) {
527− const int lastColumnIndex = column.last().m_modelIndex;
528− if (lastColumnIndex == indexToRemove) {
529− releaseItem(column.takeLast());
530− found = true;
531− }
532− lastCreatedIndex = qMax(lastCreatedIndex, lastColumnIndex);
533− }
534− }
535− if (!found) {
536− if (indexToRemove < lastCreatedIndex) {
537− qFatal("VerticalJournal only supports removal from the end of the model");
538− } else {
539− m_implicitHeightDirty = true;
252+ for (int i = remove.count - 1; i >= 0; --i) {
253+ const int indexToRemove = remove.index + i;
254+ // Since we only support removing from the end, indexToRemove
255+ // must refer to the last item of one of the columns or
256+ // be bigger than them (because it's not in the viewport and
257+ // thus we have not created a delegate for it)
258+ bool found = false;
259+ int lastCreatedIndex = INT_MIN;
260+ for (int i = 0; !found && i < m_columnVisibleItems.count(); ++i) {
261+ QList<ViewItem> &column = m_columnVisibleItems[i];
262+ if (!column.isEmpty()) {
263+ const int lastColumnIndex = column.last().m_modelIndex;
264+ if (lastColumnIndex == indexToRemove) {
265+ releaseItem(column.takeLast().m_item);
266+ found = true;
540267 }
268+ lastCreatedIndex = qMax(lastCreatedIndex, lastColumnIndex);
541269 }
542270 }
543− }
544− }
545− polish();
546−}
547−
548−
549−void VerticalJournal::relayout()
550−{
551− m_needsRelayout = true;
552− polish();
553−}
554−
555−void VerticalJournal::onHeightChanged()
556−{
557− polish();
558−}
559−
560−void VerticalJournal::updatePolish()
561−{
562− if (!model())
563− return;
564−
565− if (m_needsRelayout) {
566− m_implicitHeightDirty = true;
567−
568− QList<ViewItem> allItems;
569− Q_FOREACH(const auto &column, m_columnVisibleItems)
570− allItems << column;
571−
572− qSort(allItems);
573−
574− const int nColumns = qMax(1., floor((double)(width() + m_columnSpacing) / (m_columnWidth + m_columnSpacing)));
575− m_columnVisibleItems.resize(nColumns);
576− m_indexColumnMap.clear();
577− for (int i = 0; i < nColumns; ++i)
578− m_columnVisibleItems[i].clear();
579−
580− // If the first of allItems doesn't contain index 0 we need to drop them
581− // all since we can't consistently relayout without the first item being there
582−
583− if (!allItems.isEmpty()) {
584− if (allItems.first().m_modelIndex == 0) {
585− Q_FOREACH(const ViewItem &item, allItems)
586− positionItem(item.m_modelIndex, item.m_item);
587− } else {
588− Q_FOREACH(const ViewItem &item, allItems)
589− releaseItem(item);
271+ if (!found) {
272+ if (indexToRemove < lastCreatedIndex) {
273+ qFatal("VerticalJournal only supports removal from the end of the model");
274+ } else {
275+ setImplicitHeightDirty();
276+ }
590277 }
591278 }
592−
593− m_needsRelayout = false;
594− }
595−
596− refill();
597−
598− const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
599− const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
600− const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
601− Q_FOREACH(const auto &column, m_columnVisibleItems) {
602− Q_FOREACH(const ViewItem &item, column) {
603− QQuickItemPrivate::get(item.m_item)->setCulled(item.y() + item.height() <= from || item.y() >= to);
604− }
605279 }
606−
607− if (m_implicitHeightDirty) {
608− calculateImplicitHeight();
609− }
610−}
611−
612−void VerticalJournal::componentComplete()
613−{
614− if (m_delegateModel)
615− m_delegateModel->componentComplete();
616−
617− QQuickItem::componentComplete();
618−
619− m_needsRelayout = true;
620−
621− polish();
622280}
plugins/DashViews/verticaljournal.h+13−91
@@ -17,17 +17,7 @@
1717#ifndef VERTICALJOURNAL_H
1818#define VERTICALJOURNAL_H
1919
20−#include <QQuickItem>
21−
22−class QAbstractItemModel;
23−class QQmlComponent;
24−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
25−class QQuickChangeSet;
26−class QQuickVisualDataModel;
27−#else
28−class QQmlChangeSet;
29−class QQmlDelegateModel;
30−#endif
20+#include "abstractjournal.h"
3121
3222 /** A vertical journal is a view that creates delegates
3323 * based on a model and layouts them in columns following
@@ -60,75 +50,23 @@ class QQmlDelegateModel;
6050 * +-----+
6151 *
6252 */
63− class VerticalJournal : public QQuickItem
53+
54+class VerticalJournal : public AbstractJournal
6455{
6556 Q_OBJECT
6657
67− Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
68− Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
6958 Q_PROPERTY(qreal columnWidth READ columnWidth WRITE setColumnWidth NOTIFY columnWidthChanged)
70− Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
71− Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
72− Q_PROPERTY(qreal delegateCreationBegin READ delegateCreationBegin
73− WRITE setDelegateCreationBegin
74− NOTIFY delegateCreationBeginChanged
75− RESET resetDelegateCreationBegin)
76− Q_PROPERTY(qreal delegateCreationEnd READ delegateCreationEnd
77− WRITE setDelegateCreationEnd
78− NOTIFY delegateCreationEndChanged
79− RESET resetDelegateCreationEnd)
8059
8160friend class VerticalJournalTest;
8261
8362public:
8463 VerticalJournal();
8564
86− QAbstractItemModel *model() const;
87− void setModel(QAbstractItemModel *model);
88−
89− QQmlComponent *delegate() const;
90− void setDelegate(QQmlComponent *delegate);
91−
9265 qreal columnWidth() const;
9366 void setColumnWidth(qreal columnWidth);
9467
95− qreal columnSpacing() const;
96− void setColumnSpacing(qreal columnSpacing);
97−
98− qreal rowSpacing() const;
99− void setRowSpacing(qreal rowSpacing);
100−
101− qreal delegateCreationBegin() const;
102− void setDelegateCreationBegin(qreal);
103− void resetDelegateCreationBegin();
104−
105− qreal delegateCreationEnd() const;
106− void setDelegateCreationEnd(qreal);
107− void resetDelegateCreationEnd();
108−
10968Q_SIGNALS:
110− void modelChanged();
111− void delegateChanged();
11269 void columnWidthChanged();
113− void columnSpacingChanged();
114− void rowSpacingChanged();
115− void delegateCreationBeginChanged();
116− void delegateCreationEndChanged();
117−
118−protected:
119− void updatePolish();
120− void componentComplete();
121−
122−private Q_SLOTS:
123−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
124− void itemCreated(int modelIndex, QQuickItem *item);
125− void onModelUpdated(const QQuickChangeSet &changeSet, bool reset);
126−#else
127− void itemCreated(int modelIndex, QObject *object);
128− void onModelUpdated(const QQmlChangeSet &changeSet, bool reset);
129−#endif
130− void relayout();
131− void onHeightChanged();
13270
13371private:
13472 class ViewItem
@@ -144,39 +82,23 @@ private:
14482 int m_modelIndex;
14583 };
14684
147− void createDelegateModel();
148− void refill();
149− void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos);
150− void findTopModelIndexToAdd(int *modelIndex, qreal *yPos);
151− bool addVisibleItems(qreal fillFrom, qreal fillTo, bool asynchronous);
152− bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
153− QQuickItem *createItem(int modelIndex, bool asynchronous);
154− void positionItem(int modelIndex, QQuickItem *item);
155− void cleanupExistingItems();
156− void releaseItem(const ViewItem &item);
157− void calculateImplicitHeight();
158−
85+ void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
86+ void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
87+ bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
88+ void positionItem(int modelIndex, QQuickItem *item) override;
89+ void cleanupExistingItems() override;
90+ void calculateImplicitHeight() override;
91+ void doRelayout() override;
92+ void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
15993#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
160− QQuickVisualDataModel *m_delegateModel;
94+ void processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes) override;
16195#else
162− QQmlDelegateModel *m_delegateModel;
96+ void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) override;
16397#endif
16498
165− // Index we are waiting because we requested it asynchronously
166− int m_asyncRequestedIndex;
167−
16899 QVector<QList<ViewItem>> m_columnVisibleItems;
169100 QHash<int, int> m_indexColumnMap;
170101 int m_columnWidth;
171− int m_columnSpacing;
172− int m_rowSpacing;
173− qreal m_delegateCreationBegin;
174− qreal m_delegateCreationEnd;
175− bool m_delegateCreationBeginValid;
176− bool m_delegateCreationEndValid;
177− bool m_needsRelayout;
178− bool m_delegateValidated;
179− bool m_implicitHeightDirty;
180102};
181103
182104#endif
po/cs.po+9−7
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-10-08 08:09+0000\n"
11+"PO-Revision-Date: 2014-01-08 10:11+0000\n"
1212"Last-Translator: Tadeáš Pařík <tadeas.parik@gmail.com>\n"
1313"Language-Team: Czech <cs@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19−"X-Generator: Launchpad (build 16877)\n"
18+"X-Launchpad-Export-Date: 2014-01-09 05:41+0000\n"
19+"X-Generator: Launchpad (build 16884)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
@@ -44,7 +44,7 @@ msgstr "Uzavřít"
4444
4545#: Components/EdgeDemo.qml:171
4646msgid "Swipe up again to close the settings screen"
47−msgstr ""
47+msgstr "Přetažením nahoru dojde k uzavření obrazovky nastavení"
4848
4949#: Components/EdgeDemo.qml:198
5050msgid "Left edge"
@@ -63,6 +63,8 @@ msgid ""
6363"You have now mastered the edge gestures and can start using the "
6464"phone<br><br>Tap anywhere to start"
6565msgstr ""
66+"Nyní jste si osvojili gesta potřebná k ovládání telefonu, můžete ho začít "
67+"používat <br><br>Klepnutím kdekoli začnete"
6668
6769#: Components/PageHeader.qml:364
6870msgid "Recent searches"
@@ -138,7 +140,7 @@ msgstr "Mluvte..."
138140
139141#: Notifications/NotificationMenuItemFactory.qml:89
140142msgid "Show password"
141−msgstr ""
143+msgstr "Zobrazit heslo"
142144
143145#: Panel/SearchIndicator.qml:27
144146msgid "Search"
@@ -147,11 +149,11 @@ msgstr "Hledat"
147149#: plugins/Unity/Launcher/launcheritem.cpp:43
148150#: plugins/Unity/Launcher/launcheritem.cpp:73
149151msgid "Lock to Launcher"
150−msgstr ""
152+msgstr "Přidat do Launcheru"
151153
152154#: plugins/Unity/Launcher/launcheritem.cpp:73
153155msgid "Unlock from Launcher"
154−msgstr ""
156+msgstr "Odebrat z Launcheru"
155157
156158#: Shell.qml:439
157159#, qt-format
po/hu.po+4−4
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2014-01-04 15:30+0000\n"
12−"Last-Translator: Péter MÁTÓ <mato.peter@gmail.com>\n"
11+"PO-Revision-Date: 2014-01-07 18:02+0000\n"
12+"Last-Translator: Gabor Kelemen <kelemeng@gnome.hu>\n"
1313"Language-Team: Hungarian <hu@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2014-01-05 05:50+0000\n"
18+"X-Launchpad-Export-Date: 2014-01-08 05:43+0000\n"
1919"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
@@ -159,4 +159,4 @@ msgstr "Eltávolítás az Indítóból"
159159#: Shell.qml:439
160160#, qt-format
161161msgid "Please enter %1"
162−msgstr ""
162+msgstr "Adja meg a következőt: %1"
po/ru.po+1−1
@@ -16,7 +16,7 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2014-01-07 05:33+0000\n"
19+"X-Launchpad-Export-Date: 2014-01-08 05:43+0000\n"
2020"X-Generator: Launchpad (build 16877)\n"
2121
2222#: Components/EdgeDemoOverlay.qml:151
src/CMakeLists.txt+4−1
@@ -5,11 +5,14 @@ include_directories(
55 ${UNITY-MIR_INCLUDE_DIRS}
66)
77
8+file(GLOB_RECURSE QML_FILES ${CMAKE_SOURCE_DIR}/qml/*)
9+
810add_executable(${SHELL_APP}
911 ApplicationArguments.h
1012 main.cpp
1113 MouseTouchAdaptor.cpp
12− )
14+ ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
15+)
1316
1417qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
1518pkg_check_modules(XCB REQUIRED xcb)
tests/plugins/DashViews/CMakeLists.txt+37−8
@@ -40,15 +40,43 @@ qt5_use_modules(listviewwithpageheadertestsectionexternalmodelExec Test Core Qml
4040target_link_libraries(listviewwithpageheadertestsectionexternalmodelExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
4141add_custom_target(testlistviewwithpageheadersectionexternalmodel ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestsectionexternalmodelExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheadersectionexternalmodel.xml,xunitxml -o -,txt)
4242
43−add_executable(verticaljournaltestExec verticaljournaltest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/verticaljournal.cpp)
44−qt5_use_modules(verticaljournaltestExec Test Core Qml)
45−target_link_libraries(verticaljournaltestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
46−add_custom_target(testVerticalJournal ${CMAKE_CURRENT_BINARY_DIR}/verticaljournaltestExec -o ${CMAKE_BINARY_DIR}/testVerticalJournal.xml,xunitxml -o -,txt)
43+macro(add_journal_try_test FILENAME)
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}")
4748
48−add_executable(verticaljournaltryExec verticaljournaltry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/verticaljournal.cpp)
49−qt5_use_modules(verticaljournaltryExec Test Core Qml)
50−target_link_libraries(verticaljournaltryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
51−add_custom_target(tryVerticalJournal ${CMAKE_CURRENT_BINARY_DIR}/verticaljournaltryExec -o ${CMAKE_BINARY_DIR}/testVerticalJournal.xml,xunitxml -o -,txt)
49+ add_executable(${FILENAME}testExec
50+ ${FILENAME}test.cpp
51+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
52+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractjournal.cpp
53+ )
54+ qt5_use_modules(${FILENAME}testExec Test Core Qml)
55+ target_link_libraries(${FILENAME}testExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
56+ add_custom_target(test${CUSTOM_TARGET_NAME}
57+ ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}testExec
58+ -o ${CMAKE_BINARY_DIR}/testVerticalJournal.xml,xunitxml
59+ -o -,txt
60+ DEPENDS ${FILENAME}testExec DashViews-qml
61+ )
62+
63+ add_executable(${FILENAME}tryExec
64+ ${FILENAME}try.cpp
65+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
66+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractjournal.cpp
67+ )
68+ qt5_use_modules(${FILENAME}tryExec Test Core Qml)
69+ target_link_libraries(${FILENAME}tryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
70+ add_custom_target(try${CUSTOM_TARGET_NAME}
71+ ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}tryExec
72+ -o ${CMAKE_BINARY_DIR}/testVerticalJournal.xml,xunitxml
73+ -o -,txt
74+ DEPENDS ${FILENAME}tryExec DashViews-qml
75+ )
76+endmacro()
77+
78+add_journal_try_test(verticaljournal)
79+add_journal_try_test(horizontaljournal)
5280
5381set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
5482add_qml_test(. listviewwithpageheaderqml IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
@@ -58,3 +86,4 @@ add_dependencies(qmluitests testlistviewwithpageheadersection)
5886add_dependencies(qmluitests testlistviewwithpageheadersectionexternalmodel)
5987add_dependencies(qmluitests testlistviewwithpageheaderqml)
6088add_dependencies(qmluitests testVerticalJournal)
89+add_dependencies(qmluitests testHorizontalJournal)
tests/plugins/DashViews/horizontaljournaltest.cppnew file+465
@@ -0,0 +1,465 @@
1+/*
2+ * Copyright (C) 2013 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 "horizontaljournal.h"
32+
33+class WidthModel : public QAbstractListModel {
34+public:
35+ QHash<int, QByteArray> roleNames() const
36+ {
37+ QHash<int, QByteArray> roles;
38+ roles.insert(Qt::DisplayRole, "modelWidth");
39+ return roles;
40+ }
41+
42+ int rowCount(const QModelIndex & /*parent*/) const
43+ {
44+ return m_list.count();
45+ }
46+
47+ QVariant data(const QModelIndex &index, int /*role*/) const
48+ {
49+ return m_list[index.row()];
50+ }
51+
52+ QStringList stringList() const
53+ {
54+ return m_list;
55+ }
56+
57+ void setStringList(const QStringList &list)
58+ {
59+ beginResetModel();
60+ m_list = list;
61+ endResetModel();
62+ }
63+
64+ void addString(const QString& string)
65+ {
66+ beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
67+ m_list << string;
68+ endInsertRows();
69+ }
70+
71+ void removeLast()
72+ {
73+ beginRemoveRows(QModelIndex(), m_list.count() - 1, m_list.count() - 1);
74+ m_list.takeLast();
75+ endRemoveRows();
76+ }
77+
78+private:
79+ QStringList m_list;
80+};
81+
82+class HorizontalJournalTest : public QObject
83+{
84+ Q_OBJECT
85+
86+private:
87+ void verifyItem(const QQuickItem *item, int modelIndex, qreal x, qreal y, bool visible)
88+ {
89+ QTRY_COMPARE(item->x(), x);
90+ QTRY_COMPARE(item->y(), y);
91+ QTRY_COMPARE(item->width(), model->stringList()[modelIndex].toDouble());
92+ QTRY_COMPARE(QQuickItemPrivate::get(item)->culled, !visible);
93+ }
94+
95+ void checkInitialPositions()
96+ {
97+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
98+ QTRY_COMPARE(hj->m_visibleItems.count(), 14);
99+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
100+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
101+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
102+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
103+ verifyItem(hj->m_visibleItems[ 4], 4, 325, 0, true);
104+ verifyItem(hj->m_visibleItems[ 5], 5, 375, 0, true);
105+ verifyItem(hj->m_visibleItems[ 6], 6, 0, 160, true);
106+ verifyItem(hj->m_visibleItems[ 7], 7, 210, 160, true);
107+ verifyItem(hj->m_visibleItems[ 8], 8, 0, 320, true);
108+ verifyItem(hj->m_visibleItems[ 9], 9, 170, 320, true);
109+ verifyItem(hj->m_visibleItems[10], 10, 200, 320, true);
110+ verifyItem(hj->m_visibleItems[11], 11, 230, 320, true);
111+ verifyItem(hj->m_visibleItems[12], 12, 305, 320, true);
112+ verifyItem(hj->m_visibleItems[13], 13, 0, 480, false);
113+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 4);
114+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 375.);
115+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 7], 210.);
116+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[12], 305.);
117+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[13], 0.);
118+ QCOMPARE(hj->implicitHeight(), 900.);
119+ }
120+
121+private Q_SLOTS:
122+ void init()
123+ {
124+ view = new QQuickView();
125+ view->setResizeMode(QQuickView::SizeRootObjectToView);
126+ view->engine()->addImportPath(BUILT_PLUGINS_DIR);
127+
128+ model = new WidthModel();
129+ QStringList heightList;
130+ heightList << "100" << "50" << "125" << "10" << "40" << "70" << "200" << "110" << "160" << "20" << "20" << "65" << "80" << "200" << "300" << "130" << "400" << "300" << "500" << "10";
131+ model->setStringList(heightList);
132+
133+ view->setSource(QUrl::fromLocalFile(DASHVIEWSTEST_FOLDER "/horizontaljournaltest.qml"));
134+
135+ view->show();
136+ view->resize(470, 400);
137+ QTest::qWaitForWindowExposed(view);
138+
139+ hj = dynamic_cast<HorizontalJournal*>(view->rootObject()->findChild<QObject*>("hj"));
140+ hj->setModel(model);
141+
142+ checkInitialPositions();
143+ }
144+
145+ void cleanup()
146+ {
147+ delete view;
148+ delete model;
149+ }
150+
151+ void testWidthResize()
152+ {
153+ view->resize(629, 400);
154+ QTRY_COMPARE(hj->width(), 629.);
155+
156+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
157+ QTRY_COMPARE(hj->m_visibleItems.count(), 17);
158+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
159+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
160+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
161+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
162+ verifyItem(hj->m_visibleItems[ 4], 4, 325, 0, true);
163+ verifyItem(hj->m_visibleItems[ 5], 5, 375, 0, true);
164+ verifyItem(hj->m_visibleItems[ 6], 6, 0, 160, true);
165+ verifyItem(hj->m_visibleItems[ 7], 7, 210, 160, true);
166+ verifyItem(hj->m_visibleItems[ 8], 8, 330, 160, true);
167+ verifyItem(hj->m_visibleItems[ 9], 9, 500, 160, true);
168+ verifyItem(hj->m_visibleItems[10], 10, 530, 160, true);
169+ verifyItem(hj->m_visibleItems[11], 11, 560, 160, true);
170+ verifyItem(hj->m_visibleItems[12], 12, 0, 320, true);
171+ verifyItem(hj->m_visibleItems[13], 13, 90, 320, true);
172+ verifyItem(hj->m_visibleItems[14], 14, 300, 320, true);
173+ verifyItem(hj->m_visibleItems[15], 15, 0, 480, false);
174+ verifyItem(hj->m_visibleItems[16], 16, 140, 480, false);
175+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 4);
176+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 375.);
177+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[11], 560.);
178+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[14], 300.);
179+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[16], 140.);
180+ QCOMPARE(hj->implicitHeight(), 630. + 3. * 630. / 17.);
181+
182+ view->resize(470, 400);
183+
184+ // This is exactly the same block as the first again
185+ checkInitialPositions();
186+ }
187+
188+ void testColumnSpacing()
189+ {
190+ hj->setColumnSpacing(11);
191+
192+ QTRY_COMPARE(hj->m_needsRelayout, false);
193+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
194+ QTRY_COMPARE(hj->m_visibleItems.count(), 14);
195+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
196+ verifyItem(hj->m_visibleItems[ 1], 1, 111, 0, true);
197+ verifyItem(hj->m_visibleItems[ 2], 2, 172, 0, true);
198+ verifyItem(hj->m_visibleItems[ 3], 3, 308, 0, true);
199+ verifyItem(hj->m_visibleItems[ 4], 4, 329, 0, true);
200+ verifyItem(hj->m_visibleItems[ 5], 5, 380, 0, true);
201+ verifyItem(hj->m_visibleItems[ 6], 6, 0, 160, true);
202+ verifyItem(hj->m_visibleItems[ 7], 7, 211, 160, true);
203+ verifyItem(hj->m_visibleItems[ 8], 8, 0, 320, true);
204+ verifyItem(hj->m_visibleItems[ 9], 9, 171, 320, true);
205+ verifyItem(hj->m_visibleItems[10], 10, 202, 320, true);
206+ verifyItem(hj->m_visibleItems[11], 11, 233, 320, true);
207+ verifyItem(hj->m_visibleItems[12], 12, 309, 320, true);
208+ verifyItem(hj->m_visibleItems[13], 13, 0, 480, false);
209+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 4);
210+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 380.);
211+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 7], 211.);
212+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[12], 309.);
213+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[13], 0.);
214+ QCOMPARE(hj->implicitHeight(), 900.);
215+
216+ hj->setColumnSpacing(10);
217+ QTRY_COMPARE(hj->m_needsRelayout, false);
218+
219+ // This is exactly the same block as the first again
220+ checkInitialPositions();
221+ }
222+
223+ void testRowSpacing()
224+ {
225+ hj->setRowSpacing(11);
226+
227+ QTRY_COMPARE(hj->m_needsRelayout, false);
228+ QTRY_COMPARE(hj->m_needsRelayout, false);
229+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
230+ QTRY_COMPARE(hj->m_visibleItems.count(), 14);
231+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
232+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
233+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
234+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
235+ verifyItem(hj->m_visibleItems[ 4], 4, 325, 0, true);
236+ verifyItem(hj->m_visibleItems[ 5], 5, 375, 0, true);
237+ verifyItem(hj->m_visibleItems[ 6], 6, 0, 161, true);
238+ verifyItem(hj->m_visibleItems[ 7], 7, 210, 161, true);
239+ verifyItem(hj->m_visibleItems[ 8], 8, 0, 322, true);
240+ verifyItem(hj->m_visibleItems[ 9], 9, 170, 322, true);
241+ verifyItem(hj->m_visibleItems[10], 10, 200, 322, true);
242+ verifyItem(hj->m_visibleItems[11], 11, 230, 322, true);
243+ verifyItem(hj->m_visibleItems[12], 12, 305, 322, true);
244+ verifyItem(hj->m_visibleItems[13], 13, 0, 483, false);
245+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 4);
246+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 375.);
247+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 7], 210.);
248+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[12], 305.);
249+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[13], 0.);
250+ QCOMPARE(hj->implicitHeight(), 633. + 6. * 633. / 14.);
251+
252+ hj->setRowSpacing(10);
253+ QTRY_COMPARE(hj->m_needsRelayout, false);
254+
255+ // This is exactly the same block as the first again
256+ checkInitialPositions();
257+ }
258+
259+ void testDelegateCreationRanges()
260+ {
261+ hj->setDelegateCreationBegin(300);
262+ hj->setDelegateCreationEnd(view->height());
263+
264+ QTRY_COMPARE(hj->m_firstVisibleIndex, 6);
265+ QTRY_COMPARE(hj->m_visibleItems.count(), 7);
266+ verifyItem(hj->m_visibleItems[0], 6, 0, 160, true);
267+ verifyItem(hj->m_visibleItems[1], 7, 210, 160, true);
268+ verifyItem(hj->m_visibleItems[2], 8, 0, 320, true);
269+ verifyItem(hj->m_visibleItems[3], 9, 170, 320, true);
270+ verifyItem(hj->m_visibleItems[4], 10, 200, 320, true);
271+ verifyItem(hj->m_visibleItems[5], 11, 230, 320, true);
272+ verifyItem(hj->m_visibleItems[6], 12, 305, 320, true);
273+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 375.);
274+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 7], 210.);
275+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[12], 305.);
276+
277+ hj->resetDelegateCreationBegin();
278+ hj->resetDelegateCreationEnd();
279+
280+ // This is exactly the same block as the first again
281+ checkInitialPositions();
282+ }
283+
284+
285+ void testRowHeightChange()
286+ {
287+ hj->setRowHeight(200);
288+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
289+ QTRY_COMPARE(hj->m_visibleItems.count(), 13);
290+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
291+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
292+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
293+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
294+ verifyItem(hj->m_visibleItems[ 4], 4, 325, 0, true);
295+ verifyItem(hj->m_visibleItems[ 5], 5, 375, 0, true);
296+ verifyItem(hj->m_visibleItems[ 6], 6, 0, 210, true);
297+ verifyItem(hj->m_visibleItems[ 7], 7, 210, 210, true);
298+ verifyItem(hj->m_visibleItems[ 8], 8, 0, 420, false);
299+ verifyItem(hj->m_visibleItems[ 9], 9, 170, 420, false);
300+ verifyItem(hj->m_visibleItems[10], 10, 200, 420, false);
301+ verifyItem(hj->m_visibleItems[11], 11, 230, 420, false);
302+ verifyItem(hj->m_visibleItems[12], 12, 305, 420, false);
303+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 3);
304+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 375.);
305+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 7], 210.);
306+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[12], 305.);
307+ QCOMPARE(hj->implicitHeight(), 620. + 7. * 620. / 13.);
308+
309+ hj->setRowHeight(150);
310+
311+ // This is exactly the same block as the first again
312+ checkInitialPositions();
313+ }
314+
315+ void testChangeModel()
316+ {
317+ WidthModel *model2 = new WidthModel();
318+ QStringList list2;
319+ list2 << "100" << "50" << "125" << "25" << "50" << "50";
320+ model2->setStringList(list2);
321+ hj->setModel(model2);
322+ delete model;
323+ model = model2;
324+
325+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
326+ QTRY_COMPARE(hj->m_visibleItems.count(), 6);
327+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
328+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
329+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
330+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
331+ verifyItem(hj->m_visibleItems[ 4], 4, 340, 0, true);
332+ verifyItem(hj->m_visibleItems[ 5], 5, 400, 0, true);
333+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 0);
334+ QCOMPARE(hj->implicitHeight(), 150.);
335+ }
336+
337+ void testModelReset()
338+ {
339+ QStringList heightList;
340+ heightList << "100" << "50" << "125" << "25" << "50" << "50";
341+ model->setStringList(heightList);
342+
343+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
344+ QTRY_COMPARE(hj->m_visibleItems.count(), 6);
345+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
346+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
347+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
348+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
349+ verifyItem(hj->m_visibleItems[ 4], 4, 340, 0, true);
350+ verifyItem(hj->m_visibleItems[ 5], 5, 400, 0, true);
351+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 0);
352+ QCOMPARE(hj->implicitHeight(), 150.);
353+ }
354+
355+ void testModelRemoveLastNonVisible()
356+ {
357+ model->removeLast();
358+
359+ // This is the same than checkInitialPositions but
360+ // with a different implicitHeight since there's an item less
361+ // in the model
362+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
363+ QTRY_COMPARE(hj->m_visibleItems.count(), 14);
364+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
365+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
366+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
367+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
368+ verifyItem(hj->m_visibleItems[ 4], 4, 325, 0, true);
369+ verifyItem(hj->m_visibleItems[ 5], 5, 375, 0, true);
370+ verifyItem(hj->m_visibleItems[ 6], 6, 0, 160, true);
371+ verifyItem(hj->m_visibleItems[ 7], 7, 210, 160, true);
372+ verifyItem(hj->m_visibleItems[ 8], 8, 0, 320, true);
373+ verifyItem(hj->m_visibleItems[ 9], 9, 170, 320, true);
374+ verifyItem(hj->m_visibleItems[10], 10, 200, 320, true);
375+ verifyItem(hj->m_visibleItems[11], 11, 230, 320, true);
376+ verifyItem(hj->m_visibleItems[12], 12, 305, 320, true);
377+ verifyItem(hj->m_visibleItems[13], 13, 0, 480, false);
378+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 4);
379+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 375.);
380+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 7], 210.);
381+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[12], 305.);
382+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[13], 0.);
383+ QTRY_COMPARE(hj->implicitHeight(), 855.);
384+ }
385+
386+ void testModelAppendRemoveLast()
387+ {
388+ WidthModel *model2 = new WidthModel();
389+ QStringList heightList;
390+ heightList << "100" << "50" << "125" << "25" << "50" << "50";
391+ model2->setStringList(heightList);
392+ hj->setModel(model2);
393+ delete model;
394+ model = model2;
395+
396+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
397+ QTRY_COMPARE(hj->m_visibleItems.count(), 6);
398+ verifyItem(hj->m_visibleItems[0], 0, 0, 0, true);
399+ verifyItem(hj->m_visibleItems[1], 1, 110, 0, true);
400+ verifyItem(hj->m_visibleItems[2], 2, 170, 0, true);
401+ verifyItem(hj->m_visibleItems[3], 3, 305, 0, true);
402+ verifyItem(hj->m_visibleItems[4], 4, 340, 0, true);
403+ verifyItem(hj->m_visibleItems[5], 5, 400, 0, true);
404+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 0);
405+ QCOMPARE(hj->implicitHeight(), 150.);
406+
407+ model2->addString("75");
408+
409+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
410+ QTRY_COMPARE(hj->m_visibleItems.count(), 7);
411+ verifyItem(hj->m_visibleItems[0], 0, 0, 0, true);
412+ verifyItem(hj->m_visibleItems[1], 1, 110, 0, true);
413+ verifyItem(hj->m_visibleItems[2], 2, 170, 0, true);
414+ verifyItem(hj->m_visibleItems[3], 3, 305, 0, true);
415+ verifyItem(hj->m_visibleItems[4], 4, 340, 0, true);
416+ verifyItem(hj->m_visibleItems[5], 5, 400, 0, true);
417+ verifyItem(hj->m_visibleItems[6], 6, 0, 160, true);
418+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 1);
419+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[5], 400.);
420+ QCOMPARE(hj->implicitHeight(), 310.);
421+
422+ model2->addString("50");
423+ model2->addString("50");
424+
425+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
426+ QTRY_COMPARE(hj->m_visibleItems.count(), 9);
427+ verifyItem(hj->m_visibleItems[0], 0, 0, 0, true);
428+ verifyItem(hj->m_visibleItems[1], 1, 110, 0, true);
429+ verifyItem(hj->m_visibleItems[2], 2, 170, 0, true);
430+ verifyItem(hj->m_visibleItems[3], 3, 305, 0, true);
431+ verifyItem(hj->m_visibleItems[4], 4, 340, 0, true);
432+ verifyItem(hj->m_visibleItems[5], 5, 400, 0, true);
433+ verifyItem(hj->m_visibleItems[6], 6, 0, 160, true);
434+ verifyItem(hj->m_visibleItems[7], 7, 85, 160, true);
435+ verifyItem(hj->m_visibleItems[8], 8, 145, 160, true);
436+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 1);
437+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[5], 400.);
438+ QCOMPARE(hj->implicitHeight(), 310.);
439+
440+ model2->removeLast();
441+
442+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
443+ QTRY_COMPARE(hj->m_visibleItems.count(), 8);
444+ verifyItem(hj->m_visibleItems[0], 0, 0, 0, true);
445+ verifyItem(hj->m_visibleItems[1], 1, 110, 0, true);
446+ verifyItem(hj->m_visibleItems[2], 2, 170, 0, true);
447+ verifyItem(hj->m_visibleItems[3], 3, 305, 0, true);
448+ verifyItem(hj->m_visibleItems[4], 4, 340, 0, true);
449+ verifyItem(hj->m_visibleItems[5], 5, 400, 0, true);
450+ verifyItem(hj->m_visibleItems[6], 6, 0, 160, true);
451+ verifyItem(hj->m_visibleItems[7], 7, 85, 160, true);
452+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 1);
453+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[5], 400.);
454+ QCOMPARE(hj->implicitHeight(), 310.);
455+ }
456+
457+private:
458+ QQuickView *view;
459+ HorizontalJournal *hj;
460+ WidthModel *model;
461+};
462+
463+QTEST_MAIN(HorizontalJournalTest)
464+
465+#include "horizontaljournaltest.moc"
tests/plugins/DashViews/horizontaljournaltest.qmlnew file+43
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright (C) 2013 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+ HorizontalJournal {
23+ objectName: "hj"
24+ anchors.fill: parent
25+ rowHeight: 150
26+ columnSpacing: 10
27+ rowSpacing: 10
28+
29+ delegate: Rectangle {
30+ property real randomValue: Math.random()
31+ height: 150
32+ color: randomValue < 0.3 ? "green" : randomValue < 0.6 ? "blue" : "red";
33+ width: modelWidth
34+ border.width: 3
35+
36+ Text {
37+ text: index + "\nx: " + parent.x + "\nwidth: " + parent.width
38+ x: 10
39+ y: 10
40+ }
41+ }
42+ }
43+}
tests/plugins/DashViews/horizontaljournaltry.cppnew file+90
@@ -0,0 +1,90 @@
1+/*
2+ * Copyright (C) 2013 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 "horizontaljournal.h"
28+
29+class QWidthModela : public QStringListModel {
30+ Q_OBJECT
31+public:
32+ QHash<int, QByteArray> roleNames() const
33+ {
34+ QHash<int, QByteArray> roles;
35+ roles.insert(Qt::DisplayRole, "modelWidth");
36+ return roles;
37+ }
38+
39+public slots:
40+ void add(int height)
41+ {
42+ if (height <= 0) {
43+ height = 50 + 50. * qrand() / RAND_MAX ;
44+ }
45+ QStringList sl = stringList();
46+ sl << QString::number(height);
47+ setStringList(sl);
48+ }
49+
50+ void remove()
51+ {
52+ QStringList sl = stringList();
53+ if (!sl.isEmpty()) {
54+ sl.removeLast();
55+ setStringList(sl);
56+ }
57+ }
58+};
59+
60+int main(int argc, char *argv[])
61+{
62+ QGuiApplication a(argc, argv);
63+
64+ QQuickView *view = new QQuickView();
65+ view->setResizeMode(QQuickView::SizeRootObjectToView);
66+ view->engine()->addImportPath(BUILT_PLUGINS_DIR);
67+
68+ QWidthModela model;
69+ QStringList heightList;
70+ heightList << "100" << "50" << "125";
71+ model.setStringList(heightList);
72+
73+ QStringListModel listModel;
74+ listModel.setStringList(QStringList() << QString());
75+
76+ view->rootContext()->setContextProperty("listModel", &listModel);
77+ view->rootContext()->setContextProperty("vjModel", &model);
78+
79+ view->setSource(QUrl::fromLocalFile(DASHVIEWSTEST_FOLDER "/horizontaljournaltry.qml"));
80+
81+ view->show();
82+ view->resize(530, 400);
83+
84+ QObject::connect(view->rootObject(), SIGNAL(add(int)), &model, SLOT(add(int)));
85+ QObject::connect(view->rootObject(), SIGNAL(remove()), &model, SLOT(remove()));
86+
87+ return a.exec();
88+}
89+
90+#include "horizontaljournaltry.moc"
tests/plugins/DashViews/horizontaljournaltry.qmlnew file+99
@@ -0,0 +1,99 @@
1+/*
2+ * Copyright (C) 2013 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(int width)
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: HorizontalJournal {
32+ model: vjModel
33+ rowHeight: 100
34+ columnSpacing: 10
35+ rowSpacing: 10
36+ width: parent.width
37+ height: implicitHeight > 100 ? implicitHeight : 100
38+ delegateCreationBegin: lvwph.contentY
39+ delegateCreationEnd: lvwph.contentY + lvwph.height
40+
41+ delegate: Rectangle {
42+ height: 100
43+ color: "red";
44+ width: modelWidth
45+ border.width: 3
46+
47+ Text {
48+ text: index + "\nx: " + parent.x + "\nwidth: " + parent.width
49+ x: 10
50+ y: 10
51+ }
52+ }
53+ }
54+ }
55+
56+ Rectangle {
57+ id: controls
58+ height: parent.height
59+ width: 100
60+ anchors.right: parent.right
61+ color: "gray"
62+ opacity: 0.4
63+
64+ Rectangle {
65+ id: addButton
66+ height: 50
67+ width: parent.width
68+ color: "blue"
69+ Text {
70+ text: "Add"
71+ anchors.centerIn: parent
72+ }
73+ MouseArea {
74+ anchors.fill: parent
75+ onClicked: root.add(addField.text)
76+ }
77+ }
78+ TextInput {
79+ id: addField
80+ anchors.top: addButton.bottom
81+ height: 30
82+ width: parent.width
83+ }
84+ Rectangle {
85+ anchors.top: addField.bottom
86+ height: 50
87+ width: parent.width
88+ color: "red"
89+ Text {
90+ text: "Remove"
91+ anchors.centerIn: parent
92+ }
93+ MouseArea {
94+ anchors.fill: parent
95+ onClicked: root.remove()
96+ }
97+ }
98+ }
99+}
tests/plugins/DashViews/verticaljournaltest.cpp+5−5
@@ -30,7 +30,7 @@
3030
3131#include "verticaljournal.h"
3232
33−class QHeightModel : public QAbstractListModel {
33+class HeightModel : public QAbstractListModel {
3434public:
3535 QHash<int, QByteArray> roleNames() const
3636 {
@@ -127,7 +127,7 @@ private Q_SLOTS:
127127 view->setResizeMode(QQuickView::SizeRootObjectToView);
128128 view->engine()->addImportPath(BUILT_PLUGINS_DIR);
129129
130− model = new QHeightModel();
130+ model = new HeightModel();
131131 QStringList heightList;
132132 heightList << "100" << "50" << "125" << "10" << "40" << "70" << "200" << "110" << "160" << "20" << "20" << "65" << "80" << "200" << "300" << "130" << "400" << "300" << "500" << "10";
133133 model->setStringList(heightList);
@@ -323,7 +323,7 @@ private Q_SLOTS:
323323
324324 void testChangeModel()
325325 {
326− QHeightModel *model2 = new QHeightModel();
326+ HeightModel *model2 = new HeightModel();
327327 QStringList list2;
328328 list2 << "100" << "50" << "25" << "25" << "50" << "50";
329329 model2->setStringList(list2);
@@ -393,7 +393,7 @@ private Q_SLOTS:
393393
394394 void testModelAppendRemoveLast()
395395 {
396− QHeightModel *model2 = new QHeightModel();
396+ HeightModel *model2 = new HeightModel();
397397 QStringList list2;
398398 list2 << "100" << "50" << "25" << "25" << "50" << "50";
399399 model2->setStringList(list2);
@@ -462,7 +462,7 @@ private Q_SLOTS:
462462private:
463463 QQuickView *view;
464464 VerticalJournal *vj;
465− QHeightModel *model;
465+ HeightModel *model;
466466};
467467
468468QTEST_MAIN(VerticalJournalTest)
unity8.qmlprojectdeleted−25
@@ -1,25 +0,0 @@
1−/* File generated by Qt Creator, version 2.4.1 */
2−
3−import QmlProject 1.1
4−
5−Project {
6− mainFile: "Shell.qml"
7−
8− /* Include .qml, .js, and image files from current directory and subdirectories */
9− QmlFiles {
10− directory: "."
11− }
12− JavaScriptFiles {
13− directory: "."
14− }
15− ImageFiles {
16− directory: "."
17− }
18− /* List of plugin directories passed to QML runtime */
19− importPaths: [
20− "builddir/plugins",
21− "builddir/modules",
22− "builddir/tests/plugins",
23− "builddir/tests/utils/modules"
24− ]
25−}