on 032ca4d help sign in
unity8/plugins/DashViews/horizontaljournal.cpp
254 lines 8.6 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#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
32#include <private/qquickvisualdatamodel_p.h>
33#else
34#include <private/qqmldelegatemodel_p.h>
35#include <qqmlinfo.h>
36#endif
37#include <private/qquickitem_p.h>
38#pragma GCC diagnostic pop
40static const qreal bufferRatio = 0.5;
42HorizontalJournal::HorizontalJournal()
43 : m_firstVisibleIndex(-1)
44 , m_rowHeight(0)
48qreal HorizontalJournal::rowHeight() const
50 return m_rowHeight;
53void HorizontalJournal::setRowHeight(qreal rowHeight)
55 if (rowHeight != m_rowHeight) {
56 m_rowHeight = rowHeight;
57 Q_EMIT rowHeightChanged();
59 if (isComponentComplete()) {
60 Q_FOREACH(QQuickItem *item, m_visibleItems) {
61 item->setHeight(rowHeight);
62 }
63 relayout();
64 }
65 }
68void HorizontalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
70 if (m_visibleItems.isEmpty()) {
71 *modelIndex = 0;
72 *yPos = 0;
73 } else {
74 *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
75 if (m_lastInRowIndexPosition.contains(*modelIndex - 1)) {
76 *yPos = m_visibleItems.last()->y() + m_rowHeight + rowSpacing();
77 } else {
78 *yPos = m_visibleItems.last()->y();
79 }
80 }
83void HorizontalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
85 if (m_visibleItems.isEmpty()) {
86 *modelIndex = -1;
87 *yPos = INT_MIN;
88 } else {
89 *modelIndex = m_firstVisibleIndex - 1;
90 if (m_lastInRowIndexPosition.contains(*modelIndex)) {
91 *yPos = m_visibleItems.first()->y() - rowSpacing() - m_rowHeight;
92 } else {
93 *yPos = m_visibleItems.first()->y();
94 }
95 }
98bool HorizontalJournal::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
100 bool changed = false;
102 while (!m_visibleItems.isEmpty() && m_visibleItems.first()->y() + m_rowHeight < bufferFromY) {
103 releaseItem(m_visibleItems.takeFirst());
104 changed = true;
105 m_firstVisibleIndex++;
106 }
108 while (!m_visibleItems.isEmpty() && m_visibleItems.last()->y() > bufferToY) {
109 releaseItem(m_visibleItems.takeLast());
110 changed = true;
111 m_lastInRowIndexPosition.remove(m_firstVisibleIndex + m_visibleItems.count());
112 }
114 if (m_visibleItems.isEmpty()) {
115 m_firstVisibleIndex = -1;
116 }
118 return changed;
121void HorizontalJournal::addItemToView(int modelIndex, QQuickItem *item)
123 if (item->height() != m_rowHeight) {
124 qWarning() << "Item" << modelIndex << "height is not the one that the rowHeight mandates, resetting it";
125 item->setHeight(m_rowHeight);
126 }
128 if (m_visibleItems.isEmpty()) {
129 Q_ASSERT(modelIndex == 0);
130 item->setY(0);
131 item->setX(0);
132 m_visibleItems << item;
133 m_firstVisibleIndex = 0;
134 } else {
135 // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count()
136 if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
137 QQuickItem *lastItem = m_visibleItems.last();
138 if (lastItem->x() + lastItem->width() + columnSpacing() + item->width() <= width()) {
139 // Fits in the row
140 item->setY(lastItem->y());
141 item->setX(lastItem->x() + lastItem->width() + columnSpacing());
142 } else {
143 // Starts a new row
144 item->setY(lastItem->y() + m_rowHeight + rowSpacing());
145 item->setX(0);
146 m_lastInRowIndexPosition[modelIndex - 1] = lastItem->x();
147 }
148 m_visibleItems << item;
149 } else if (modelIndex == m_firstVisibleIndex - 1) {
150 QQuickItem *firstItem = m_visibleItems.first();
151 if (m_lastInRowIndexPosition.contains(modelIndex)) {
152 // It is the last item of its row, so start a new one since we're going back
153 item->setY(firstItem->y() - rowSpacing() - m_rowHeight);
154 item->setX(m_lastInRowIndexPosition[modelIndex]);
155 } else {
156 item->setY(firstItem->y());
157 item->setX(firstItem->x() - columnSpacing() - item->width());
158 }
159 m_firstVisibleIndex = modelIndex;
160 m_visibleItems.prepend(item);
161 } else {
162 qWarning() << "HorizontalJournal::addItemToView - Got unexpected modelIndex"
163 << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
164 }
165 }
168void HorizontalJournal::cleanupExistingItems()
170 // Cleanup the existing items
171 Q_FOREACH(QQuickItem *item, m_visibleItems)
172 releaseItem(item);
173 m_visibleItems.clear();
174 m_lastInRowIndexPosition.clear();
175 m_firstVisibleIndex = -1;
176 setImplicitHeightDirty();
179void HorizontalJournal::calculateImplicitHeight()
181 if (m_firstVisibleIndex >= 0) {
182 const int nIndexes = m_firstVisibleIndex + m_visibleItems.count();
183 const double bottomMostY = m_visibleItems.last()->y() + m_rowHeight;
184 const double averageHeight = bottomMostY / nIndexes;
185 setImplicitHeight(bottomMostY + averageHeight * (model()->rowCount() - nIndexes));
186 } else {
187 setImplicitHeight(0);
188 }
191#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
192void HorizontalJournal::processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes)
193#else
194void HorizontalJournal::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
195#endif
197#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
198 Q_FOREACH(const QQuickChangeSet::Remove &remove, removes) {
199#else
200 Q_FOREACH(const QQmlChangeSet::Remove &remove, removes) {
201#endif
202 for (int i = remove.count - 1; i >= 0; --i) {
203 const int indexToRemove = remove.index + i;
204 // We only support removing from the end so
205 // any of the last items of a column has to be indexToRemove
206 const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
207 if (indexToRemove == lastIndex) {
208 releaseItem(m_visibleItems.takeLast());
209 m_lastInRowIndexPosition.remove(indexToRemove);
210 } else {
211 if (indexToRemove < lastIndex) {
212 qFatal("HorizontalJournal only supports removal from the end of the model");
213 } else {
214 setImplicitHeightDirty();
215 }
216 }
217 }
218 }
219 if (m_visibleItems.isEmpty()) {
220 m_firstVisibleIndex = -1;
221 }
225void HorizontalJournal::doRelayout()
227 // If m_firstVisibleIndex is not 0 we need to drop all the delegates
228 // since we can't consistently relayout without the first item being there
230 if (m_firstVisibleIndex == 0) {
231 int i = 0;
232 const QList<QQuickItem*> allItems = m_visibleItems;
233 m_visibleItems.clear();
234 m_lastInRowIndexPosition.clear();
235 Q_FOREACH(QQuickItem *item, allItems) {
236 addItemToView(i, item);
237 ++i;
238 }
239 } else {
240 Q_FOREACH(QQuickItem *item, m_visibleItems) {
241 releaseItem(item);
242 }
243 m_visibleItems.clear();
244 m_lastInRowIndexPosition.clear();
245 m_firstVisibleIndex = -1;
246 }
249void HorizontalJournal::updateItemCulling(qreal visibleFromY, qreal visibleToY)
251 Q_FOREACH(QQuickItem *item, m_visibleItems) {
252 QQuickItemPrivate::get(item)->setCulled(item->y() + m_rowHeight <= visibleFromY || item->y() >= visibleToY);
253 }