on ca361bf help sign in
unity8/plugins/Dash/horizontaljournal.cpp
239 lines 8.2 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#include <qqmlinfo.h>
30#include <private/qqmldelegatemodel_p.h>
31#include <private/qquickitem_p.h>
33HorizontalJournal::HorizontalJournal()
34 : m_firstVisibleIndex(-1)
35 , m_rowHeight(0)
39qreal HorizontalJournal::rowHeight() const
41 return m_rowHeight;
44void HorizontalJournal::setRowHeight(qreal rowHeight)
46 if (rowHeight != m_rowHeight) {
47 m_rowHeight = rowHeight;
48 Q_EMIT rowHeightChanged();
50 if (isComponentComplete()) {
51 Q_FOREACH(QQuickItem *item, m_visibleItems) {
52 item->setHeight(rowHeight);
53 }
54 relayout();
55 }
56 }
59void HorizontalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
61 if (m_visibleItems.isEmpty()) {
62 *modelIndex = 0;
63 *yPos = 0;
64 } else {
65 *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
66 if (m_lastInRowIndexPosition.contains(*modelIndex - 1)) {
67 *yPos = m_visibleItems.last()->y() + m_rowHeight + rowSpacing();
68 } else {
69 *yPos = m_visibleItems.last()->y();
70 }
71 }
74void HorizontalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
76 if (m_visibleItems.isEmpty()) {
77 *modelIndex = -1;
78 *yPos = INT_MIN;
79 } else {
80 *modelIndex = m_firstVisibleIndex - 1;
81 if (m_lastInRowIndexPosition.contains(*modelIndex)) {
82 *yPos = m_visibleItems.first()->y() - rowSpacing() - m_rowHeight;
83 } else {
84 *yPos = m_visibleItems.first()->y();
85 }
86 }
89bool HorizontalJournal::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
91 bool changed = false;
93 while (!m_visibleItems.isEmpty() && m_visibleItems.first()->y() + m_rowHeight < bufferFromY) {
94 releaseItem(m_visibleItems.takeFirst());
95 changed = true;
96 m_firstVisibleIndex++;
97 }
99 while (!m_visibleItems.isEmpty() && m_visibleItems.last()->y() > bufferToY) {
100 releaseItem(m_visibleItems.takeLast());
101 changed = true;
102 m_lastInRowIndexPosition.remove(m_firstVisibleIndex + m_visibleItems.count());
103 }
105 if (m_visibleItems.isEmpty()) {
106 m_firstVisibleIndex = -1;
107 }
109 return changed;
112void HorizontalJournal::addItemToView(int modelIndex, QQuickItem *item)
114 if (item->height() != m_rowHeight) {
115 qWarning() << "Item" << modelIndex << "height is not the one that the rowHeight mandates, resetting it";
116 item->setHeight(m_rowHeight);
117 }
119 if (m_visibleItems.isEmpty()) {
120 Q_ASSERT(modelIndex == 0);
121 item->setY(0);
122 item->setX(0);
123 m_visibleItems << item;
124 m_firstVisibleIndex = 0;
125 } else {
126 // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count()
127 if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
128 QQuickItem *lastItem = m_visibleItems.last();
129 if (lastItem->x() + lastItem->width() + columnSpacing() + item->width() <= width()) {
130 // Fits in the row
131 item->setY(lastItem->y());
132 item->setX(lastItem->x() + lastItem->width() + columnSpacing());
133 } else {
134 // Starts a new row
135 item->setY(lastItem->y() + m_rowHeight + rowSpacing());
136 item->setX(0);
137 m_lastInRowIndexPosition[modelIndex - 1] = lastItem->x();
138 }
139 m_visibleItems << item;
140 } else if (modelIndex == m_firstVisibleIndex - 1) {
141 QQuickItem *firstItem = m_visibleItems.first();
142 if (m_lastInRowIndexPosition.contains(modelIndex)) {
143 // It is the last item of its row, so start a new one since we're going back
144 item->setY(firstItem->y() - rowSpacing() - m_rowHeight);
145 item->setX(m_lastInRowIndexPosition[modelIndex]);
146 } else {
147 item->setY(firstItem->y());
148 item->setX(firstItem->x() - columnSpacing() - item->width());
149 }
150 m_firstVisibleIndex = modelIndex;
151 m_visibleItems.prepend(item);
152 } else {
153 qWarning() << "HorizontalJournal::addItemToView - Got unexpected modelIndex"
154 << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
155 }
156 }
159void HorizontalJournal::cleanupExistingItems()
161 // Cleanup the existing items
162 Q_FOREACH(QQuickItem *item, m_visibleItems)
163 releaseItem(item);
164 m_visibleItems.clear();
165 m_lastInRowIndexPosition.clear();
166 m_firstVisibleIndex = -1;
167 setImplicitHeightDirty();
170void HorizontalJournal::calculateImplicitHeight()
172 if (m_firstVisibleIndex >= 0) {
173 const int nIndexes = m_firstVisibleIndex + m_visibleItems.count();
174 const double bottomMostY = m_visibleItems.last()->y() + m_rowHeight;
175 const double averageHeight = bottomMostY / nIndexes;
176 setImplicitHeight(bottomMostY + averageHeight * (model()->rowCount() - nIndexes));
177 } else {
178 setImplicitHeight(0);
179 }
182void HorizontalJournal::processModelRemoves(const QVector<QQmlChangeSet::Change> &removes)
184 Q_FOREACH(const QQmlChangeSet::Change remove, removes) {
185 for (int i = remove.count - 1; i >= 0; --i) {
186 const int indexToRemove = remove.index + i;
187 // We only support removing from the end so
188 // any of the last items of a column has to be indexToRemove
189 const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
190 if (indexToRemove == lastIndex) {
191 releaseItem(m_visibleItems.takeLast());
192 m_lastInRowIndexPosition.remove(indexToRemove);
193 } else {
194 if (indexToRemove < lastIndex) {
195 qDebug() << "HorizontalJournal only supports removal from the end of the model, resetting instead";
196 cleanupExistingItems();
197 break;
198 } else {
199 setImplicitHeightDirty();
200 }
201 }
202 }
203 }
204 if (m_visibleItems.isEmpty()) {
205 m_firstVisibleIndex = -1;
206 }
210void HorizontalJournal::doRelayout()
212 // If m_firstVisibleIndex is not 0 we need to drop all the delegates
213 // since we can't consistently relayout without the first item being there
215 if (m_firstVisibleIndex == 0) {
216 int i = 0;
217 const QList<QQuickItem*> allItems = m_visibleItems;
218 m_visibleItems.clear();
219 m_lastInRowIndexPosition.clear();
220 Q_FOREACH(QQuickItem *item, allItems) {
221 addItemToView(i, item);
222 ++i;
223 }
224 } else {
225 Q_FOREACH(QQuickItem *item, m_visibleItems) {
226 releaseItem(item);
227 }
228 m_visibleItems.clear();
229 m_lastInRowIndexPosition.clear();
230 m_firstVisibleIndex = -1;
231 }
234void HorizontalJournal::updateItemCulling(qreal visibleFromY, qreal visibleToY)
236 Q_FOREACH(QQuickItem *item, m_visibleItems) {
237 QQuickItemPrivate::get(item)->setCulled(item->y() + m_rowHeight <= visibleFromY || item->y() >= visibleToY);
238 }