2 * Copyright (C) 2013 Canonical, Ltd.
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.
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.
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/>.
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
29#include "horizontaljournal.h"
31#include <qqmlengine.h>
32#pragma GCC diagnostic push
33#pragma GCC diagnostic ignored "-pedantic"
34#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
35#include <private/qquickvisualdatamodel_p.h>
37#include <private/qqmldelegatemodel_p.h>
40#include <private/qquickitem_p.h>
41#pragma GCC diagnostic pop
43static const qreal bufferRatio = 0.5;
45HorizontalJournal::HorizontalJournal()
46 : m_delegateModel(nullptr)
47 , m_asyncRequestedIndex(-1)
48 , m_firstVisibleIndex(-1)
50 , m_horizontalSpacing(0)
51 , m_verticalSpacing(0)
52 , m_delegateCreationBegin(0)
53 , m_delegateCreationEnd(0)
54 , m_delegateCreationBeginValid(false)
55 , m_delegateCreationEndValid(false)
56 , m_needsRelayout(false)
57 , m_delegateValidated(false)
58 , m_implicitHeightDirty(false)
60 connect(this, SIGNAL(widthChanged()), this, SLOT(relayout()));
61 connect(this, SIGNAL(heightChanged()), this, SLOT(onHeightChanged()));
64QAbstractItemModel *HorizontalJournal::model() const
66 return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
69void HorizontalJournal::setModel(QAbstractItemModel *model)
71 if (model != this->model()) {
72 if (!m_delegateModel) {
73 createDelegateModel();
75#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
76 m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
78 m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
80 // Cleanup the existing items
81 Q_FOREACH(QQuickItem *item, m_visibleItems)
83 m_visibleItems.clear();
84 m_lastInRowIndexPosition.clear();
85 m_firstVisibleIndex = 0;
87 Q_EMIT modelChanged();
92QQmlComponent *HorizontalJournal::delegate() const
94 return m_delegateModel ? m_delegateModel->delegate() : nullptr;
97void HorizontalJournal::setDelegate(QQmlComponent *delegate)
99 if (delegate != this->delegate()) {
100 if (!m_delegateModel) {
101 createDelegateModel();
104 // Cleanup the existing items
105 Q_FOREACH(QQuickItem *item, m_visibleItems)
107 m_visibleItems.clear();
108 m_lastInRowIndexPosition.clear();
109 m_firstVisibleIndex = 0;
111 m_delegateModel->setDelegate(delegate);
113 Q_EMIT delegateChanged();
114 m_delegateValidated = false;
119qreal HorizontalJournal::rowHeight() const
124void HorizontalJournal::setRowHeight(qreal rowHeight)
126 if (rowHeight != m_rowHeight) {
127 m_rowHeight = rowHeight;
128 Q_EMIT rowHeightChanged();
130 if (isComponentComplete()) {
131 Q_FOREACH(QQuickItem *item, m_visibleItems) {
132 item->setHeight(rowHeight);
139qreal HorizontalJournal::horizontalSpacing() const
141 return m_horizontalSpacing;
144void HorizontalJournal::setHorizontalSpacing(qreal horizontalSpacing)
146 if (horizontalSpacing != m_horizontalSpacing) {
147 m_horizontalSpacing = horizontalSpacing;
148 Q_EMIT horizontalSpacingChanged();
150 if (isComponentComplete()) {
156qreal HorizontalJournal::verticalSpacing() const
158 return m_verticalSpacing;
161void HorizontalJournal::setVerticalSpacing(qreal verticalSpacing)
163 if (verticalSpacing != m_verticalSpacing) {
164 m_verticalSpacing = verticalSpacing;
165 Q_EMIT verticalSpacingChanged();
167 if (isComponentComplete()) {
173qreal HorizontalJournal::delegateCreationBegin() const
175 return m_delegateCreationBegin;
178void HorizontalJournal::setDelegateCreationBegin(qreal begin)
180 m_delegateCreationBeginValid = true;
181 if (m_delegateCreationBegin == begin)
183 m_delegateCreationBegin = begin;
184 if (isComponentComplete()) {
187 emit delegateCreationBeginChanged();
190void HorizontalJournal::resetDelegateCreationBegin()
192 m_delegateCreationBeginValid = false;
193 if (m_delegateCreationBegin == 0)
195 m_delegateCreationBegin = 0;
196 if (isComponentComplete()) {
199 emit delegateCreationBeginChanged();
202qreal HorizontalJournal::delegateCreationEnd() const
204 return m_delegateCreationEnd;
207void HorizontalJournal::setDelegateCreationEnd(qreal end)
209 m_delegateCreationEndValid = true;
210 if (m_delegateCreationEnd == end)
212 m_delegateCreationEnd = end;
213 if (isComponentComplete()) {
216 emit delegateCreationEndChanged();
219void HorizontalJournal::resetDelegateCreationEnd()
221 m_delegateCreationEndValid = false;
222 if (m_delegateCreationEnd == 0)
224 m_delegateCreationEnd = 0;
225 if (isComponentComplete()) {
228 emit delegateCreationEndChanged();
231void HorizontalJournal::createDelegateModel()
233#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
234 m_delegateModel = new QQuickVisualDataModel(qmlContext(this), this);
235 connect(m_delegateModel, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(itemCreated(int,QQuickItem*)));
237 m_delegateModel = new QQmlDelegateModel(qmlContext(this), this);
238 connect(m_delegateModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(itemCreated(int,QObject*)));
240 if (isComponentComplete())
241 m_delegateModel->componentComplete();
244void HorizontalJournal::refill()
246 if (!isComponentComplete()) {
250 const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
251 const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
252 const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
253 const qreal buffer = (to - from) * bufferRatio;
254 const qreal bufferFrom = from - buffer;
255 const qreal bufferTo = to + buffer;
257 bool added = addVisibleItems(from, to, false);
258 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
259 added |= addVisibleItems(bufferFrom, bufferTo, true);
261 if (added || removed) {
262 m_implicitHeightDirty = true;
266void HorizontalJournal::findBottomModelIndexToAdd(int *modelIndex, double *yPos)
268 if (m_visibleItems.isEmpty()) {
270 *yPos = m_verticalSpacing;
272 *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
273 if (m_lastInRowIndexPosition.contains(*modelIndex - 1)) {
274 *yPos = m_visibleItems.last()->y() + m_rowHeight + m_verticalSpacing;
276 *yPos = m_visibleItems.last()->y();
281void HorizontalJournal::findTopModelIndexToAdd(int *modelIndex, double *yPos)
283 if (m_visibleItems.isEmpty()) {
287 *modelIndex = m_firstVisibleIndex - 1;
288 if (m_lastInRowIndexPosition.contains(*modelIndex)) {
289 *yPos = m_visibleItems.first()->y() - m_verticalSpacing - m_rowHeight;
291 *yPos = m_visibleItems.first()->y();
296bool HorizontalJournal::addVisibleItems(qreal fillFrom, qreal fillTo, bool asynchronous)
301 if (m_delegateModel->count() == 0)
306 findBottomModelIndexToAdd(&modelIndex, &yPos);
307 bool changed = false;
308 while (modelIndex < m_delegateModel->count() && yPos <= fillTo) {
309 if (!createItem(modelIndex, asynchronous))
313 findBottomModelIndexToAdd(&modelIndex, &yPos);
316 findTopModelIndexToAdd(&modelIndex, &yPos);
317 while (modelIndex >= 0 && yPos > fillFrom) {
318 if (!createItem(modelIndex, asynchronous))
322 findTopModelIndexToAdd(&modelIndex, &yPos);
328bool HorizontalJournal::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
330 bool changed = false;
332 while (!m_visibleItems.isEmpty() && m_visibleItems.first()->y() + m_rowHeight < bufferFrom) {
333 releaseItem(m_visibleItems.takeFirst());
335 m_firstVisibleIndex++;
338 while (!m_visibleItems.isEmpty() && m_visibleItems.last()->y() > bufferTo) {
339 releaseItem(m_visibleItems.takeLast());
343 if (m_visibleItems.isEmpty()) {
344 m_firstVisibleIndex = -1;
350QQuickItem *HorizontalJournal::createItem(int modelIndex, bool asynchronous)
352 if (asynchronous && m_asyncRequestedIndex != -1)
355 m_asyncRequestedIndex = -1;
356#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
357 QQuickItem *item = m_delegateModel->item(modelIndex, asynchronous);
359 QObject* object = m_delegateModel->object(modelIndex, asynchronous);
360 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
363#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
364 m_asyncRequestedIndex = modelIndex;
367 m_delegateModel->release(object);
368 if (!m_delegateValidated) {
369 m_delegateValidated = true;
370 QObject* delegateObj = delegate();
371 qmlInfo(delegateObj ? delegateObj : this) << "Delegate must be of Item type";
374 m_asyncRequestedIndex = modelIndex;
379 if (item->height() != m_rowHeight) {
380 qWarning() << "Item" << modelIndex << "height is not the one that the rowHeight mandates, resetting it";
381 item->setHeight(m_rowHeight);
383 positionItem(modelIndex, item);
388void HorizontalJournal::positionItem(int modelIndex, QQuickItem *item)
390 if (m_visibleItems.isEmpty()) {
391 Q_ASSERT(modelIndex == 0);
392 item->setY(m_verticalSpacing);
393 item->setX(m_horizontalSpacing);
394 m_visibleItems << item;
395 m_firstVisibleIndex = 0;
397 // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count()
398 if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
399 QQuickItem *lastItem = m_visibleItems.last();
400 if (lastItem->x() + lastItem->width() + m_horizontalSpacing + item->width() + m_horizontalSpacing <= width()) {
402 item->setY(lastItem->y());
403 item->setX(lastItem->x() + lastItem->width() + m_horizontalSpacing);
406 item->setY(lastItem->y() + m_rowHeight + m_verticalSpacing);
407 item->setX(m_horizontalSpacing);
408 m_lastInRowIndexPosition[modelIndex - 1] = lastItem->x();
410 m_visibleItems << item;
411 } else if (modelIndex == m_firstVisibleIndex - 1) {
412 QQuickItem *firstItem = m_visibleItems.first();
413 if (m_lastInRowIndexPosition.contains(modelIndex)) {
414 // 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);
416 item->setX(m_lastInRowIndexPosition[modelIndex]);
418 item->setY(firstItem->y());
419 item->setX(firstItem->x() - m_horizontalSpacing - item->width());
421 m_firstVisibleIndex = modelIndex;
422 m_visibleItems.prepend(item);
424 qWarning() << "HorizontalJournal::positionItem - Got unexpected modelIndex" << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
429void HorizontalJournal::releaseItem(QQuickItem *item)
431#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
432 QQuickVisualModel::ReleaseFlags flags = m_delegateModel->release(item);
433 if (flags & QQuickVisualModel::Destroyed) {
435 QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item);
436 if (flags & QQmlDelegateModel::Destroyed) {
438 item->setParentItem(nullptr);
442#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
443void HorizontalJournal::itemCreated(int modelIndex, QQuickItem *item)
446void HorizontalJournal::itemCreated(int modelIndex, QObject *object)
448 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
450 qWarning() << "ListViewWithPageHeader::itemCreated got a non item for index" << modelIndex;
454 item->setParentItem(this);
455 if (modelIndex == m_asyncRequestedIndex) {
456 createItem(modelIndex, false);
457 m_implicitHeightDirty = true;
462void HorizontalJournal::relayout()
464 m_needsRelayout = true;
468void HorizontalJournal::onHeightChanged()
473void HorizontalJournal::updatePolish()
478 if (m_needsRelayout) {
479 // If m_firstVisibleIndex is not 0 we need to drop all the delegates
480 // since we can't consistently relayout without the first item being there
482 if (m_firstVisibleIndex == 0) {
484 Q_FOREACH(QQuickItem *item, m_visibleItems) {
485 positionItem(i, item);
489 Q_FOREACH(QQuickItem *item, m_visibleItems) {
492 m_visibleItems.clear();
493 m_lastInRowIndexPosition.clear();
494 m_firstVisibleIndex = 0;
497 m_needsRelayout = false;
502 const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
503 const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
504 const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
505 Q_FOREACH(QQuickItem *item, m_visibleItems) {
506 QQuickItemPrivate::get(item)->setCulled(item->y() + m_rowHeight <= from || item->y() >= to);
509 if (m_implicitHeightDirty) {
510 m_implicitHeightDirty = false;
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));
518 setImplicitHeight(0);
523void HorizontalJournal::componentComplete()
526 m_delegateModel->componentComplete();
528 QQuickItem::componentComplete();
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list