on xenial help sign in
HorizontalJournal "finished"
0a4b90bAlbert Astals
plugins/DashViews/horizontaljournal.cpp+107−51
@@ -47,8 +47,8 @@ HorizontalJournal::HorizontalJournal()
47 , m_asyncRequestedIndex(-1)
47 , m_asyncRequestedIndex(-1)
48 , m_firstVisibleIndex(-1)
48 , m_firstVisibleIndex(-1)
49 , m_rowHeight(0)
49 , m_rowHeight(0)
50− , m_horizontalSpacing(0)
50+ , m_columnSpacing(0)
51− , m_verticalSpacing(0)
51+ , m_rowSpacing(0)
52 , m_delegateCreationBegin(0)
52 , m_delegateCreationBegin(0)
53 , m_delegateCreationEnd(0)
53 , m_delegateCreationEnd(0)
54 , m_delegateCreationBeginValid(false)
54 , m_delegateCreationBeginValid(false)
@@ -71,18 +71,20 @@ void HorizontalJournal::setModel(QAbstractItemModel *model)
71 if (model != this->model()) {
71 if (model != this->model()) {
72 if (!m_delegateModel) {
72 if (!m_delegateModel) {
73 createDelegateModel();
73 createDelegateModel();
74− }
74+ } else {
75#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
75#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
76+ disconnect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
77+ }
76 m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
78 m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
79+ connect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
77#else
80#else
81+ disconnect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
82+ }
78 m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
83 m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
84+ connect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
79#endif
85#endif
80− // Cleanup the existing items
86+
81− Q_FOREACH(QQuickItem *item, m_visibleItems)
87+ cleanupExistingItems();
82− releaseItem(item);
83− m_visibleItems.clear();
84− m_lastInRowIndexPosition.clear();
85− m_firstVisibleIndex = 0;
86
88
87 Q_EMIT modelChanged();
89 Q_EMIT modelChanged();
88 polish();
90 polish();
@@ -101,12 +103,7 @@ void HorizontalJournal::setDelegate(QQmlComponent *delegate)
101 createDelegateModel();
103 createDelegateModel();
102 }
104 }
103
105
104− // Cleanup the existing items
106+ cleanupExistingItems();
105− Q_FOREACH(QQuickItem *item, m_visibleItems)
106− releaseItem(item);
107− m_visibleItems.clear();
108− m_lastInRowIndexPosition.clear();
109− m_firstVisibleIndex = 0;
110
107
111 m_delegateModel->setDelegate(delegate);
108 m_delegateModel->setDelegate(delegate);
112
109
@@ -136,16 +133,16 @@ void HorizontalJournal::setRowHeight(qreal rowHeight)
136 }
133 }
137}
134}
138
135
139−qreal HorizontalJournal::horizontalSpacing() const
136+qreal HorizontalJournal::columnSpacing() const
140{
137{
141− return m_horizontalSpacing;
138+ return m_columnSpacing;
142}
139}
143
140
144−void HorizontalJournal::setHorizontalSpacing(qreal horizontalSpacing)
141+void HorizontalJournal::setColumnSpacing(qreal columnSpacing)
145{
142{
146− if (horizontalSpacing != m_horizontalSpacing) {
143+ if (columnSpacing != m_columnSpacing) {
147− m_horizontalSpacing = horizontalSpacing;
144+ m_columnSpacing = columnSpacing;
148− Q_EMIT horizontalSpacingChanged();
145+ Q_EMIT columnSpacingChanged();
149
146
150 if (isComponentComplete()) {
147 if (isComponentComplete()) {
151 relayout();
148 relayout();
@@ -153,16 +150,16 @@ void HorizontalJournal::setHorizontalSpacing(qreal horizontalSpacing)
153 }
150 }
154}
151}
155
152
156−qreal HorizontalJournal::verticalSpacing() const
153+qreal HorizontalJournal::rowSpacing() const
157{
154{
158− return m_verticalSpacing;
155+ return m_rowSpacing;
159}
156}
160
157
161−void HorizontalJournal::setVerticalSpacing(qreal verticalSpacing)
158+void HorizontalJournal::setRowSpacing(qreal rowSpacing)
162{
159{
163− if (verticalSpacing != m_verticalSpacing) {
160+ if (rowSpacing != m_rowSpacing) {
164− m_verticalSpacing = verticalSpacing;
161+ m_rowSpacing = rowSpacing;
165− Q_EMIT verticalSpacingChanged();
162+ Q_EMIT rowSpacingChanged();
166
163
167 if (isComponentComplete()) {
164 if (isComponentComplete()) {
168 relayout();
165 relayout();
@@ -267,11 +264,11 @@ void HorizontalJournal::findBottomModelIndexToAdd(int *modelIndex, double *yPos)
267{
264{
268 if (m_visibleItems.isEmpty()) {
265 if (m_visibleItems.isEmpty()) {
269 *modelIndex = 0;
266 *modelIndex = 0;
270− *yPos = m_verticalSpacing;
267+ *yPos = 0;
271 } else {
268 } else {
272 *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
269 *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
273 if (m_lastInRowIndexPosition.contains(*modelIndex - 1)) {
270 if (m_lastInRowIndexPosition.contains(*modelIndex - 1)) {
274− *yPos = m_visibleItems.last()->y() + m_rowHeight + m_verticalSpacing;
271+ *yPos = m_visibleItems.last()->y() + m_rowHeight + m_rowSpacing;
275 } else {
272 } else {
276 *yPos = m_visibleItems.last()->y();
273 *yPos = m_visibleItems.last()->y();
277 }
274 }
@@ -286,7 +283,7 @@ void HorizontalJournal::findTopModelIndexToAdd(int *modelIndex, double *yPos)
286 } else {
283 } else {
287 *modelIndex = m_firstVisibleIndex - 1;
284 *modelIndex = m_firstVisibleIndex - 1;
288 if (m_lastInRowIndexPosition.contains(*modelIndex)) {
285 if (m_lastInRowIndexPosition.contains(*modelIndex)) {
289− *yPos = m_visibleItems.first()->y() - m_verticalSpacing - m_rowHeight;
286+ *yPos = m_visibleItems.first()->y() - m_rowSpacing - m_rowHeight;
290 } else {
287 } else {
291 *yPos = m_visibleItems.first()->y();
288 *yPos = m_visibleItems.first()->y();
292 }
289 }
@@ -302,7 +299,7 @@ bool HorizontalJournal::addVisibleItems(qreal fillFrom, qreal fillTo, bool async
302 return false;
299 return false;
303
300
304 int modelIndex;
301 int modelIndex;
305− double yPos;
302+ qreal yPos;
306 findBottomModelIndexToAdd(&modelIndex, &yPos);
303 findBottomModelIndexToAdd(&modelIndex, &yPos);
307 bool changed = false;
304 bool changed = false;
308 while (modelIndex < m_delegateModel->count() && yPos <= fillTo) {
305 while (modelIndex < m_delegateModel->count() && yPos <= fillTo) {
@@ -338,6 +335,7 @@ bool HorizontalJournal::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
338 while (!m_visibleItems.isEmpty() && m_visibleItems.last()->y() > bufferTo) {
335 while (!m_visibleItems.isEmpty() && m_visibleItems.last()->y() > bufferTo) {
339 releaseItem(m_visibleItems.takeLast());
336 releaseItem(m_visibleItems.takeLast());
340 changed = true;
337 changed = true;
338+ m_lastInRowIndexPosition.remove(m_firstVisibleIndex + m_visibleItems.count());
341 }
339 }
342
340
343 if (m_visibleItems.isEmpty()) {
341 if (m_visibleItems.isEmpty()) {
@@ -389,22 +387,22 @@ void HorizontalJournal::positionItem(int modelIndex, QQuickItem *item)
389{
387{
390 if (m_visibleItems.isEmpty()) {
388 if (m_visibleItems.isEmpty()) {
391 Q_ASSERT(modelIndex == 0);
389 Q_ASSERT(modelIndex == 0);
392− item->setY(m_verticalSpacing);
390+ item->setY(0);
393− item->setX(m_horizontalSpacing);
391+ item->setX(0);
394 m_visibleItems << item;
392 m_visibleItems << item;
395 m_firstVisibleIndex = 0;
393 m_firstVisibleIndex = 0;
396 } else {
394 } else {
397 // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count()
395 // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count()
398 if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
396 if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
399 QQuickItem *lastItem = m_visibleItems.last();
397 QQuickItem *lastItem = m_visibleItems.last();
400− if (lastItem->x() + lastItem->width() + m_horizontalSpacing + item->width() + m_horizontalSpacing <= width()) {
398+ if (lastItem->x() + lastItem->width() + m_columnSpacing + item->width() <= width()) {
401 // Fits in the row
399 // Fits in the row
402 item->setY(lastItem->y());
400 item->setY(lastItem->y());
403− item->setX(lastItem->x() + lastItem->width() + m_horizontalSpacing);
401+ item->setX(lastItem->x() + lastItem->width() + m_columnSpacing);
404 } else {
402 } else {
405 // Starts a new row
403 // Starts a new row
406− item->setY(lastItem->y() + m_rowHeight + m_verticalSpacing);
404+ item->setY(lastItem->y() + m_rowHeight + m_rowSpacing);
407− item->setX(m_horizontalSpacing);
405+ item->setX(0);
408 m_lastInRowIndexPosition[modelIndex - 1] = lastItem->x();
406 m_lastInRowIndexPosition[modelIndex - 1] = lastItem->x();
409 }
407 }
410 m_visibleItems << item;
408 m_visibleItems << item;
@@ -412,11 +410,11 @@ void HorizontalJournal::positionItem(int modelIndex, QQuickItem *item)
412 QQuickItem *firstItem = m_visibleItems.first();
410 QQuickItem *firstItem = m_visibleItems.first();
413 if (m_lastInRowIndexPosition.contains(modelIndex)) {
411 if (m_lastInRowIndexPosition.contains(modelIndex)) {
414 // It is the last item of its row, so start a new one since we're going back
412 // It is the last item of its row, so start a new one since we're going back
415− item->setY(firstItem->y() - m_verticalSpacing - m_rowHeight);
413+ item->setY(firstItem->y() - m_rowSpacing - m_rowHeight);
416 item->setX(m_lastInRowIndexPosition[modelIndex]);
414 item->setX(m_lastInRowIndexPosition[modelIndex]);
417 } else {
415 } else {
418 item->setY(firstItem->y());
416 item->setY(firstItem->y());
419− item->setX(firstItem->x() - m_horizontalSpacing - item->width());
417+ item->setX(firstItem->x() - m_columnSpacing - item->width());
420 }
418 }
421 m_firstVisibleIndex = modelIndex;
419 m_firstVisibleIndex = modelIndex;
422 m_visibleItems.prepend(item);
420 m_visibleItems.prepend(item);
@@ -426,6 +424,16 @@ void HorizontalJournal::positionItem(int modelIndex, QQuickItem *item)
426 }
424 }
427}
425}
428
426
427+void HorizontalJournal::cleanupExistingItems()
428+{
429+ // Cleanup the existing items
430+ Q_FOREACH(QQuickItem *item, m_visibleItems)
431+ releaseItem(item);
432+ m_visibleItems.clear();
433+ m_lastInRowIndexPosition.clear();
434+ m_firstVisibleIndex = 0;
435+}
436+
429void HorizontalJournal::releaseItem(QQuickItem *item)
437void HorizontalJournal::releaseItem(QQuickItem *item)
430{
438{
431#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
439#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
@@ -439,6 +447,20 @@ void HorizontalJournal::releaseItem(QQuickItem *item)
439 }
447 }
440}
448}
441
449
450+void HorizontalJournal::calculateImplicitHeight()
451+{
452+ m_implicitHeightDirty = false;
453+
454+ if (m_firstVisibleIndex >= 0) {
455+ const int nIndexes = m_firstVisibleIndex + m_visibleItems.count();
456+ const double bottomMostY = m_visibleItems.last()->y() + m_rowHeight;
457+ const double averageHeight = bottomMostY / nIndexes;
458+ setImplicitHeight(bottomMostY + averageHeight * (model()->rowCount() - nIndexes));
459+ } else {
460+ setImplicitHeight(0);
461+ }
462+}
463+
442#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
464#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
443void HorizontalJournal::itemCreated(int modelIndex, QQuickItem *item)
465void HorizontalJournal::itemCreated(int modelIndex, QQuickItem *item)
444{
466{
@@ -447,7 +469,7 @@ void HorizontalJournal::itemCreated(int modelIndex, QObject *object)
447{
469{
448 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
470 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
449 if (!item) {
471 if (!item) {
450− qWarning() << "ListViewWithPageHeader::itemCreated got a non item for index" << modelIndex;
472+ qWarning() << "HorizontalJournal::itemCreated got a non item for index" << modelIndex;
451 return;
473 return;
452 }
474 }
453#endif
475#endif
@@ -459,6 +481,44 @@ void HorizontalJournal::itemCreated(int modelIndex, QObject *object)
459 }
481 }
460}
482}
461
483
484+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
485+void HorizontalJournal::onModelUpdated(const QQuickChangeSet &changeSet, bool reset)
486+#else
487+void HorizontalJournal::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
488+#endif
489+{
490+ if (reset) {
491+ cleanupExistingItems();
492+ } else {
493+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
494+ Q_FOREACH(const QQuickChangeSet::Remove &remove, changeSet.removes()) {
495+#else
496+ Q_FOREACH(const QQmlChangeSet::Remove &remove, changeSet.removes()) {
497+#endif
498+ for (int i = remove.count - 1; i >= 0; --i) {
499+ const int indexToRemove = remove.index + i;
500+ // We only support removing from the end so
501+ // any of the last items of a column has to be indexToRemove
502+ const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
503+ if (indexToRemove == lastIndex) {
504+ releaseItem(m_visibleItems.takeLast());
505+ m_lastInRowIndexPosition.remove(indexToRemove);
506+ } else {
507+ if (indexToRemove < lastIndex) {
508+ qFatal("HorizontalJournal only supports removal from the end of the model");
509+ } else {
510+ m_implicitHeightDirty = true;
511+ }
512+ }
513+ }
514+ }
515+ if (m_visibleItems.isEmpty()) {
516+ m_firstVisibleIndex = -1;
517+ }
518+ }
519+ polish();
520+}
521+
462void HorizontalJournal::relayout()
522void HorizontalJournal::relayout()
463{
523{
464 m_needsRelayout = true;
524 m_needsRelayout = true;
@@ -481,7 +541,10 @@ void HorizontalJournal::updatePolish()
481
541
482 if (m_firstVisibleIndex == 0) {
542 if (m_firstVisibleIndex == 0) {
483 int i = 0;
543 int i = 0;
544+ const QList<QQuickItem*> allItems = m_visibleItems;
545+ m_visibleItems.clear();
546+ m_lastInRowIndexPosition.clear();
484− Q_FOREACH(QQuickItem *item, m_visibleItems) {
547+ Q_FOREACH(QQuickItem *item, allItems) {
485 positionItem(i, item);
548 positionItem(i, item);
486 ++i;
549 ++i;
487 }
550 }
@@ -507,16 +570,7 @@ void HorizontalJournal::updatePolish()
507 }
570 }
508
571
509 if (m_implicitHeightDirty) {
572 if (m_implicitHeightDirty) {
510− m_implicitHeightDirty = false;
573+ calculateImplicitHeight();
511−
512− if (m_firstVisibleIndex >= 0) {
513− const int nIndexes = m_firstVisibleIndex + m_visibleItems.count();
514− const double bottomMostY = m_visibleItems.last()->y() + m_rowHeight + m_verticalSpacing;
515− const double averageHeight = bottomMostY / nIndexes;
516− setImplicitHeight(bottomMostY + averageHeight * (model()->rowCount() - nIndexes));
517− } else {
518− setImplicitHeight(0);
519− }
520 }
574 }
521}
575}
522
576
@@ -527,5 +581,7 @@ void HorizontalJournal::componentComplete()
527
581
528 QQuickItem::componentComplete();
582 QQuickItem::componentComplete();
529
583
584+ m_needsRelayout = true;
585+
530 polish();
586 polish();
531}
587}
plugins/DashViews/horizontaljournal.h+24−14
@@ -22,8 +22,10 @@
22class QAbstractItemModel;
22class QAbstractItemModel;
23class QQmlComponent;
23class QQmlComponent;
24#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
24#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
25+class QQuickChangeSet;
25class QQuickVisualDataModel;
26class QQuickVisualDataModel;
26#else
27#else
28+class QQmlChangeSet;
27class QQmlDelegateModel;
29class QQmlDelegateModel;
28#endif
30#endif
29
31
@@ -41,10 +43,16 @@ class QQmlDelegateModel;
41 Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
43 Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
42 Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
44 Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
43 Q_PROPERTY(qreal rowHeight READ rowHeight WRITE setRowHeight NOTIFY rowHeightChanged)
45 Q_PROPERTY(qreal rowHeight READ rowHeight WRITE setRowHeight NOTIFY rowHeightChanged)
44− Q_PROPERTY(qreal horizontalSpacing READ horizontalSpacing WRITE setHorizontalSpacing NOTIFY horizontalSpacingChanged)
46+ Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
45− Q_PROPERTY(qreal verticalSpacing READ verticalSpacing WRITE setVerticalSpacing NOTIFY verticalSpacingChanged)
47+ Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
46− Q_PROPERTY(qreal delegateCreationBegin READ delegateCreationBegin WRITE setDelegateCreationBegin NOTIFY delegateCreationBeginChanged RESET resetDelegateCreationBegin)
48+ Q_PROPERTY(qreal delegateCreationBegin READ delegateCreationBegin
49+ WRITE setDelegateCreationBegin
50+ NOTIFY delegateCreationBeginChanged
51+ RESET resetDelegateCreationBegin)
47− Q_PROPERTY(qreal delegateCreationEnd READ delegateCreationEnd WRITE setDelegateCreationEnd NOTIFY delegateCreationEndChanged RESET resetDelegateCreationEnd)
52+ Q_PROPERTY(qreal delegateCreationEnd READ delegateCreationEnd
53+ WRITE setDelegateCreationEnd
54+ NOTIFY delegateCreationEndChanged
55+ RESET resetDelegateCreationEnd)
48
56
49friend class HorizontalJournalTest;
57friend class HorizontalJournalTest;
50
58
@@ -60,11 +68,11 @@ public:
60 qreal rowHeight() const;
68 qreal rowHeight() const;
61 void setRowHeight(qreal rowHeight);
69 void setRowHeight(qreal rowHeight);
62
70
63− qreal horizontalSpacing() const;
71+ qreal columnSpacing() const;
64− void setHorizontalSpacing(qreal horizontalSpacing);
72+ void setColumnSpacing(qreal columnSpacing);
65
73
66− qreal verticalSpacing() const;
74+ qreal rowSpacing() const;
67− void setVerticalSpacing(qreal verticalSpacing);
75+ void setRowSpacing(qreal rowSpacing);
68
76
69 qreal delegateCreationBegin() const;
77 qreal delegateCreationBegin() const;
70 void setDelegateCreationBegin(qreal);
78 void setDelegateCreationBegin(qreal);
@@ -78,8 +86,8 @@ Q_SIGNALS:
78 void modelChanged();
86 void modelChanged();
79 void delegateChanged();
87 void delegateChanged();
80 void rowHeightChanged();
88 void rowHeightChanged();
81− void horizontalSpacingChanged();
89+ void columnSpacingChanged();
82− void verticalSpacingChanged();
90+ void rowSpacingChanged();
83 void delegateCreationBeginChanged();
91 void delegateCreationBeginChanged();
84 void delegateCreationEndChanged();
92 void delegateCreationEndChanged();
85
93
@@ -90,8 +98,10 @@ protected:
90private Q_SLOTS:
98private Q_SLOTS:
91#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
99#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
92 void itemCreated(int modelIndex, QQuickItem *item);
100 void itemCreated(int modelIndex, QQuickItem *item);
101+ void onModelUpdated(const QQuickChangeSet &changeSet, bool reset);
93#else
102#else
94 void itemCreated(int modelIndex, QObject *object);
103 void itemCreated(int modelIndex, QObject *object);
104+ void onModelUpdated(const QQmlChangeSet &changeSet, bool reset);
95#endif
105#endif
96 void relayout();
106 void relayout();
97 void onHeightChanged();
107 void onHeightChanged();
@@ -105,9 +115,9 @@ private:
105 bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
115 bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
106 QQuickItem *createItem(int modelIndex, bool asynchronous);
116 QQuickItem *createItem(int modelIndex, bool asynchronous);
107 void positionItem(int modelIndex, QQuickItem *item);
117 void positionItem(int modelIndex, QQuickItem *item);
108− void doRelayout();
118+ void cleanupExistingItems();
109−
110 void releaseItem(QQuickItem *item);
119 void releaseItem(QQuickItem *item);
120+ void calculateImplicitHeight();
111
121
112#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
122#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
113 QQuickVisualDataModel *m_delegateModel;
123 QQuickVisualDataModel *m_delegateModel;
@@ -122,8 +132,8 @@ private:
122 QList<QQuickItem*> m_visibleItems;
132 QList<QQuickItem*> m_visibleItems;
123 QMap<int, double> m_lastInRowIndexPosition;
133 QMap<int, double> m_lastInRowIndexPosition;
124 int m_rowHeight;
134 int m_rowHeight;
125− int m_horizontalSpacing;
135+ int m_columnSpacing;
126− int m_verticalSpacing;
136+ int m_rowSpacing;
127 qreal m_delegateCreationBegin;
137 qreal m_delegateCreationBegin;
128 qreal m_delegateCreationEnd;
138 qreal m_delegateCreationEnd;
129 bool m_delegateCreationBeginValid;
139 bool m_delegateCreationBeginValid;
plugins/DashViews/plugin.cpp+2
@@ -17,6 +17,7 @@
17
17
18#include "plugin.h"
18#include "plugin.h"
19
19
20+#include "horizontaljournal.h"
20#include "listviewwithpageheader.h"
21#include "listviewwithpageheader.h"
21#include "verticaljournal.h"
22#include "verticaljournal.h"
22
23
@@ -26,6 +27,7 @@ void DashViewsPlugin::registerTypes(const char *uri)
26{
27{
27 Q_ASSERT(uri == QLatin1String("DashViews"));
28 Q_ASSERT(uri == QLatin1String("DashViews"));
28 qmlRegisterType<QAbstractItemModel>();
29 qmlRegisterType<QAbstractItemModel>();
30+ qmlRegisterType<HorizontalJournal>(uri, 0, 1, "HorizontalJournal");
29 qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
31 qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
30 qmlRegisterType<VerticalJournal>(uri, 0, 1, "VerticalJournal");
32 qmlRegisterType<VerticalJournal>(uri, 0, 1, "VerticalJournal");
31}
33}
tests/plugins/DashViews/CMakeLists.txt+11
@@ -50,6 +50,16 @@ qt5_use_modules(verticaljournaltryExec Test Core Qml)
50target_link_libraries(verticaljournaltryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
50target_link_libraries(verticaljournaltryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
51add_custom_target(tryVerticalJournal ${CMAKE_CURRENT_BINARY_DIR}/verticaljournaltryExec -o ${CMAKE_BINARY_DIR}/testVerticalJournal.xml,xunitxml -o -,txt)
51add_custom_target(tryVerticalJournal ${CMAKE_CURRENT_BINARY_DIR}/verticaljournaltryExec -o ${CMAKE_BINARY_DIR}/testVerticalJournal.xml,xunitxml -o -,txt)
52
52
53+add_executable(horizontaljournaltestExec horizontaljournaltest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/horizontaljournal.cpp)
54+qt5_use_modules(horizontaljournaltestExec Test Core Qml)
55+target_link_libraries(horizontaljournaltestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
56+add_custom_target(testHorizontalJournal ${CMAKE_CURRENT_BINARY_DIR}/horizontaljournaltestExec -o ${CMAKE_BINARY_DIR}/testHorizontalJournal.xml,xunitxml -o -,txt)
57+
58+add_executable(horizontaljournaltryExec horizontaljournaltry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/horizontaljournal.cpp)
59+qt5_use_modules(horizontaljournaltryExec Test Core Qml)
60+target_link_libraries(horizontaljournaltryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
61+add_custom_target(tryHorizontalJournal ${CMAKE_CURRENT_BINARY_DIR}/horizontaljournaltryExec -o ${CMAKE_BINARY_DIR}/testHorizontalJournal.xml,xunitxml -o -,txt)
62+
53set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
63set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
54add_qml_test(. listviewwithpageheaderqml IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
64add_qml_test(. listviewwithpageheaderqml IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
55
65
@@ -58,3 +68,4 @@ add_dependencies(qmluitests testlistviewwithpageheadersection)
58add_dependencies(qmluitests testlistviewwithpageheadersectionexternalmodel)
68add_dependencies(qmluitests testlistviewwithpageheadersectionexternalmodel)
59add_dependencies(qmluitests testlistviewwithpageheaderqml)
69add_dependencies(qmluitests testlistviewwithpageheaderqml)
60add_dependencies(qmluitests testVerticalJournal)
70add_dependencies(qmluitests testVerticalJournal)
71+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 QWidthModel : 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 QWidthModel();
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+ QWidthModel *model2 = new QWidthModel();
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+ QWidthModel *model2 = new QWidthModel();
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+ QWidthModel *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+}