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
28#include "verticaljournal.h"
30#pragma GCC diagnostic push
31#pragma GCC diagnostic ignored "-pedantic"
32#include <private/qquickitem_p.h>
33#pragma GCC diagnostic pop
35VerticalJournal::VerticalJournal()
40qreal VerticalJournal::columnWidth() const
45void VerticalJournal::setColumnWidth(qreal columnWidth)
47 if (columnWidth != m_columnWidth) {
48 m_columnWidth = columnWidth;
49 Q_EMIT columnWidthChanged();
51 if (isComponentComplete()) {
52 Q_FOREACH(const auto &column, m_columnVisibleItems) {
53 Q_FOREACH(const ViewItem item, column) {
54 item.m_item->setWidth(columnWidth);
62void VerticalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
65 *yPos = std::numeric_limits<qreal>::max();
67 Q_FOREACH(const auto &column, m_columnVisibleItems) {
68 if (!column.isEmpty()) {
69 const ViewItem &item = column.last();
70 *yPos = qMin(*yPos, item.y() + item.height() + rowSpacing());
71 *modelIndex = qMax(*modelIndex, item.m_modelIndex + 1);
78void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
81 *yPos = std::numeric_limits<qreal>::lowest();
82 int columnToAddTo = -1;
84 // Find the topmost free column
85 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
86 const auto &column = m_columnVisibleItems[i];
87 if (!column.isEmpty()) {
88 const ViewItem &item = column.first();
89 const auto itemTopPos = item.y() - rowSpacing();
90 if (itemTopPos > *yPos) {
92 *modelIndex = item.m_modelIndex - 1;
98 if (*modelIndex > 0) {
99 Q_ASSERT(m_indexColumnMap.contains(*modelIndex));
100 while (m_indexColumnMap[*modelIndex] != columnToAddTo) {
101 // We found out that we have to add to columnToAddTo
102 // and thought that we had to add *modelIndex, but history tells
103 // it is not correct, so find up from *modelIndex until we found the index
104 // that has to end up in columnToAddTo
105 *modelIndex = *modelIndex - 1;
106 Q_ASSERT(m_indexColumnMap.contains(*modelIndex));
111bool VerticalJournal::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
113 bool changed = false;
115 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
116 QList<ViewItem> &column = m_columnVisibleItems[i];
117 while (!column.isEmpty() && column.first().y() + column.first().height() < bufferFromY) {
118 releaseItem(column.takeFirst().m_item);
122 while (!column.isEmpty() && column.last().y() > bufferToY) {
123 releaseItem(column.takeLast().m_item);
131void VerticalJournal::addItemToView(int modelIndex, QQuickItem *item)
133 if (item->width() != m_columnWidth) {
134 qWarning() << "Item" << modelIndex << "width is not the one that the columnWidth mandates, resetting it";
135 item->setWidth(m_columnWidth);
138 // Check if we add it to the bottom of existing column items
139 const QList<ViewItem> &firstColumn = m_columnVisibleItems[0];
140 qreal columnToAddY = !firstColumn.isEmpty() ? firstColumn.last().y() + firstColumn.last().height() : -rowSpacing();
141 int columnToAddTo = 0;
142 for (int i = 1; i < m_columnVisibleItems.count(); ++i) {
143 const QList<ViewItem> &column = m_columnVisibleItems[i];
144 const qreal iY = !column.isEmpty() ? column.last().y() + column.last().height() : -rowSpacing();
145 if (iY < columnToAddY) {
151 const QList<ViewItem> &columnToAdd = m_columnVisibleItems[columnToAddTo];
152 if (columnToAdd.isEmpty() || columnToAdd.last().m_modelIndex < modelIndex) {
153 item->setX(columnToAddTo * (m_columnWidth + columnSpacing()));
154 item->setY(columnToAddY + rowSpacing());
156 m_columnVisibleItems[columnToAddTo] << ViewItem(item, modelIndex);
157 m_indexColumnMap[modelIndex] = columnToAddTo;
159 Q_ASSERT(m_indexColumnMap.contains(modelIndex));
160 columnToAddTo = m_indexColumnMap[modelIndex];
161 columnToAddY = m_columnVisibleItems[columnToAddTo].first().y();
163 item->setX(columnToAddTo * (m_columnWidth + columnSpacing()));
164 item->setY(columnToAddY - rowSpacing() - item->height());
166 m_columnVisibleItems[columnToAddTo].prepend(ViewItem(item, modelIndex));
170void VerticalJournal::cleanupExistingItems()
172 // Cleanup the existing items
173 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
174 QList<ViewItem> &column = m_columnVisibleItems[i];
175 Q_FOREACH(const ViewItem item, column)
176 releaseItem(item.m_item);
179 m_indexColumnMap.clear();
180 setImplicitHeightDirty();
183void VerticalJournal::calculateImplicitHeight()
185 int lastModelIndex = -1;
186 qreal bottomMostY = 0;
187 Q_FOREACH(const auto &column, m_columnVisibleItems) {
188 if (!column.isEmpty()) {
189 const ViewItem &item = column.last();
190 lastModelIndex = qMax(lastModelIndex, item.m_modelIndex);
191 bottomMostY = qMax(bottomMostY, item.y() + item.height());
194 if (lastModelIndex >= 0) {
195 const double averageHeight = bottomMostY / (lastModelIndex + 1);
196 setImplicitHeight(bottomMostY + averageHeight * (model()->rowCount() - lastModelIndex - 1));
198 setImplicitHeight(0);
202void VerticalJournal::doRelayout()
204 QList<ViewItem> allItems;
205 Q_FOREACH(const auto &column, m_columnVisibleItems)
210 const int nColumns = qMax(1., floor((double)(width() + columnSpacing()) / (m_columnWidth + columnSpacing())));
211 m_columnVisibleItems.resize(nColumns);
212 m_indexColumnMap.clear();
213 for (int i = 0; i < nColumns; ++i)
214 m_columnVisibleItems[i].clear();
216 // If the first of allItems doesn't contain index 0 we need to drop them
217 // all since we can't consistently relayout without the first item being there
219 if (!allItems.isEmpty()) {
220 if (allItems.first().m_modelIndex == 0) {
221 Q_FOREACH(const ViewItem item, allItems)
222 addItemToView(item.m_modelIndex, item.m_item);
224 Q_FOREACH(const ViewItem item, allItems)
225 releaseItem(item.m_item);
230void VerticalJournal::updateItemCulling(qreal visibleFromY, qreal visibleToY)
232 Q_FOREACH(const auto &column, m_columnVisibleItems) {
233 Q_FOREACH(const ViewItem item, column) {
234 const bool cull = item.y() + item.height() <= visibleFromY || item.y() >= visibleToY;
235 QQuickItemPrivate::get(item.m_item)->setCulled(cull);
240void VerticalJournal::processModelRemoves(const QVector<QQmlChangeSet::Change> &removes)
242 Q_FOREACH(const QQmlChangeSet::Change remove, removes) {
243 for (int i = remove.count - 1; i >= 0; --i) {
244 const int indexToRemove = remove.index + i;
245 // Since we only support removing from the end, indexToRemove
246 // must refer to the last item of one of the columns or
247 // be bigger than them (because it's not in the viewport and
248 // thus we have not created a delegate for it)
250 int lastCreatedIndex = INT_MIN;
251 for (int i = 0; !found && i < m_columnVisibleItems.count(); ++i) {
252 QList<ViewItem> &column = m_columnVisibleItems[i];
253 if (!column.isEmpty()) {
254 const int lastColumnIndex = column.last().m_modelIndex;
255 if (lastColumnIndex == indexToRemove) {
256 releaseItem(column.takeLast().m_item);
259 lastCreatedIndex = qMax(lastCreatedIndex, lastColumnIndex);
263 if (indexToRemove < lastCreatedIndex) {
264 qDebug() << "VerticalJournal only supports removal from the end of the model, resetting instead";
265 cleanupExistingItems();
268 setImplicitHeightDirty();
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