@@ -27,88 +27,16 @@
2727 */
2828#include "verticaljournal.h"
2929
30−#include <qqmlengine.h>
3130#pragma GCC diagnostic push
3231#pragma GCC diagnostic ignored "-pedantic"
33−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
34−#include <private/qquickvisualdatamodel_p.h>
35−#else
36−#include <private/qqmldelegatemodel_p.h>
37−#include <qqmlinfo.h>
38−#endif
3932#include <private/qquickitem_p.h>
4033#pragma GCC diagnostic pop
4134
4235static const qreal bufferRatio = 0.5;
4336
4437VerticalJournal::VerticalJournal()
45− : m_delegateModel(nullptr)
46− , m_asyncRequestedIndex(-1)
47− , m_columnWidth(0)
48− , m_columnSpacing(0)
49− , m_rowSpacing(0)
50− , m_delegateCreationBegin(0)
51− , m_delegateCreationEnd(0)
52− , m_delegateCreationBeginValid(false)
53− , m_delegateCreationEndValid(false)
54− , m_needsRelayout(false)
55− , m_delegateValidated(false)
56− , m_implicitHeightDirty(false)
57−{
58− connect(this, SIGNAL(widthChanged()), this, SLOT(relayout()));
59− connect(this, SIGNAL(heightChanged()), this, SLOT(onHeightChanged()));
60−}
61−
62−QAbstractItemModel *VerticalJournal::model() const
38+ : m_columnWidth(0)
6339{
64− return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
65−}
66−
67−void VerticalJournal::setModel(QAbstractItemModel *model)
68−{
69− if (model != this->model()) {
70− if (!m_delegateModel) {
71− createDelegateModel();
72− } else {
73−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
74− disconnect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
75− }
76− m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
77− connect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
78−#else
79− disconnect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
80− }
81− m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
82− connect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
83−#endif
84−
85− cleanupExistingItems();
86−
87− Q_EMIT modelChanged();
88− polish();
89− }
90−}
91−
92−QQmlComponent *VerticalJournal::delegate() const
93−{
94− return m_delegateModel ? m_delegateModel->delegate() : nullptr;
95−}
96−
97−void VerticalJournal::setDelegate(QQmlComponent *delegate)
98−{
99− if (delegate != this->delegate()) {
100− if (!m_delegateModel) {
101− createDelegateModel();
102− }
103−
104− cleanupExistingItems();
105−
106− m_delegateModel->setDelegate(delegate);
107−
108− Q_EMIT delegateChanged();
109− m_delegateValidated = false;
110− polish();
111− }
11240}
11341
11442qreal VerticalJournal::columnWidth() const
@@ -133,133 +61,6 @@ void VerticalJournal::setColumnWidth(qreal columnWidth)
13361 }
13462}
13563
136−qreal VerticalJournal::columnSpacing() const
137−{
138− return m_columnSpacing;
139−}
140−
141−void VerticalJournal::setColumnSpacing(qreal columnSpacing)
142−{
143− if (columnSpacing != m_columnSpacing) {
144− m_columnSpacing = columnSpacing;
145− Q_EMIT columnSpacingChanged();
146−
147− if (isComponentComplete()) {
148− relayout();
149− }
150− }
151−}
152−
153−qreal VerticalJournal::rowSpacing() const
154−{
155− return m_rowSpacing;
156−}
157−
158−void VerticalJournal::setRowSpacing(qreal rowSpacing)
159−{
160− if (rowSpacing != m_rowSpacing) {
161− m_rowSpacing = rowSpacing;
162− Q_EMIT rowSpacingChanged();
163−
164− if (isComponentComplete()) {
165− relayout();
166− }
167− }
168−}
169−
170−qreal VerticalJournal::delegateCreationBegin() const
171−{
172− return m_delegateCreationBegin;
173−}
174−
175−void VerticalJournal::setDelegateCreationBegin(qreal begin)
176−{
177− m_delegateCreationBeginValid = true;
178− if (m_delegateCreationBegin == begin)
179− return;
180− m_delegateCreationBegin = begin;
181− if (isComponentComplete()) {
182− polish();
183− }
184− emit delegateCreationBeginChanged();
185−}
186−
187−void VerticalJournal::resetDelegateCreationBegin()
188−{
189− m_delegateCreationBeginValid = false;
190− if (m_delegateCreationBegin == 0)
191− return;
192− m_delegateCreationBegin = 0;
193− if (isComponentComplete()) {
194− polish();
195− }
196− emit delegateCreationBeginChanged();
197−}
198−
199−qreal VerticalJournal::delegateCreationEnd() const
200−{
201− return m_delegateCreationEnd;
202−}
203−
204−void VerticalJournal::setDelegateCreationEnd(qreal end)
205−{
206− m_delegateCreationEndValid = true;
207− if (m_delegateCreationEnd == end)
208− return;
209− m_delegateCreationEnd = end;
210− if (isComponentComplete()) {
211− polish();
212− }
213− emit delegateCreationEndChanged();
214−}
215−
216−void VerticalJournal::resetDelegateCreationEnd()
217−{
218− m_delegateCreationEndValid = false;
219− if (m_delegateCreationEnd == 0)
220− return;
221− m_delegateCreationEnd = 0;
222− if (isComponentComplete()) {
223− polish();
224− }
225− emit delegateCreationEndChanged();
226−}
227−
228−void VerticalJournal::createDelegateModel()
229−{
230−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
231− m_delegateModel = new QQuickVisualDataModel(qmlContext(this), this);
232− connect(m_delegateModel, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(itemCreated(int,QQuickItem*)));
233−#else
234− m_delegateModel = new QQmlDelegateModel(qmlContext(this), this);
235− connect(m_delegateModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(itemCreated(int,QObject*)));
236−#endif
237− if (isComponentComplete())
238− m_delegateModel->componentComplete();
239−}
240−
241−void VerticalJournal::refill()
242−{
243− if (!isComponentComplete()) {
244− return;
245− }
246−
247− const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
248− const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
249− const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
250− const qreal buffer = (to - from) * bufferRatio;
251− const qreal bufferFrom = from - buffer;
252− const qreal bufferTo = to + buffer;
253−
254− bool added = addVisibleItems(from, to, false);
255− bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
256− added |= addVisibleItems(bufferFrom, bufferTo, true);
257−
258− if (added || removed) {
259− m_implicitHeightDirty = true;
260− }
261−}
262−
26364void VerticalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
26465{
26566 *modelIndex = 0;
@@ -268,7 +69,7 @@ void VerticalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
26869 Q_FOREACH(const auto &column, m_columnVisibleItems) {
26970 if (!column.isEmpty()) {
27071 const ViewItem &item = column.last();
271− *yPos = qMin(*yPos, item.y() + item.height() + m_rowSpacing);
72+ *yPos = qMin(*yPos, item.y() + item.height() + rowSpacing());
27273 *modelIndex = qMax(*modelIndex, item.m_modelIndex + 1);
27374 } else {
27475 *yPos = 0;
@@ -287,7 +88,7 @@ void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
28788 const auto &column = m_columnVisibleItems[i];
28889 if (!column.isEmpty()) {
28990 const ViewItem &item = column.first();
290− const auto itemTopPos = item.y() - m_rowSpacing;
91+ const auto itemTopPos = item.y() - rowSpacing();
29192 if (itemTopPos > *yPos) {
29293 *yPos = itemTopPos;
29394 *modelIndex = item.m_modelIndex - 1;
@@ -309,38 +110,6 @@ void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
309110 }
310111}
311112
312−bool VerticalJournal::addVisibleItems(qreal fillFrom, qreal fillTo, bool asynchronous)
313−{
314− if (!delegate())
315− return false;
316−
317− if (m_delegateModel->count() == 0)
318− return false;
319−
320− int modelIndex;
321− qreal yPos;
322− findBottomModelIndexToAdd(&modelIndex, &yPos);
323− bool changed = false;
324− while (modelIndex < m_delegateModel->count() && yPos <= fillTo) {
325− if (!createItem(modelIndex, asynchronous))
326− break;
327−
328− changed = true;
329− findBottomModelIndexToAdd(&modelIndex, &yPos);
330− }
331−
332− findTopModelIndexToAdd(&modelIndex, &yPos);
333− while (modelIndex >= 0 && yPos > fillFrom) {
334− if (!createItem(modelIndex, asynchronous))
335− break;
336−
337− changed = true;
338− findTopModelIndexToAdd(&modelIndex, &yPos);
339− }
340−
341− return changed;
342−}
343−
344113bool VerticalJournal::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
345114{
346115 bool changed = false;
@@ -348,12 +117,12 @@ bool VerticalJournal::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
348117 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
349118 QList<ViewItem> &column = m_columnVisibleItems[i];
350119 while (!column.isEmpty() && column.first().y() + column.first().height() < bufferFrom) {
351− releaseItem(column.takeFirst());
120+ releaseItem(column.takeFirst().m_item);
352121 changed = true;
353122 }
354123
355124 while (!column.isEmpty() && column.last().y() > bufferTo) {
356− releaseItem(column.takeLast());
125+ releaseItem(column.takeLast().m_item);
357126 changed = true;
358127 }
359128 }
@@ -361,53 +130,20 @@ bool VerticalJournal::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
361130 return changed;
362131}
363132
364−QQuickItem *VerticalJournal::createItem(int modelIndex, bool asynchronous)
133+void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
365134{
366− if (asynchronous && m_asyncRequestedIndex != -1)
367− return nullptr;
368−
369− m_asyncRequestedIndex = -1;
370−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
371− QQuickItem *item = m_delegateModel->item(modelIndex, asynchronous);
372−#else
373− QObject* object = m_delegateModel->object(modelIndex, asynchronous);
374− QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
375−#endif
376− if (!item) {
377−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
378− m_asyncRequestedIndex = modelIndex;
379−#else
380− if (object) {
381− m_delegateModel->release(object);
382− if (!m_delegateValidated) {
383− m_delegateValidated = true;
384− QObject* delegateObj = delegate();
385− qmlInfo(delegateObj ? delegateObj : this) << "Delegate must be of Item type";
386− }
387− } else {
388− m_asyncRequestedIndex = modelIndex;
389− }
390−#endif
391− return nullptr;
392− } else {
393− if (item->width() != m_columnWidth) {
394− qWarning() << "Item" << modelIndex << "width is not the one that the columnWidth mandates, resetting it";
395− item->setWidth(m_columnWidth);
396− }
397− positionItem(modelIndex, item);
398− return item;
135+ if (item->width() != m_columnWidth) {
136+ qWarning() << "Item" << modelIndex << "width is not the one that the columnWidth mandates, resetting it";
137+ item->setWidth(m_columnWidth);
399138 }
400−}
401139
402−void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
403−{
404140 // Check if we add it to the bottom of existing column items
405141 const QList<ViewItem> &firstColumn = m_columnVisibleItems[0];
406− qreal columnToAddY = !firstColumn.isEmpty() ? firstColumn.last().y() + firstColumn.last().height() : -m_rowSpacing;
142+ qreal columnToAddY = !firstColumn.isEmpty() ? firstColumn.last().y() + firstColumn.last().height() : -rowSpacing();
407143 int columnToAddTo = 0;
408144 for (int i = 1; i < m_columnVisibleItems.count(); ++i) {
409145 const QList<ViewItem> &column = m_columnVisibleItems[i];
410− const qreal iY = !column.isEmpty() ? column.last().y() + column.last().height() : -m_rowSpacing;
146+ const qreal iY = !column.isEmpty() ? column.last().y() + column.last().height() : -rowSpacing();
411147 if (iY < columnToAddY) {
412148 columnToAddTo = i;
413149 columnToAddY = iY;
@@ -416,8 +152,8 @@ void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
416152
417153 const QList<ViewItem> &columnToAdd = m_columnVisibleItems[columnToAddTo];
418154 if (columnToAdd.isEmpty() || columnToAdd.last().m_modelIndex < modelIndex) {
419− item->setX(columnToAddTo * (m_columnWidth + m_columnSpacing));
420− item->setY(columnToAddY + m_rowSpacing);
155+ item->setX(columnToAddTo * (m_columnWidth + columnSpacing()));
156+ item->setY(columnToAddY + rowSpacing());
421157
422158 m_columnVisibleItems[columnToAddTo] << ViewItem(item, modelIndex);
423159 m_indexColumnMap[modelIndex] = columnToAddTo;
@@ -426,8 +162,8 @@ void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
426162 columnToAddTo = m_indexColumnMap[modelIndex];
427163 columnToAddY = m_columnVisibleItems[columnToAddTo].first().y();
428164
429− item->setX(columnToAddTo * (m_columnWidth + m_columnSpacing));
430− item->setY(columnToAddY - m_rowSpacing - item->height());
165+ item->setX(columnToAddTo * (m_columnWidth + columnSpacing()));
166+ item->setY(columnToAddY - rowSpacing() - item->height());
431167
432168 m_columnVisibleItems[columnToAddTo].prepend(ViewItem(item, modelIndex));
433169 }
@@ -439,29 +175,14 @@ void VerticalJournal::cleanupExistingItems()
439175 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
440176 QList<ViewItem> &column = m_columnVisibleItems[i];
441177 Q_FOREACH(const ViewItem &item, column)
442− releaseItem(item);
178+ releaseItem(item.m_item);
443179 column.clear();
444180 }
445181 m_indexColumnMap.clear();
446182}
447183
448−void VerticalJournal::releaseItem(const ViewItem &item)
449−{
450−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
451− QQuickVisualModel::ReleaseFlags flags = m_delegateModel->release(item.m_item);
452− if (flags & QQuickVisualModel::Destroyed) {
453−#else
454− QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item.m_item);
455− if (flags & QQmlDelegateModel::Destroyed) {
456−#endif
457− item.m_item->setParentItem(nullptr);
458− }
459−}
460−
461184void VerticalJournal::calculateImplicitHeight()
462185{
463− m_implicitHeightDirty = false;
464−
465186 int lastModelIndex = -1;
466187 qreal bottomMostY = 0;
467188 Q_FOREACH(const auto &column, m_columnVisibleItems) {
@@ -479,144 +200,80 @@ void VerticalJournal::calculateImplicitHeight()
479200 }
480201}
481202
482−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
483−void VerticalJournal::itemCreated(int modelIndex, QQuickItem *item)
484−{
485−#else
486−void VerticalJournal::itemCreated(int modelIndex, QObject *object)
203+void VerticalJournal::doRelayout()
487204{
488− QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
489− if (!item) {
490− qWarning() << "VerticalJournal::itemCreated got a non item for index" << modelIndex;
491− return;
205+ QList<ViewItem> allItems;
206+ Q_FOREACH(const auto &column, m_columnVisibleItems)
207+ allItems << column;
208+
209+ qSort(allItems);
210+
211+ const int nColumns = qMax(1., floor((double)(width() + columnSpacing()) / (m_columnWidth + columnSpacing())));
212+ m_columnVisibleItems.resize(nColumns);
213+ m_indexColumnMap.clear();
214+ for (int i = 0; i < nColumns; ++i)
215+ m_columnVisibleItems[i].clear();
216+
217+ // If the first of allItems doesn't contain index 0 we need to drop them
218+ // all since we can't consistently relayout without the first item being there
219+
220+ if (!allItems.isEmpty()) {
221+ if (allItems.first().m_modelIndex == 0) {
222+ Q_FOREACH(const ViewItem &item, allItems)
223+ positionItem(item.m_modelIndex, item.m_item);
224+ } else {
225+ Q_FOREACH(const ViewItem &item, allItems)
226+ releaseItem(item.m_item);
227+ }
492228 }
493−#endif
494− item->setParentItem(this);
495− if (modelIndex == m_asyncRequestedIndex) {
496− createItem(modelIndex, false);
497− m_implicitHeightDirty = true;
498− polish();
229+}
230+
231+void VerticalJournal::updateItemCulling(qreal visibleFrom, qreal visibleTo)
232+{
233+ Q_FOREACH(const auto &column, m_columnVisibleItems) {
234+ Q_FOREACH(const ViewItem &item, column) {
235+ QQuickItemPrivate::get(item.m_item)->setCulled(item.y() + item.height() <= visibleFrom || item.y() >= visibleTo);
236+ }
499237 }
500238}
501239
502240#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
503−void VerticalJournal::onModelUpdated(const QQuickChangeSet &changeSet, bool reset)
241+void VerticalJournal::processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes)
504242#else
505−void VerticalJournal::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
243+void VerticalJournal::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
506244#endif
507245{
508− if (reset) {
509− cleanupExistingItems();
510− } else {
511246#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
512− Q_FOREACH(const QQuickChangeSet::Remove &remove, changeSet.removes()) {
247+ Q_FOREACH(const QQuickChangeSet::Remove &remove, removes) {
513248#else
514− Q_FOREACH(const QQmlChangeSet::Remove &remove, changeSet.removes()) {
249+ Q_FOREACH(const QQmlChangeSet::Remove &remove, removes) {
515250#endif
516− for (int i = remove.count - 1; i >= 0; --i) {
517− const int indexToRemove = remove.index + i;
518− // Since we only support removing from the end, indexToRemove
519− // must refer to the last item of one of the columns or
520− // be bigger than them (because it's not in the viewport and
521− // thus we have not created a delegate for it)
522− bool found = false;
523− int lastCreatedIndex = INT_MIN;
524− for (int i = 0; !found && i < m_columnVisibleItems.count(); ++i) {
525− QList<ViewItem> &column = m_columnVisibleItems[i];
526− if (!column.isEmpty()) {
527− const int lastColumnIndex = column.last().m_modelIndex;
528− if (lastColumnIndex == indexToRemove) {
529− releaseItem(column.takeLast());
530− found = true;
531− }
532− lastCreatedIndex = qMax(lastCreatedIndex, lastColumnIndex);
533− }
534− }
535− if (!found) {
536− if (indexToRemove < lastCreatedIndex) {
537− qFatal("VerticalJournal only supports removal from the end of the model");
538− } else {
539− m_implicitHeightDirty = true;
251+ for (int i = remove.count - 1; i >= 0; --i) {
252+ const int indexToRemove = remove.index + i;
253+ // Since we only support removing from the end, indexToRemove
254+ // must refer to the last item of one of the columns or
255+ // be bigger than them (because it's not in the viewport and
256+ // thus we have not created a delegate for it)
257+ bool found = false;
258+ int lastCreatedIndex = INT_MIN;
259+ for (int i = 0; !found && i < m_columnVisibleItems.count(); ++i) {
260+ QList<ViewItem> &column = m_columnVisibleItems[i];
261+ if (!column.isEmpty()) {
262+ const int lastColumnIndex = column.last().m_modelIndex;
263+ if (lastColumnIndex == indexToRemove) {
264+ releaseItem(column.takeLast().m_item);
265+ found = true;
540266 }
267+ lastCreatedIndex = qMax(lastCreatedIndex, lastColumnIndex);
541268 }
542269 }
543− }
544− }
545− polish();
546−}
547−
548−
549−void VerticalJournal::relayout()
550−{
551− m_needsRelayout = true;
552− polish();
553−}
554−
555−void VerticalJournal::onHeightChanged()
556−{
557− polish();
558−}
559−
560−void VerticalJournal::updatePolish()
561−{
562− if (!model())
563− return;
564−
565− if (m_needsRelayout) {
566− m_implicitHeightDirty = true;
567−
568− QList<ViewItem> allItems;
569− Q_FOREACH(const auto &column, m_columnVisibleItems)
570− allItems << column;
571−
572− qSort(allItems);
573−
574− const int nColumns = qMax(1., floor((double)(width() + m_columnSpacing) / (m_columnWidth + m_columnSpacing)));
575− m_columnVisibleItems.resize(nColumns);
576− m_indexColumnMap.clear();
577− for (int i = 0; i < nColumns; ++i)
578− m_columnVisibleItems[i].clear();
579−
580− // If the first of allItems doesn't contain index 0 we need to drop them
581− // all since we can't consistently relayout without the first item being there
582−
583− if (!allItems.isEmpty()) {
584− if (allItems.first().m_modelIndex == 0) {
585− Q_FOREACH(const ViewItem &item, allItems)
586− positionItem(item.m_modelIndex, item.m_item);
587− } else {
588− Q_FOREACH(const ViewItem &item, allItems)
589− releaseItem(item);
270+ if (!found) {
271+ if (indexToRemove < lastCreatedIndex) {
272+ qFatal("VerticalJournal only supports removal from the end of the model");
273+ } else {
274+ setImplicitHeightDirty();
275+ }
590276 }
591277 }
592−
593− m_needsRelayout = false;
594− }
595−
596− refill();
597−
598− const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
599− const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
600− const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
601− Q_FOREACH(const auto &column, m_columnVisibleItems) {
602− Q_FOREACH(const ViewItem &item, column) {
603− QQuickItemPrivate::get(item.m_item)->setCulled(item.y() + item.height() <= from || item.y() >= to);
604− }
605278 }
606−
607− if (m_implicitHeightDirty) {
608− calculateImplicitHeight();
609− }
610−}
611−
612−void VerticalJournal::componentComplete()
613−{
614− if (m_delegateModel)
615− m_delegateModel->componentComplete();
616−
617− QQuickItem::componentComplete();
618−
619− m_needsRelayout = true;
620−
621− polish();
622279}