on 1663e5c help sign in
unity8/plugins/DashViews/abstractjournal.cpp
388 lines 11 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#include "abstractjournal.h"
19static const qreal bufferRatio = 0.5;
21AbstractJournal::AbstractJournal()
22 : m_delegateModel(nullptr)
23 , m_asyncRequestedIndex(-1)
24 , m_columnSpacing(0)
25 , m_rowSpacing(0)
26 , m_delegateCreationBegin(0)
27 , m_delegateCreationEnd(0)
28 , m_delegateCreationBeginValid(false)
29 , m_delegateCreationEndValid(false)
30 , m_needsRelayout(false)
31 , m_delegateValidated(false)
32 , m_implicitHeightDirty(false)
34 connect(this, SIGNAL(widthChanged()), this, SLOT(relayout()));
35 connect(this, SIGNAL(heightChanged()), this, SLOT(onHeightChanged()));
38QAbstractItemModel *AbstractJournal::model() const
40 return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
43void AbstractJournal::setModel(QAbstractItemModel *model)
45 if (model != this->model()) {
46 if (!m_delegateModel) {
47 createDelegateModel();
48 } else {
49#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
50 disconnect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
51 }
52 m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
53 connect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
54#else
55 disconnect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
56 }
57 m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
58 connect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
59#endif
61 cleanupExistingItems();
63 Q_EMIT modelChanged();
64 polish();
65 }
68QQmlComponent *AbstractJournal::delegate() const
70 return m_delegateModel ? m_delegateModel->delegate() : nullptr;
73void AbstractJournal::setDelegate(QQmlComponent *delegate)
75 if (delegate != this->delegate()) {
76 if (!m_delegateModel) {
77 createDelegateModel();
78 }
80 cleanupExistingItems();
82 m_delegateModel->setDelegate(delegate);
84 Q_EMIT delegateChanged();
85 m_delegateValidated = false;
86 polish();
87 }
90qreal AbstractJournal::columnSpacing() const
92 return m_columnSpacing;
95void AbstractJournal::setColumnSpacing(qreal columnSpacing)
97 if (columnSpacing != m_columnSpacing) {
98 m_columnSpacing = columnSpacing;
99 Q_EMIT columnSpacingChanged();
101 if (isComponentComplete()) {
102 relayout();
103 }
104 }
107qreal AbstractJournal::rowSpacing() const
109 return m_rowSpacing;
112void AbstractJournal::setRowSpacing(qreal rowSpacing)
114 if (rowSpacing != m_rowSpacing) {
115 m_rowSpacing = rowSpacing;
116 Q_EMIT rowSpacingChanged();
118 if (isComponentComplete()) {
119 relayout();
120 }
121 }
124qreal AbstractJournal::delegateCreationBegin() const
126 return m_delegateCreationBegin;
129void AbstractJournal::setDelegateCreationBegin(qreal begin)
131 m_delegateCreationBeginValid = true;
132 if (m_delegateCreationBegin == begin)
133 return;
134 m_delegateCreationBegin = begin;
135 if (isComponentComplete()) {
136 polish();
137 }
138 emit delegateCreationBeginChanged();
141void AbstractJournal::resetDelegateCreationBegin()
143 m_delegateCreationBeginValid = false;
144 if (m_delegateCreationBegin == 0)
145 return;
146 m_delegateCreationBegin = 0;
147 if (isComponentComplete()) {
148 polish();
149 }
150 emit delegateCreationBeginChanged();
153qreal AbstractJournal::delegateCreationEnd() const
155 return m_delegateCreationEnd;
158void AbstractJournal::setDelegateCreationEnd(qreal end)
160 m_delegateCreationEndValid = true;
161 if (m_delegateCreationEnd == end)
162 return;
163 m_delegateCreationEnd = end;
164 if (isComponentComplete()) {
165 polish();
166 }
167 emit delegateCreationEndChanged();
170void AbstractJournal::resetDelegateCreationEnd()
172 m_delegateCreationEndValid = false;
173 if (m_delegateCreationEnd == 0)
174 return;
175 m_delegateCreationEnd = 0;
176 if (isComponentComplete()) {
177 polish();
178 }
179 emit delegateCreationEndChanged();
182void AbstractJournal::createDelegateModel()
184#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
185 m_delegateModel = new QQuickVisualDataModel(qmlContext(this), this);
186 connect(m_delegateModel, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(itemCreated(int,QQuickItem*)));
187#else
188 m_delegateModel = new QQmlDelegateModel(qmlContext(this), this);
189 connect(m_delegateModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(itemCreated(int,QObject*)));
190#endif
191 if (isComponentComplete())
192 m_delegateModel->componentComplete();
195void AbstractJournal::refill()
197 if (!isComponentComplete()) {
198 return;
199 }
201 const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
202 const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
203 const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
204 const qreal buffer = (to - from) * bufferRatio;
205 const qreal bufferFrom = from - buffer;
206 const qreal bufferTo = to + buffer;
208 bool added = addVisibleItems(from, to, false);
209 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
210 added |= addVisibleItems(bufferFrom, bufferTo, true);
212 if (added || removed) {
213 m_implicitHeightDirty = true;
214 polish();
215 }
218bool AbstractJournal::addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous)
220 if (!delegate())
221 return false;
223 if (m_delegateModel->count() == 0)
224 return false;
226 int modelIndex;
227 qreal yPos;
228 findBottomModelIndexToAdd(&modelIndex, &yPos);
229 bool changed = false;
230 while (modelIndex < m_delegateModel->count() && yPos <= fillToY) {
231 if (!createItem(modelIndex, asynchronous))
232 break;
234 changed = true;
235 findBottomModelIndexToAdd(&modelIndex, &yPos);
236 }
238 findTopModelIndexToAdd(&modelIndex, &yPos);
239 while (modelIndex >= 0 && yPos > fillFromY) {
240 if (!createItem(modelIndex, asynchronous))
241 break;
243 changed = true;
244 findTopModelIndexToAdd(&modelIndex, &yPos);
245 }
247 return changed;
250QQuickItem *AbstractJournal::createItem(int modelIndex, bool asynchronous)
252 if (asynchronous && m_asyncRequestedIndex != -1)
253 return nullptr;
255 m_asyncRequestedIndex = -1;
256#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
257 QQuickItem *item = m_delegateModel->item(modelIndex, asynchronous);
258#else
259 QObject* object = m_delegateModel->object(modelIndex, asynchronous);
260 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
261#endif
262 if (!item) {
263#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
264 m_asyncRequestedIndex = modelIndex;
265#else
266 if (object) {
267 m_delegateModel->release(object);
268 if (!m_delegateValidated) {
269 m_delegateValidated = true;
270 QObject* delegateObj = delegate();
271 qmlInfo(delegateObj ? delegateObj : this) << "Delegate must be of Item type";
272 }
273 } else {
274 m_asyncRequestedIndex = modelIndex;
275 }
276#endif
277 return nullptr;
278 } else {
279 positionItem(modelIndex, item);
280 return item;
281 }
284void AbstractJournal::releaseItem(QQuickItem *item)
286#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
287 QQuickVisualModel::ReleaseFlags flags = m_delegateModel->release(item);
288 if (flags & QQuickVisualModel::Destroyed) {
289#else
290 QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item);
291 if (flags & QQmlDelegateModel::Destroyed) {
292#endif
293 item->setParentItem(nullptr);
294 }
297void AbstractJournal::setImplicitHeightDirty()
299 m_implicitHeightDirty = true;
302#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
303void AbstractJournal::itemCreated(int modelIndex, QQuickItem *item)
305#else
306void AbstractJournal::itemCreated(int modelIndex, QObject *object)
308 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
309 if (!item) {
310 qWarning() << "AbstractJournal::itemCreated got a non item for index" << modelIndex;
311 return;
312 }
313#endif
314 item->setParentItem(this);
316 // We only need to call createItem if we are here because of an asynchronous generation
317 // otherwise we are in this slot because createItem is creating the item sync
318 // and thus we don't need to call createItem again, nor need to set m_implicitHeightDirty
319 // and call polish because the sync createItem was called from addVisibleItems that
320 // is called from refill that will already do those if an item was added
321 if (modelIndex == m_asyncRequestedIndex) {
322 createItem(modelIndex, false);
323 m_implicitHeightDirty = true;
324 polish();
325 }
328#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
329void AbstractJournal::onModelUpdated(const QQuickChangeSet &changeSet, bool reset)
330#else
331void AbstractJournal::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
332#endif
334 if (reset) {
335 cleanupExistingItems();
336 } else {
337 processModelRemoves(changeSet.removes());
338 }
339 polish();
343void AbstractJournal::relayout()
345 m_needsRelayout = true;
346 polish();
349void AbstractJournal::onHeightChanged()
351 polish();
354void AbstractJournal::updatePolish()
356 if (!model())
357 return;
359 if (m_needsRelayout) {
360 doRelayout();
361 m_needsRelayout = false;
362 m_implicitHeightDirty = true;
363 }
365 refill();
367 const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
368 const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
369 const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
370 updateItemCulling(from, to);
372 if (m_implicitHeightDirty) {
373 calculateImplicitHeight();
374 m_implicitHeightDirty = false;
375 }
378void AbstractJournal::componentComplete()
380 if (m_delegateModel)
381 m_delegateModel->componentComplete();
383 QQuickItem::componentComplete();
385 m_needsRelayout = true;
387 polish();