on 2ce304c help sign in
unity8/plugins/Dash/horizontaljournal.cpp
244 lines 8.3 kB history raw
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 */
17/*
18 * The implementation is centered around m_visibleItems
19 * that a list for each of the items in the view.
20 * m_firstVisibleIndex is the index of the first item in m_visibleItems
21 * m_lastInRowIndexPosition is a map that contains the x position
22 * of items that are the last ones of a row so we can reconstruct the rows
23 * when building back
24 */
26#include "horizontaljournal.h"
28#include <qqmlengine.h>
29#pragma GCC diagnostic push
30#pragma GCC diagnostic ignored "-pedantic"
31#include <private/qqmldelegatemodel_p.h>
32#include <qqmlinfo.h>
33#include <private/qquickitem_p.h>
34#pragma GCC diagnostic pop
36HorizontalJournal::HorizontalJournal()
37 : m_firstVisibleIndex(-1)
38 , m_rowHeight(0)
42qreal HorizontalJournal::rowHeight() const
44 return m_rowHeight;
47void HorizontalJournal::setRowHeight(qreal rowHeight)
49 if (rowHeight != m_rowHeight) {
50 m_rowHeight = rowHeight;
51 Q_EMIT rowHeightChanged();
53 if (isComponentComplete()) {
54 Q_FOREACH(QQuickItem *item, m_visibleItems) {
55 item->setHeight(rowHeight);
56 }
57 relayout();
58 }
59 }
62void HorizontalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
64 if (m_visibleItems.isEmpty()) {
65 *modelIndex = 0;
66 *yPos = 0;
67 } else {
68 *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
69 if (m_lastInRowIndexPosition.contains(*modelIndex - 1)) {
70 *yPos = m_visibleItems.last()->y() + m_rowHeight + rowSpacing();
71 } else {
72 *yPos = m_visibleItems.last()->y();
73 }
74 }
77void HorizontalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
79 if (m_visibleItems.isEmpty()) {
80 *modelIndex = -1;
81 *yPos = INT_MIN;
82 } else {
83 *modelIndex = m_firstVisibleIndex - 1;
84 if (m_lastInRowIndexPosition.contains(*modelIndex)) {
85 *yPos = m_visibleItems.first()->y() - rowSpacing() - m_rowHeight;
86 } else {
87 *yPos = m_visibleItems.first()->y();
88 }
89 }
92bool HorizontalJournal::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
94 bool changed = false;
96 while (!m_visibleItems.isEmpty() && m_visibleItems.first()->y() + m_rowHeight < bufferFromY) {
97 releaseItem(m_visibleItems.takeFirst());
98 changed = true;
99 m_firstVisibleIndex++;
100 }
102 while (!m_visibleItems.isEmpty() && m_visibleItems.last()->y() > bufferToY) {
103 releaseItem(m_visibleItems.takeLast());
104 changed = true;
105 m_lastInRowIndexPosition.remove(m_firstVisibleIndex + m_visibleItems.count());
106 }
108 if (m_visibleItems.isEmpty()) {
109 m_firstVisibleIndex = -1;
110 }
112 return changed;
115void HorizontalJournal::addItemToView(int modelIndex, QQuickItem *item)
117 if (item->height() != m_rowHeight) {
118 qWarning() << "Item" << modelIndex << "height is not the one that the rowHeight mandates, resetting it";
119 item->setHeight(m_rowHeight);
120 }
122 if (m_visibleItems.isEmpty()) {
123 Q_ASSERT(modelIndex == 0);
124 item->setY(0);
125 item->setX(0);
126 m_visibleItems << item;
127 m_firstVisibleIndex = 0;
128 } else {
129 // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count()
130 if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
131 QQuickItem *lastItem = m_visibleItems.last();
132 if (lastItem->x() + lastItem->width() + columnSpacing() + item->width() <= width()) {
133 // Fits in the row
134 item->setY(lastItem->y());
135 item->setX(lastItem->x() + lastItem->width() + columnSpacing());
136 } else {
137 // Starts a new row
138 item->setY(lastItem->y() + m_rowHeight + rowSpacing());
139 item->setX(0);
140 m_lastInRowIndexPosition[modelIndex - 1] = lastItem->x();
141 }
142 m_visibleItems << item;
143 } else if (modelIndex == m_firstVisibleIndex - 1) {
144 QQuickItem *firstItem = m_visibleItems.first();
145 if (m_lastInRowIndexPosition.contains(modelIndex)) {
146 // It is the last item of its row, so start a new one since we're going back
147 item->setY(firstItem->y() - rowSpacing() - m_rowHeight);
148 item->setX(m_lastInRowIndexPosition[modelIndex]);
149 } else {
150 item->setY(firstItem->y());
151 item->setX(firstItem->x() - columnSpacing() - item->width());
152 }
153 m_firstVisibleIndex = modelIndex;
154 m_visibleItems.prepend(item);
155 } else {
156 qWarning() << "HorizontalJournal::addItemToView - Got unexpected modelIndex"
157 << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
158 }
159 }
162void HorizontalJournal::cleanupExistingItems()
164 // Cleanup the existing items
165 Q_FOREACH(QQuickItem *item, m_visibleItems)
166 releaseItem(item);
167 m_visibleItems.clear();
168 m_lastInRowIndexPosition.clear();
169 m_firstVisibleIndex = -1;
170 setImplicitHeightDirty();
173void HorizontalJournal::calculateImplicitHeight()
175 if (m_firstVisibleIndex >= 0) {
176 const int nIndexes = m_firstVisibleIndex + m_visibleItems.count();
177 const double bottomMostY = m_visibleItems.last()->y() + m_rowHeight;
178 const double averageHeight = bottomMostY / nIndexes;
179 setImplicitHeight(bottomMostY + averageHeight * (model()->rowCount() - nIndexes));
180 } else {
181 setImplicitHeight(0);
182 }
185#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
186void HorizontalJournal::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
187#else
188void HorizontalJournal::processModelRemoves(const QVector<QQmlChangeSet::Change> &removes)
189#endif
191 Q_FOREACH(const QQmlChangeSet::Change &remove, removes) {
192 for (int i = remove.count - 1; i >= 0; --i) {
193 const int indexToRemove = remove.index + i;
194 // We only support removing from the end so
195 // any of the last items of a column has to be indexToRemove
196 const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
197 if (indexToRemove == lastIndex) {
198 releaseItem(m_visibleItems.takeLast());
199 m_lastInRowIndexPosition.remove(indexToRemove);
200 } else {
201 if (indexToRemove < lastIndex) {
202 qFatal("HorizontalJournal only supports removal from the end of the model");
203 } else {
204 setImplicitHeightDirty();
205 }
206 }
207 }
208 }
209 if (m_visibleItems.isEmpty()) {
210 m_firstVisibleIndex = -1;
211 }
215void HorizontalJournal::doRelayout()
217 // If m_firstVisibleIndex is not 0 we need to drop all the delegates
218 // since we can't consistently relayout without the first item being there
220 if (m_firstVisibleIndex == 0) {
221 int i = 0;
222 const QList<QQuickItem*> allItems = m_visibleItems;
223 m_visibleItems.clear();
224 m_lastInRowIndexPosition.clear();
225 Q_FOREACH(QQuickItem *item, allItems) {
226 addItemToView(i, item);
227 ++i;
228 }
229 } else {
230 Q_FOREACH(QQuickItem *item, m_visibleItems) {
231 releaseItem(item);
232 }
233 m_visibleItems.clear();
234 m_lastInRowIndexPosition.clear();
235 m_firstVisibleIndex = -1;
236 }
239void HorizontalJournal::updateItemCulling(qreal visibleFromY, qreal visibleToY)
241 Q_FOREACH(QQuickItem *item, m_visibleItems) {
242 QQuickItemPrivate::get(item)->setCulled(item->y() + m_rowHeight <= visibleFromY || item->y() >= visibleToY);
243 }