on xenial help sign in
Vertical journal

Comes from lp:~aacid/+junk/verticalJournal. Approved by PS Jenkins bot, Daniel d'Andrada.

commit9d637cdae6208f24307a94bbdbdcdd99461d1cef authorAlbert Astals <albert.astals@canonical.com> dateWed 18 Dec 2013, 13:46 (13 years ago) parentb997e66d247be5
25 files changed+1364−29
Dash/ScopeListView.qml+1−1 debian/unity8-private.install+1−1 plugins/CMakeLists.txt+1−1 plugins/ListViewWithPageHeader/CMakeLists.txt → plugins/DashViews/CMakeLists.txt+6−5 plugins/ListViewWithPageHeader/listviewwithpageheader.cpp → plugins/DashViews/listviewwithpageheader.cpp plugins/ListViewWithPageHeader/listviewwithpageheader.h → plugins/DashViews/listviewwithpageheader.h plugins/ListViewWithPageHeader/plugin.cpp → plugins/DashViews/plugin.cpp+4−2 plugins/ListViewWithPageHeader/plugin.h → plugins/DashViews/plugin.h+3−3 plugins/DashViews/qmldir+3 plugins/DashViews/verticaljournal.cpp+590 plugins/DashViews/verticaljournal.h+182 plugins/ListViewWithPageHeader/qmldir−3 tests/plugins/CMakeLists.txt+1−1 tests/plugins/ListViewWithPageHeader/CMakeLists.txt → tests/plugins/DashViews/CMakeLists.txt+16−5 tests/plugins/ListViewWithPageHeader/listviewwithpageheadertest.cpp → tests/plugins/DashViews/listviewwithpageheadertest.cpp+1−1 tests/plugins/ListViewWithPageHeader/test.qml → tests/plugins/DashViews/listviewwithpageheadertest.qml+1−1 tests/plugins/ListViewWithPageHeader/listviewwithpageheadertestsection.cpp → tests/plugins/DashViews/listviewwithpageheadertestsection.cpp+1−1 tests/plugins/ListViewWithPageHeader/test_section.qml → tests/plugins/DashViews/listviewwithpageheadertestsection.qml+1−1 tests/plugins/ListViewWithPageHeader/listviewwithpageheadertestsectionexternalmodel.cpp → tests/plugins/DashViews/listviewwithpageheadertestsectionexternalmodel.cpp+1−1 tests/plugins/ListViewWithPageHeader/test_sectionexternalmodel.qml → tests/plugins/DashViews/listviewwithpageheadertestsectionexternalmodel.qml+1−1 tests/plugins/ListViewWithPageHeader/tst_listviewwithpageheaderqml.qml → tests/plugins/DashViews/tst_listviewwithpageheaderqml.qml+1−1 tests/plugins/DashViews/verticaljournaltest.cpp+315 tests/plugins/DashViews/verticaljournaltest.qml+44 tests/plugins/DashViews/verticaljournaltry.cpp+90 tests/plugins/DashViews/verticaljournaltry.qml+100
Dash/ScopeListView.qml+1−1
@@ -15,7 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18−import ListViewWithPageHeader 0.1
18+import DashViews 0.1
1919
2020ListViewWithPageHeader {
2121 maximumFlickVelocity: height * 10
debian/unity8-private.install+1−1
@@ -1,7 +1,7 @@
11usr/lib/*/unity8/qml/AccountsService
22usr/lib/*/unity8/qml/HudClient
33usr/lib/*/unity8/qml/LightDM
4−usr/lib/*/unity8/qml/ListViewWithPageHeader
4+usr/lib/*/unity8/qml/DashViews
55usr/lib/*/unity8/qml/Powerd
66usr/lib/*/unity8/qml/SessionBroadcast
77usr/lib/*/unity8/qml/Ubuntu
plugins/CMakeLists.txt+1−1
@@ -1,7 +1,7 @@
11add_subdirectory(AccountsService)
22add_subdirectory(HudClient)
33add_subdirectory(LightDM)
4−add_subdirectory(ListViewWithPageHeader)
4+add_subdirectory(DashViews)
55add_subdirectory(Powerd)
66add_subdirectory(SessionBroadcast)
77add_subdirectory(Ubuntu)
plugins/ListViewWithPageHeader/CMakeLists.txt → plugins/DashViews/CMakeLists.txtrenamed+6−5
@@ -24,19 +24,20 @@ remove_definitions(-DQT_NO_KEYWORDS)
2424set(QMLPLUGIN_SRC
2525 plugin.cpp
2626 listviewwithpageheader.cpp
27+ verticaljournal.cpp
2728 )
2829
29−add_library(ListViewWithPageHeader-qml MODULE
30+add_library(DashViews-qml MODULE
3031 ${QMLPLUGIN_SRC}
3132 )
3233
33−target_link_libraries(ListViewWithPageHeader-qml
34+target_link_libraries(DashViews-qml
3435 ${Qt5Gui_LIBRARIES}
3536 ${Qt5Quick_LIBRARIES}
3637 )
3738
38−qt5_use_modules(ListViewWithPageHeader-qml Qml Quick)
39+qt5_use_modules(DashViews-qml Qml Quick)
3940
4041# export the qmldir qmltypes and plugin files
41−export_qmlfiles(ListViewWithPageHeader ListViewWithPageHeader)
42−export_qmlplugin(ListViewWithPageHeader 0.1 ListViewWithPageHeader TARGETS ListViewWithPageHeader-qml)
42+export_qmlfiles(DashViews DashViews)
43+export_qmlplugin(DashViews 0.1 DashViews TARGETS DashViews-qml)
plugins/ListViewWithPageHeader/listviewwithpageheader.cpp → plugins/DashViews/listviewwithpageheader.cpprenamed

The content is the same.

plugins/ListViewWithPageHeader/listviewwithpageheader.h → plugins/DashViews/listviewwithpageheader.hrenamed

The content is the same.

plugins/ListViewWithPageHeader/plugin.cpp → plugins/DashViews/plugin.cpprenamed+4−2
@@ -18,12 +18,14 @@
1818#include "plugin.h"
1919
2020#include "listviewwithpageheader.h"
21+#include "verticaljournal.h"
2122
2223#include <QAbstractItemModel>
2324
24−void ListViewWithPageHeaderPlugin::registerTypes(const char *uri)
25+void DashViewsPlugin::registerTypes(const char *uri)
2526{
26− Q_ASSERT(uri == QLatin1String("ListViewWithPageHeader"));
27+ Q_ASSERT(uri == QLatin1String("DashViews"));
2728 qmlRegisterType<QAbstractItemModel>();
2829 qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
30+ qmlRegisterType<VerticalJournal>(uri, 0, 1, "VerticalJournal");
2931}
plugins/ListViewWithPageHeader/plugin.h → plugins/DashViews/plugin.hrenamed+3−3
@@ -15,13 +15,13 @@
1515 *
1616 */
1717
18−#ifndef LISTVIEWWITHPAGEHEADER_PLUGIN_H
19−#define LISTVIEWWITHPAGEHEADER_PLUGIN_H
18+#ifndef DASHVIEWS_PLUGIN_H
19+#define DASHVIEWS_PLUGIN_H
2020
2121#include <QtQml/QQmlEngine>
2222#include <QtQml/QQmlExtensionPlugin>
2323
24−class ListViewWithPageHeaderPlugin : public QQmlExtensionPlugin
24+class DashViewsPlugin : public QQmlExtensionPlugin
2525{
2626 Q_OBJECT
2727 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
plugins/DashViews/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module DashViews
2+plugin DashViews-qml
3+typeinfo plugin.qmltypes
plugins/DashViews/verticaljournal.cppnew file+590
@@ -0,0 +1,590 @@
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_columnVisibleItems
19+ * that holds a vector of lists. There's a list for each of the
20+ * columns the view has. In the list the items of the column are
21+ * ordered as they appear topdown in the view. m_indexColumnMap is
22+ * used when re-building the list up since given a position
23+ * in the middle of the list and the need to create the previous does
24+ * not give us enough information to know in which column we have
25+ * to position the item so that when we reach the item the view is
26+ * correctly layouted at 0 for all the columns
27+ */
28+#include "verticaljournal.h"
29+
30+#include <qqmlengine.h>
31+#pragma GCC diagnostic push
32+#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
39+#include <private/qquickitem_p.h>
40+#pragma GCC diagnostic pop
41+
42+static const qreal bufferRatio = 0.5;
43+
44+VerticalJournal::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
63+{
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+ }
112+}
113+
114+qreal VerticalJournal::columnWidth() const
115+{
116+ return m_columnWidth;
117+}
118+
119+void VerticalJournal::setColumnWidth(qreal columnWidth)
120+{
121+ if (columnWidth != m_columnWidth) {
122+ m_columnWidth = columnWidth;
123+ Q_EMIT columnWidthChanged();
124+
125+ if (isComponentComplete()) {
126+ Q_FOREACH(const auto &column, m_columnVisibleItems) {
127+ Q_FOREACH(const ViewItem &item, column) {
128+ item.m_item->setWidth(columnWidth);
129+ }
130+ }
131+ relayout();
132+ }
133+ }
134+}
135+
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+
263+void VerticalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
264+{
265+ *modelIndex = 0;
266+ *yPos = std::numeric_limits<qreal>::max();
267+
268+ Q_FOREACH(const auto &column, m_columnVisibleItems) {
269+ if (!column.isEmpty()) {
270+ const ViewItem &item = column.last();
271+ *yPos = qMin(*yPos, item.y() + item.height() + m_rowSpacing);
272+ *modelIndex = qMax(*modelIndex, item.m_modelIndex + 1);
273+ } else {
274+ *yPos = 0;
275+ }
276+ }
277+}
278+
279+void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
280+{
281+ *modelIndex = INT_MAX;
282+ *yPos = std::numeric_limits<qreal>::lowest();
283+ int columnToAddTo = -1;
284+
285+ // Find the topmost free column
286+ for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
287+ const auto &column = m_columnVisibleItems[i];
288+ if (!column.isEmpty()) {
289+ const ViewItem &item = column.first();
290+ const auto itemTopPos = item.y() - m_rowSpacing;
291+ if (itemTopPos > *yPos) {
292+ *yPos = itemTopPos;
293+ *modelIndex = item.m_modelIndex - 1;
294+ columnToAddTo = i;
295+ }
296+ }
297+ }
298+
299+ if (*modelIndex > 0) {
300+ Q_ASSERT(m_indexColumnMap.contains(*modelIndex));
301+ while (m_indexColumnMap[*modelIndex] != columnToAddTo) {
302+ // We found out that we have to add to columnToAddTo
303+ // and thought that we had to add *modelIndex, but history tells
304+ // it is not correct, so find up from *modelIndex until we found the index
305+ // that has to end up in columnToAddTo
306+ *modelIndex = *modelIndex - 1;
307+ Q_ASSERT(m_indexColumnMap.contains(*modelIndex));
308+ }
309+ }
310+}
311+
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)
345+{
346+ bool changed = false;
347+
348+ for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
349+ QList<ViewItem> &column = m_columnVisibleItems[i];
350+ while (!column.isEmpty() && column.first().y() + column.first().height() < bufferFrom) {
351+ releaseItem(column.takeFirst());
352+ changed = true;
353+ }
354+
355+ while (!column.isEmpty() && column.last().y() > bufferTo) {
356+ releaseItem(column.takeLast());
357+ changed = true;
358+ }
359+ }
360+
361+ return changed;
362+}
363+
364+QQuickItem *VerticalJournal::createItem(int modelIndex, bool asynchronous)
365+{
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;
399+ }
400+}
401+
402+void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
403+{
404+ // Check if we add it to the bottom of existing column items
405+ const QList<ViewItem> &firstColumn = m_columnVisibleItems[0];
406+ qreal columnToAddY = !firstColumn.isEmpty() ? firstColumn.last().y() + firstColumn.last().height() : -m_rowSpacing;
407+ int columnToAddTo = 0;
408+ for (int i = 1; i < m_columnVisibleItems.count(); ++i) {
409+ const QList<ViewItem> &column = m_columnVisibleItems[i];
410+ const qreal iY = !column.isEmpty() ? column.last().y() + column.last().height() : -m_rowSpacing;
411+ if (iY < columnToAddY) {
412+ columnToAddTo = i;
413+ columnToAddY = iY;
414+ }
415+ }
416+
417+ const QList<ViewItem> &columnToAdd = m_columnVisibleItems[columnToAddTo];
418+ if (columnToAdd.isEmpty() || columnToAdd.last().m_modelIndex < modelIndex) {
419+ item->setX(columnToAddTo * (m_columnWidth + m_columnSpacing));
420+ item->setY(columnToAddY + m_rowSpacing);
421+
422+ m_columnVisibleItems[columnToAddTo] << ViewItem(item, modelIndex);
423+ m_indexColumnMap[modelIndex] = columnToAddTo;
424+ } else {
425+ Q_ASSERT(m_indexColumnMap.contains(modelIndex));
426+ columnToAddTo = m_indexColumnMap[modelIndex];
427+ columnToAddY = m_columnVisibleItems[columnToAddTo].first().y();
428+
429+ item->setX(columnToAddTo * (m_columnWidth + m_columnSpacing));
430+ item->setY(columnToAddY - m_rowSpacing - item->height());
431+
432+ m_columnVisibleItems[columnToAddTo].prepend(ViewItem(item, modelIndex));
433+ }
434+}
435+
436+void VerticalJournal::cleanupExistingItems()
437+{
438+ // Cleanup the existing items
439+ for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
440+ QList<ViewItem> &column = m_columnVisibleItems[i];
441+ Q_FOREACH(const ViewItem &item, column)
442+ releaseItem(item);
443+ column.clear();
444+ }
445+ m_indexColumnMap.clear();
446+}
447+
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+
461+void VerticalJournal::calculateImplicitHeight()
462+{
463+ m_implicitHeightDirty = false;
464+
465+ int lastModelIndex = -1;
466+ qreal bottomMostY = 0;
467+ Q_FOREACH(const auto &column, m_columnVisibleItems) {
468+ if (!column.isEmpty()) {
469+ const ViewItem &item = column.last();
470+ lastModelIndex = qMax(lastModelIndex, item.m_modelIndex);
471+ bottomMostY = qMax(bottomMostY, item.y() + item.height());
472+ }
473+ }
474+ if (lastModelIndex >= 0) {
475+ const double averageHeight = bottomMostY / (lastModelIndex + 1);
476+ setImplicitHeight(bottomMostY + averageHeight * (model()->rowCount() - lastModelIndex - 1));
477+ } else {
478+ setImplicitHeight(0);
479+ }
480+}
481+
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)
487+{
488+ QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
489+ if (!item) {
490+ qWarning() << "VerticalJournal::itemCreated got a non item for index" << modelIndex;
491+ return;
492+ }
493+#endif
494+ item->setParentItem(this);
495+ if (modelIndex == m_asyncRequestedIndex) {
496+ createItem(modelIndex, false);
497+ m_implicitHeightDirty = true;
498+ polish();
499+ }
500+}
501+
502+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
503+void VerticalJournal::onModelUpdated(const QQuickChangeSet & /*changeSet*/, bool reset)
504+#else
505+void VerticalJournal::onModelUpdated(const QQmlChangeSet & /*changeSet*/, bool reset)
506+#endif
507+{
508+ // TODO Add support for additions/removals at end that are not reset calls
509+ // TODO Add a test for reset
510+ if (reset) {
511+ cleanupExistingItems();
512+ }
513+ polish();
514+}
515+
516+
517+void VerticalJournal::relayout()
518+{
519+ m_needsRelayout = true;
520+ polish();
521+}
522+
523+void VerticalJournal::onHeightChanged()
524+{
525+ polish();
526+}
527+
528+void VerticalJournal::updatePolish()
529+{
530+ if (!model())
531+ return;
532+
533+ if (m_needsRelayout) {
534+ m_implicitHeightDirty = true;
535+
536+ QList<ViewItem> allItems;
537+ Q_FOREACH(const auto &column, m_columnVisibleItems)
538+ allItems << column;
539+
540+ qSort(allItems);
541+
542+ const int nColumns = qMax(1., floor((double)(width() + m_columnSpacing) / (m_columnWidth + m_columnSpacing)));
543+ m_columnVisibleItems.resize(nColumns);
544+ m_indexColumnMap.clear();
545+ for (int i = 0; i < nColumns; ++i)
546+ m_columnVisibleItems[i].clear();
547+
548+ // If the first of allItems doesn't contain index 0 we need to drop them
549+ // all since we can't consistently relayout without the first item being there
550+
551+ if (!allItems.isEmpty()) {
552+ if (allItems.first().m_modelIndex == 0) {
553+ Q_FOREACH(const ViewItem &item, allItems)
554+ positionItem(item.m_modelIndex, item.m_item);
555+ } else {
556+ Q_FOREACH(const ViewItem &item, allItems)
557+ releaseItem(item);
558+ }
559+ }
560+
561+ m_needsRelayout = false;
562+ }
563+
564+ refill();
565+
566+ const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
567+ const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
568+ const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
569+ Q_FOREACH(const auto &column, m_columnVisibleItems) {
570+ Q_FOREACH(const ViewItem &item, column) {
571+ QQuickItemPrivate::get(item.m_item)->setCulled(item.y() + item.height() <= from || item.y() >= to);
572+ }
573+ }
574+
575+ if (m_implicitHeightDirty) {
576+ calculateImplicitHeight();
577+ }
578+}
579+
580+void VerticalJournal::componentComplete()
581+{
582+ if (m_delegateModel)
583+ m_delegateModel->componentComplete();
584+
585+ QQuickItem::componentComplete();
586+
587+ m_needsRelayout = true;
588+
589+ polish();
590+}
plugins/DashViews/verticaljournal.hnew file+182
@@ -0,0 +1,182 @@
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 VERTICALJOURNAL_H
18+#define VERTICALJOURNAL_H
19+
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
31+
32+ /** A vertical journal is a view that creates delegates
33+ * based on a model and layouts them in columns following
34+ * a top-left most position rule.
35+ *
36+ * The number of columns is calculated using the width of
37+ * the view itself, the columnWidth (i.e. the width of each individual delegate)
38+ * and the columnSpacing between columns.
39+ *
40+ * All delegates are forced to columnWidth if they don't have it.
41+ *
42+ * The first nColumns items are layouted at row 0 from column 0
43+ * to column nColumns-1 in order. After that every new item
44+ * is positioned in the column which provides the free topmost
45+ * position as possible. If more than one column tie in providing
46+ * the topmost free position the leftmost column will be used.
47+ *
48+ * Example:
49+ *
50+ * +-----+ +-----+ +-----+
51+ * | | | 2 | | |
52+ * | | | | | |
53+ * | 1 | +-----+ | 3 |
54+ * | | +-----+ | |
55+ * | | | | +-----+
56+ * +-----+ | 4 | +-----+
57+ * +-----+ | | | 5 |
58+ * | 6 | +-----+ | |
59+ * | | +-----+
60+ * +-----+
61+ *
62+ */
63+ class VerticalJournal : public QQuickItem
64+{
65+ Q_OBJECT
66+
67+ Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
68+ Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
69+ 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)
80+
81+friend class VerticalJournalTest;
82+
83+public:
84+ VerticalJournal();
85+
86+ QAbstractItemModel *model() const;
87+ void setModel(QAbstractItemModel *model);
88+
89+ QQmlComponent *delegate() const;
90+ void setDelegate(QQmlComponent *delegate);
91+
92+ qreal columnWidth() const;
93+ void setColumnWidth(qreal columnWidth);
94+
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+
109+Q_SIGNALS:
110+ void modelChanged();
111+ void delegateChanged();
112+ 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();
132+
133+private:
134+ class ViewItem
135+ {
136+ public:
137+ ViewItem(QQuickItem *item, int modelIndex) : m_item(item), m_modelIndex(modelIndex) {}
138+ qreal x() const { return m_item->x(); }
139+ qreal y() const { return m_item->y(); }
140+ qreal height() const { return m_item->height(); }
141+ bool operator<(const ViewItem &v) const { return m_modelIndex < v.m_modelIndex; }
142+
143+ QQuickItem *m_item;
144+ int m_modelIndex;
145+ };
146+
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+
159+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
160+ QQuickVisualDataModel *m_delegateModel;
161+#else
162+ QQmlDelegateModel *m_delegateModel;
163+#endif
164+
165+ // Index we are waiting because we requested it asynchronously
166+ int m_asyncRequestedIndex;
167+
168+ QVector<QList<ViewItem>> m_columnVisibleItems;
169+ QHash<int, int> m_indexColumnMap;
170+ 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;
180+};
181+
182+#endif
plugins/ListViewWithPageHeader/qmldirdeleted−3
@@ -1,3 +0,0 @@
1−module ListViewWithPageHeader
2−plugin ListViewWithPageHeader-qml
3−typeinfo plugin.qmltypes
tests/plugins/CMakeLists.txt+1−1
@@ -1,6 +1,6 @@
11add_subdirectory(AccountsService)
22add_subdirectory(LightDM)
3−add_subdirectory(ListViewWithPageHeader)
3+add_subdirectory(DashViews)
44add_subdirectory(Ubuntu)
55add_subdirectory(Unity)
66add_subdirectory(Utils)
tests/plugins/ListViewWithPageHeader/CMakeLists.txt → tests/plugins/DashViews/CMakeLists.txtrenamed+16−5
@@ -16,30 +16,40 @@ include_directories(
1616 ${Qt5Quick_INCLUDE_DIRS}
1717 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
1818 ${Qt5V8_PRIVATE_INCLUDE_DIR}
19− ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/ListViewWithPageHeader
19+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews
2020 ${CMAKE_CURRENT_BINARY_DIR}
2121 )
2222
2323remove_definitions(-DQT_NO_KEYWORDS)
2424
25−add_definitions(-DLISTVIEWWITHPAGEHEADER_FOLDER="${CMAKE_CURRENT_SOURCE_DIR}")
25+add_definitions(-DDASHVIEWSTEST_FOLDER="${CMAKE_CURRENT_SOURCE_DIR}")
2626add_definitions(-DBUILT_PLUGINS_DIR="${CMAKE_BINARY_DIR}/plugins")
2727
28−add_executable(listviewwithpageheadertestExec listviewwithpageheadertest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/ListViewWithPageHeader/listviewwithpageheader.cpp)
28+add_executable(listviewwithpageheadertestExec listviewwithpageheadertest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/listviewwithpageheader.cpp)
2929qt5_use_modules(listviewwithpageheadertestExec Test Core Qml)
3030target_link_libraries(listviewwithpageheadertestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
3131add_custom_target(testlistviewwithpageheader ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheader.xml,xunitxml -o -,txt)
3232
33−add_executable(listviewwithpageheadertestsectionExec listviewwithpageheadertestsection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/ListViewWithPageHeader/listviewwithpageheader.cpp)
33+add_executable(listviewwithpageheadertestsectionExec listviewwithpageheadertestsection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/listviewwithpageheader.cpp)
3434qt5_use_modules(listviewwithpageheadertestsectionExec Test Core Qml)
3535target_link_libraries(listviewwithpageheadertestsectionExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
3636add_custom_target(testlistviewwithpageheadersection ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestsectionExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheadersection.xml,xunitxml -o -,txt)
3737
38−add_executable(listviewwithpageheadertestsectionexternalmodelExec listviewwithpageheadertestsectionexternalmodel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/ListViewWithPageHeader/listviewwithpageheader.cpp)
38+add_executable(listviewwithpageheadertestsectionexternalmodelExec listviewwithpageheadertestsectionexternalmodel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/listviewwithpageheader.cpp)
3939qt5_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)
47+
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)
52+
4353set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
4454add_qml_test(. listviewwithpageheaderqml IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
4555
@@ -47,3 +57,4 @@ add_dependencies(qmluitests testlistviewwithpageheader)
4757add_dependencies(qmluitests testlistviewwithpageheadersection)
4858add_dependencies(qmluitests testlistviewwithpageheadersectionexternalmodel)
4959add_dependencies(qmluitests testlistviewwithpageheaderqml)
60+add_dependencies(qmluitests testVerticalJournal)
tests/plugins/ListViewWithPageHeader/listviewwithpageheadertest.cpp → tests/plugins/DashViews/listviewwithpageheadertest.cpprenamed+1−1
@@ -118,7 +118,7 @@ private Q_SLOTS:
118118 {
119119 view = new QQuickView();
120120 view->engine()->addImportPath(BUILT_PLUGINS_DIR);
121− view->setSource(QUrl::fromLocalFile(LISTVIEWWITHPAGEHEADER_FOLDER "/test.qml"));
121+ view->setSource(QUrl::fromLocalFile(DASHVIEWSTEST_FOLDER "/listviewwithpageheadertest.qml"));
122122 lvwph = dynamic_cast<ListViewWithPageHeader*>(view->rootObject()->findChild<QQuickFlickable*>());
123123#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
124124 model = view->rootObject()->findChild<QQuickListModel*>();
tests/plugins/ListViewWithPageHeader/test.qml → tests/plugins/DashViews/listviewwithpageheadertest.qmlrenamed+1−1
@@ -15,7 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18−import ListViewWithPageHeader 0.1
18+import DashViews 0.1
1919
2020Rectangle {
2121 width: 300
tests/plugins/ListViewWithPageHeader/listviewwithpageheadertestsection.cpp → tests/plugins/DashViews/listviewwithpageheadertestsection.cpprenamed+1−1
@@ -133,7 +133,7 @@ private Q_SLOTS:
133133 {
134134 view = new QQuickView();
135135 view->engine()->addImportPath(BUILT_PLUGINS_DIR);
136− view->setSource(QUrl::fromLocalFile(LISTVIEWWITHPAGEHEADER_FOLDER "/test_section.qml"));
136+ view->setSource(QUrl::fromLocalFile(DASHVIEWSTEST_FOLDER "/listviewwithpageheadertestsection.qml"));
137137 lvwph = dynamic_cast<ListViewWithPageHeader*>(view->rootObject()->findChild<QQuickFlickable*>());
138138#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
139139 model = view->rootObject()->findChild<QQuickListModel*>();
tests/plugins/ListViewWithPageHeader/test_section.qml → tests/plugins/DashViews/listviewwithpageheadertestsection.qmlrenamed+1−1
@@ -15,7 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18−import ListViewWithPageHeader 0.1
18+import DashViews 0.1
1919
2020Rectangle {
2121 width: 300
tests/plugins/ListViewWithPageHeader/listviewwithpageheadertestsectionexternalmodel.cpp → tests/plugins/DashViews/listviewwithpageheadertestsectionexternalmodel.cpprenamed+1−1
@@ -173,7 +173,7 @@ private Q_SLOTS:
173173 {
174174 view = new QQuickView();
175175 view->engine()->addImportPath(BUILT_PLUGINS_DIR);
176− view->setSource(QUrl::fromLocalFile(LISTVIEWWITHPAGEHEADER_FOLDER "/test_sectionexternalmodel.qml"));
176+ view->setSource(QUrl::fromLocalFile(DASHVIEWSTEST_FOLDER "/listviewwithpageheadertestsectionexternalmodel.qml"));
177177 lvwph = dynamic_cast<ListViewWithPageHeader*>(view->rootObject()->findChild<QQuickFlickable*>());
178178 QVERIFY(lvwph);
179179 view->show();
tests/plugins/ListViewWithPageHeader/test_sectionexternalmodel.qml → tests/plugins/DashViews/listviewwithpageheadertestsectionexternalmodel.qmlrenamed+1−1
@@ -15,7 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18−import ListViewWithPageHeader 0.1
18+import DashViews 0.1
1919
2020Rectangle {
2121 width: 300
tests/plugins/ListViewWithPageHeader/tst_listviewwithpageheaderqml.qml → tests/plugins/DashViews/tst_listviewwithpageheaderqml.qmlrenamed+1−1
@@ -16,7 +16,7 @@
1616
1717import QtQuick 2.0
1818import QtTest 1.0
19−import ListViewWithPageHeader 0.1
19+import DashViews 0.1
2020
2121Rectangle {
2222 width: 300
tests/plugins/DashViews/verticaljournaltest.cppnew file+315
@@ -0,0 +1,315 @@
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 "verticaljournal.h"
32+
33+class QHeightModel : public QStringListModel {
34+public:
35+ QHash<int, QByteArray> roleNames() const
36+ {
37+ QHash<int, QByteArray> roles;
38+ roles.insert(Qt::DisplayRole, "modelHeight");
39+ return roles;
40+ }
41+};
42+
43+class VerticalJournalTest : public QObject
44+{
45+ Q_OBJECT
46+
47+private:
48+ void verifyItem(const VerticalJournal::ViewItem &item, int modelIndex, qreal x, qreal y, bool visible)
49+ {
50+ QTRY_COMPARE(item.m_modelIndex, modelIndex);
51+ QTRY_COMPARE(item.x(), x);
52+ QTRY_COMPARE(item.y(), y);
53+ QTRY_COMPARE(item.height(), heightList[modelIndex].toDouble());
54+ QTRY_COMPARE(QQuickItemPrivate::get(item.m_item)->culled, !visible);
55+ }
56+
57+ void checkInitialPositions()
58+ {
59+ QTRY_COMPARE(vj->m_columnVisibleItems.count(), 3);
60+ QTRY_COMPARE(vj->m_columnVisibleItems[0].count(), 5);
61+ QTRY_COMPARE(vj->m_columnVisibleItems[1].count(), 7);
62+ QTRY_COMPARE(vj->m_columnVisibleItems[2].count(), 6);
63+ verifyItem(vj->m_columnVisibleItems[0][0], 0, 0, 0, true);
64+ verifyItem(vj->m_columnVisibleItems[1][0], 1, 160, 0, true);
65+ verifyItem(vj->m_columnVisibleItems[2][0], 2, 320, 0, true);
66+ verifyItem(vj->m_columnVisibleItems[1][1], 3, 160, 60, true);
67+ verifyItem(vj->m_columnVisibleItems[1][2], 4, 160, 80, true);
68+ verifyItem(vj->m_columnVisibleItems[0][1], 5, 0, 110, true);
69+ verifyItem(vj->m_columnVisibleItems[1][3], 6, 160, 130, true);
70+ verifyItem(vj->m_columnVisibleItems[2][1], 7, 320, 135, true);
71+ verifyItem(vj->m_columnVisibleItems[0][2], 8, 0, 190, true);
72+ verifyItem(vj->m_columnVisibleItems[2][2], 9, 320, 255, true);
73+ verifyItem(vj->m_columnVisibleItems[2][3], 10, 320, 285, true);
74+ verifyItem(vj->m_columnVisibleItems[2][4], 11, 320, 315, true);
75+ verifyItem(vj->m_columnVisibleItems[1][4], 12, 160, 340, true);
76+ verifyItem(vj->m_columnVisibleItems[0][3], 13, 0, 360, true);
77+ verifyItem(vj->m_columnVisibleItems[2][5], 14, 320, 390, true);
78+ verifyItem(vj->m_columnVisibleItems[1][5], 15, 160, 430, false);
79+ verifyItem(vj->m_columnVisibleItems[0][4], 16, 0, 570, false);
80+ verifyItem(vj->m_columnVisibleItems[1][6], 17, 160, 570, false);
81+ QCOMPARE(vj->implicitHeight(), 970. + 2. * 970. / 18.);
82+ }
83+
84+private Q_SLOTS:
85+ void init()
86+ {
87+ view = new QQuickView();
88+ view->setResizeMode(QQuickView::SizeRootObjectToView);
89+ view->engine()->addImportPath(BUILT_PLUGINS_DIR);
90+
91+ model = new QHeightModel();
92+ heightList.clear();
93+ heightList << "100" << "50" << "125" << "10" << "40" << "70" << "200" << "110" << "160" << "20" << "20" << "65" << "80" << "200" << "300" << "130" << "400" << "300" << "500" << "10";
94+ model->setStringList(heightList);
95+
96+ view->setSource(QUrl::fromLocalFile(DASHVIEWSTEST_FOLDER "/verticaljournaltest.qml"));
97+
98+ view->show();
99+ view->resize(470, 400);
100+ QTest::qWaitForWindowExposed(view);
101+
102+ vj = dynamic_cast<VerticalJournal*>(view->rootObject()->findChild<QObject*>("vj"));
103+ vj->setModel(model);
104+
105+ checkInitialPositions();
106+ }
107+
108+ void cleanup()
109+ {
110+ delete view;
111+ delete model;
112+ }
113+
114+ void testWidthResize()
115+ {
116+ view->resize(629, 400);
117+ QTRY_COMPARE(vj->width(), 629.);
118+
119+ // This is exactly the same block as above as nothing changed, just white space on the right
120+ checkInitialPositions();
121+
122+ view->resize(630, 400);
123+
124+ QTRY_COMPARE(vj->m_columnVisibleItems.count(), 4);
125+ QTRY_COMPARE(vj->m_columnVisibleItems[0].count(), 4);
126+ QTRY_COMPARE(vj->m_columnVisibleItems[1].count(), 7);
127+ QTRY_COMPARE(vj->m_columnVisibleItems[2].count(), 4);
128+ QTRY_COMPARE(vj->m_columnVisibleItems[3].count(), 5);
129+ verifyItem(vj->m_columnVisibleItems[0][0], 0, 0, 0, true);
130+ verifyItem(vj->m_columnVisibleItems[1][0], 1, 160, 0, true);
131+ verifyItem(vj->m_columnVisibleItems[2][0], 2, 320, 0, true);
132+ verifyItem(vj->m_columnVisibleItems[3][0], 3, 480, 0, true);
133+ verifyItem(vj->m_columnVisibleItems[3][1], 4, 480, 20, true);
134+ verifyItem(vj->m_columnVisibleItems[1][1], 5, 160, 60, true);
135+ verifyItem(vj->m_columnVisibleItems[3][2], 6, 480, 70, true);
136+ verifyItem(vj->m_columnVisibleItems[0][1], 7, 0, 110, true);
137+ verifyItem(vj->m_columnVisibleItems[2][1], 8, 320, 135, true);
138+ verifyItem(vj->m_columnVisibleItems[1][2], 9, 160, 140, true);
139+ verifyItem(vj->m_columnVisibleItems[1][3], 10, 160, 170, true);
140+ verifyItem(vj->m_columnVisibleItems[1][4], 11, 160, 200, true);
141+ verifyItem(vj->m_columnVisibleItems[0][2], 12, 0, 230, true);
142+ verifyItem(vj->m_columnVisibleItems[1][5], 13, 160, 275, true);
143+ verifyItem(vj->m_columnVisibleItems[3][3], 14, 480, 280, true);
144+ verifyItem(vj->m_columnVisibleItems[2][2], 15, 320, 305, true);
145+ verifyItem(vj->m_columnVisibleItems[0][3], 16, 0, 320, true);
146+ verifyItem(vj->m_columnVisibleItems[2][3], 17, 320, 445, false);
147+ verifyItem(vj->m_columnVisibleItems[1][6], 18, 160, 485, false);
148+ verifyItem(vj->m_columnVisibleItems[3][4], 19, 480, 590, false);
149+ QTRY_COMPARE(vj->implicitHeight(), 985.);
150+
151+ view->resize(470, 400);
152+
153+ // This is exactly the same block as the first again
154+ checkInitialPositions();
155+ }
156+
157+ void testColumnSpacing()
158+ {
159+ vj->setColumnSpacing(11);
160+
161+ QTRY_COMPARE(vj->m_needsRelayout, false);
162+ QTRY_COMPARE(vj->m_columnVisibleItems.count(), 2);
163+ QTRY_COMPARE(vj->m_columnVisibleItems[0].count(), 7);
164+ QTRY_COMPARE(vj->m_columnVisibleItems[1].count(), 7);
165+ verifyItem(vj->m_columnVisibleItems[0][0], 0, 0, 0, true);
166+ verifyItem(vj->m_columnVisibleItems[1][0], 1, 161, 0, true);
167+ verifyItem(vj->m_columnVisibleItems[1][1], 2, 161, 60, true);
168+ verifyItem(vj->m_columnVisibleItems[0][1], 3, 0, 110, true);
169+ verifyItem(vj->m_columnVisibleItems[0][2], 4, 0, 130, true);
170+ verifyItem(vj->m_columnVisibleItems[0][3], 5, 0, 180, true);
171+ verifyItem(vj->m_columnVisibleItems[1][2], 6, 161, 195, true);
172+ verifyItem(vj->m_columnVisibleItems[0][4], 7, 0, 260, true);
173+ verifyItem(vj->m_columnVisibleItems[0][5], 8, 0, 380, true);
174+ verifyItem(vj->m_columnVisibleItems[1][3], 9, 161, 405, false);
175+ verifyItem(vj->m_columnVisibleItems[1][4], 10, 161, 435, false);
176+ verifyItem(vj->m_columnVisibleItems[1][5], 11, 161, 465, false);
177+ verifyItem(vj->m_columnVisibleItems[1][6], 12, 161, 540, false);
178+ verifyItem(vj->m_columnVisibleItems[0][6], 13, 0, 550, false);
179+ QCOMPARE(vj->implicitHeight(), 750. + 6. * 750. / 14.);
180+
181+ vj->setColumnSpacing(10);
182+ QTRY_COMPARE(vj->m_needsRelayout, false);
183+
184+ // This is exactly the same block as the first again
185+ checkInitialPositions();
186+ }
187+
188+ void testRowSpacing()
189+ {
190+ vj->setRowSpacing(11);
191+
192+ QTRY_COMPARE(vj->m_needsRelayout, false);
193+ QTRY_COMPARE(vj->m_columnVisibleItems.count(), 3);
194+ QTRY_COMPARE(vj->m_columnVisibleItems[0].count(), 5);
195+ QTRY_COMPARE(vj->m_columnVisibleItems[1].count(), 7);
196+ QTRY_COMPARE(vj->m_columnVisibleItems[2].count(), 6);
197+ verifyItem(vj->m_columnVisibleItems[0][0], 0, 0, 0, true);
198+ verifyItem(vj->m_columnVisibleItems[1][0], 1, 160, 0, true);
199+ verifyItem(vj->m_columnVisibleItems[2][0], 2, 320, 0, true);
200+ verifyItem(vj->m_columnVisibleItems[1][1], 3, 160, 61, true);
201+ verifyItem(vj->m_columnVisibleItems[1][2], 4, 160, 82, true);
202+ verifyItem(vj->m_columnVisibleItems[0][1], 5, 0, 111, true);
203+ verifyItem(vj->m_columnVisibleItems[1][3], 6, 160, 133, true);
204+ verifyItem(vj->m_columnVisibleItems[2][1], 7, 320, 136, true);
205+ verifyItem(vj->m_columnVisibleItems[0][2], 8, 0, 192, true);
206+ verifyItem(vj->m_columnVisibleItems[2][2], 9, 320, 257, true);
207+ verifyItem(vj->m_columnVisibleItems[2][3], 10, 320, 288, true);
208+ verifyItem(vj->m_columnVisibleItems[2][4], 11, 320, 319, true);
209+ verifyItem(vj->m_columnVisibleItems[1][4], 12, 160, 344, true);
210+ verifyItem(vj->m_columnVisibleItems[0][3], 13, 0, 363, true);
211+ verifyItem(vj->m_columnVisibleItems[2][5], 14, 320, 395, true);
212+ verifyItem(vj->m_columnVisibleItems[1][5], 15, 160, 435, false);
213+ verifyItem(vj->m_columnVisibleItems[0][4], 16, 0, 574, false);
214+ verifyItem(vj->m_columnVisibleItems[1][6], 17, 160, 576, false);
215+ QCOMPARE(vj->implicitHeight(), 974. + 2. * 974. / 18.);
216+
217+ vj->setRowSpacing(10);
218+ QTRY_COMPARE(vj->m_needsRelayout, false);
219+
220+ // This is exactly the same block as the first again
221+ checkInitialPositions();
222+ }
223+
224+ void testDelegateCreationRanges()
225+ {
226+ vj->setDelegateCreationBegin(200);
227+ vj->setDelegateCreationEnd(view->height());
228+
229+ QTRY_COMPARE(vj->m_columnVisibleItems.count(), 3);
230+ QTRY_COMPARE(vj->m_columnVisibleItems[0].count(), 4);
231+ QTRY_COMPARE(vj->m_columnVisibleItems[1].count(), 4);
232+ QTRY_COMPARE(vj->m_columnVisibleItems[2].count(), 6);
233+ verifyItem(vj->m_columnVisibleItems[0][0], 0, 0, 0, false);
234+ verifyItem(vj->m_columnVisibleItems[2][0], 2, 320, 0, false);
235+ verifyItem(vj->m_columnVisibleItems[1][0], 4, 160, 80, false);
236+ verifyItem(vj->m_columnVisibleItems[0][1], 5, 0, 110, false);
237+ verifyItem(vj->m_columnVisibleItems[1][1], 6, 160, 130, true);
238+ verifyItem(vj->m_columnVisibleItems[2][1], 7, 320, 135, true);
239+ verifyItem(vj->m_columnVisibleItems[0][2], 8, 0, 190, true);
240+ verifyItem(vj->m_columnVisibleItems[2][2], 9, 320, 255, true);
241+ verifyItem(vj->m_columnVisibleItems[2][3], 10, 320, 285, true);
242+ verifyItem(vj->m_columnVisibleItems[2][4], 11, 320, 315, true);
243+ verifyItem(vj->m_columnVisibleItems[1][2], 12, 160, 340, true);
244+ verifyItem(vj->m_columnVisibleItems[0][3], 13, 0, 360, true);
245+ verifyItem(vj->m_columnVisibleItems[2][5], 14, 320, 390, true);
246+ verifyItem(vj->m_columnVisibleItems[1][3], 15, 160, 430, false);
247+ QCOMPARE(vj->implicitHeight(), 690. + 4. * 690. / 16.);
248+
249+ vj->resetDelegateCreationBegin();
250+ vj->resetDelegateCreationEnd();
251+
252+ // This is exactly the same block as the first again
253+ checkInitialPositions();
254+ }
255+
256+
257+ void testColumnWidthChange()
258+ {
259+ vj->setColumnWidth(200);
260+ QTRY_COMPARE(vj->m_columnVisibleItems.count(), 2);
261+ QTRY_COMPARE(vj->m_columnVisibleItems[0].count(), 7);
262+ QTRY_COMPARE(vj->m_columnVisibleItems[1].count(), 7);
263+ verifyItem(vj->m_columnVisibleItems[0][0], 0, 0, 0, true);
264+ verifyItem(vj->m_columnVisibleItems[1][0], 1, 210, 0, true);
265+ verifyItem(vj->m_columnVisibleItems[1][1], 2, 210, 60, true);
266+ verifyItem(vj->m_columnVisibleItems[0][1], 3, 0, 110, true);
267+ verifyItem(vj->m_columnVisibleItems[0][2], 4, 0, 130, true);
268+ verifyItem(vj->m_columnVisibleItems[0][3], 5, 0, 180, true);
269+ verifyItem(vj->m_columnVisibleItems[1][2], 6, 210, 195, true);
270+ verifyItem(vj->m_columnVisibleItems[0][4], 7, 0, 260, true);
271+ verifyItem(vj->m_columnVisibleItems[0][5], 8, 0, 380, true);
272+ verifyItem(vj->m_columnVisibleItems[1][3], 9, 210, 405, false);
273+ verifyItem(vj->m_columnVisibleItems[1][4], 10, 210, 435, false);
274+ verifyItem(vj->m_columnVisibleItems[1][5], 11, 210, 465, false);
275+ verifyItem(vj->m_columnVisibleItems[1][6], 12, 210, 540, false);
276+ verifyItem(vj->m_columnVisibleItems[0][6], 13, 0, 550, false);
277+ QCOMPARE(vj->implicitHeight(), 750. + 6. * 750. / 14.);
278+
279+ vj->setColumnWidth(150);
280+
281+ // This is exactly the same block as the first again
282+ checkInitialPositions();
283+ }
284+
285+ void testChangeModel()
286+ {
287+ QHeightModel model2;
288+ QStringList list2;
289+ list2 << "100" << "50" << "25" << "25" << "50" << "50";
290+ model2.setStringList(list2);
291+ vj->setModel(&model2);
292+ heightList = list2;
293+
294+ QTRY_COMPARE(vj->m_columnVisibleItems.count(), 3);
295+ QTRY_COMPARE(vj->m_columnVisibleItems[0].count(), 1);
296+ QTRY_COMPARE(vj->m_columnVisibleItems[1].count(), 2);
297+ QTRY_COMPARE(vj->m_columnVisibleItems[2].count(), 3);
298+ verifyItem(vj->m_columnVisibleItems[0][0], 0, 0, 0, true);
299+ verifyItem(vj->m_columnVisibleItems[1][0], 1, 160, 0, true);
300+ verifyItem(vj->m_columnVisibleItems[2][0], 2, 320, 0, true);
301+ verifyItem(vj->m_columnVisibleItems[2][1], 3, 320, 35, true);
302+ verifyItem(vj->m_columnVisibleItems[1][1], 4, 160, 60, true);
303+ verifyItem(vj->m_columnVisibleItems[2][2], 5, 320, 70, true);
304+ }
305+
306+private:
307+ QQuickView *view;
308+ VerticalJournal *vj;
309+ QStringList heightList;
310+ QHeightModel *model;
311+};
312+
313+QTEST_MAIN(VerticalJournalTest)
314+
315+#include "verticaljournaltest.moc"
tests/plugins/DashViews/verticaljournaltest.qmlnew file+44
@@ -0,0 +1,44 @@
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+ VerticalJournal {
23+ id: vj
24+ objectName: "vj"
25+ anchors.fill: parent
26+ columnWidth: 150
27+ columnSpacing: 10
28+ rowSpacing: 10
29+
30+ delegate: Rectangle {
31+ property real randomValue: Math.random()
32+ width: 150
33+ color: randomValue < 0.3 ? "green" : randomValue < 0.6 ? "blue" : "red";
34+ height: modelHeight
35+ border.width: 3
36+
37+ Text {
38+ text: index + "\ny: " + parent.y + "\nheight: " + parent.height
39+ x: 10
40+ y: 10
41+ }
42+ }
43+ }
44+}
tests/plugins/DashViews/verticaljournaltry.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 "verticaljournal.h"
28+
29+class QHeightModel : public QStringListModel {
30+ Q_OBJECT
31+public:
32+ QHash<int, QByteArray> roleNames() const
33+ {
34+ QHash<int, QByteArray> roles;
35+ roles.insert(Qt::DisplayRole, "modelHeight");
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+ QHeightModel 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 "/verticaljournaltry.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 "verticaljournaltry.moc"
tests/plugins/DashViews/verticaljournaltry.qmlnew file+100
@@ -0,0 +1,100 @@
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 height)
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: VerticalJournal {
32+ id: vj
33+ model: vjModel
34+ columnWidth: 100
35+ columnSpacing: 10
36+ rowSpacing: 10
37+ width: parent.width
38+ height: implicitHeight > 100 ? implicitHeight : 100
39+ delegateCreationBegin: lvwph.contentY
40+ delegateCreationEnd: lvwph.contentY + lvwph.height
41+
42+ delegate: Rectangle {
43+ width: 100
44+ color: "red";
45+ height: modelHeight
46+ border.width: 3
47+
48+ Text {
49+ text: index + "\ny: " + parent.y + "\nheight: " + parent.height
50+ x: 10
51+ y: 10
52+ }
53+ }
54+ }
55+ }
56+
57+ Rectangle {
58+ id: controls
59+ height: parent.height
60+ width: 100
61+ anchors.right: parent.right
62+ color: "gray"
63+ opacity: 0.4
64+
65+ Rectangle {
66+ id: addButton
67+ height: 50
68+ width: parent.width
69+ color: "blue"
70+ Text {
71+ text: "Add"
72+ anchors.centerIn: parent
73+ }
74+ MouseArea {
75+ anchors.fill: parent
76+ onClicked: root.add(addField.text)
77+ }
78+ }
79+ TextInput {
80+ id: addField
81+ anchors.top: addButton.bottom
82+ height: 30
83+ width: parent.width
84+ }
85+ Rectangle {
86+ anchors.top: addField.bottom
87+ height: 50
88+ width: parent.width
89+ color: "red"
90+ Text {
91+ text: "Remove"
92+ anchors.centerIn: parent
93+ }
94+ MouseArea {
95+ anchors.fill: parent
96+ onClicked: root.remove()
97+ }
98+ }
99+ }
100+}