on e804c19 help sign in
unity8/plugins/Dash/abstractdashview.cpp
346 lines 9.0 kB history raw
1/*
2 * Copyright (C) 2013, 2014 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 "abstractdashview.h"
19AbstractDashView::AbstractDashView()
20 : m_delegateModel(nullptr)
21 , m_asyncRequestedIndex(-1)
22 , m_columnSpacing(0)
23 , m_rowSpacing(0)
24 , m_buffer(320) // Same value used in qquickitemview.cpp in Qt 5.4
25 , m_displayMarginBeginning(0)
26 , m_displayMarginEnd(0)
27 , m_needsRelayout(false)
28 , m_delegateValidated(false)
29 , m_implicitHeightDirty(false)
31 connect(this, &AbstractDashView::widthChanged, this, &AbstractDashView::relayout);
32 connect(this, &AbstractDashView::heightChanged, this, &AbstractDashView::onHeightChanged);
35QAbstractItemModel *AbstractDashView::model() const
37 return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
40void AbstractDashView::setModel(QAbstractItemModel *model)
42 if (model != this->model()) {
43 if (!m_delegateModel) {
44 createDelegateModel();
45 } else {
46 disconnect(m_delegateModel, &QQmlDelegateModel::modelUpdated, this, &AbstractDashView::onModelUpdated);
47 }
48 m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
49 connect(m_delegateModel, &QQmlDelegateModel::modelUpdated, this, &AbstractDashView::onModelUpdated);
51 cleanupExistingItems();
53 Q_EMIT modelChanged();
54 polish();
55 }
58QQmlComponent *AbstractDashView::delegate() const
60 return m_delegateModel ? m_delegateModel->delegate() : nullptr;
63void AbstractDashView::setDelegate(QQmlComponent *delegate)
65 if (delegate != this->delegate()) {
66 if (!m_delegateModel) {
67 createDelegateModel();
68 }
70 cleanupExistingItems();
72 m_delegateModel->setDelegate(delegate);
74 Q_EMIT delegateChanged();
75 m_delegateValidated = false;
76 polish();
77 }
80qreal AbstractDashView::columnSpacing() const
82 return m_columnSpacing;
85void AbstractDashView::setColumnSpacing(qreal columnSpacing)
87 if (columnSpacing != m_columnSpacing) {
88 m_columnSpacing = columnSpacing;
89 Q_EMIT columnSpacingChanged();
91 if (isComponentComplete()) {
92 relayout();
93 }
94 }
97qreal AbstractDashView::rowSpacing() const
99 return m_rowSpacing;
102void AbstractDashView::setRowSpacing(qreal rowSpacing)
104 if (rowSpacing != m_rowSpacing) {
105 m_rowSpacing = rowSpacing;
106 Q_EMIT rowSpacingChanged();
108 if (isComponentComplete()) {
109 relayout();
110 }
111 }
114int AbstractDashView::cacheBuffer() const
116 return m_buffer;
119void AbstractDashView::setCacheBuffer(int buffer)
121 if (buffer < 0) {
122 qmlInfo(this) << "Cannot set a negative cache buffer";
123 return;
124 }
126 if (m_buffer != buffer) {
127 m_buffer = buffer;
128 if (isComponentComplete()) {
129 polish();
130 }
131 emit cacheBufferChanged();
132 }
135qreal AbstractDashView::displayMarginBeginning() const
137 return m_displayMarginBeginning;
140void AbstractDashView::setDisplayMarginBeginning(qreal begin)
142 if (m_displayMarginBeginning == begin)
143 return;
144 m_displayMarginBeginning = begin;
145 if (isComponentComplete()) {
146 polish();
147 }
148 emit displayMarginBeginningChanged();
151qreal AbstractDashView::displayMarginEnd() const
153 return m_displayMarginEnd;
156void AbstractDashView::setDisplayMarginEnd(qreal end)
158 if (m_displayMarginEnd == end)
159 return;
160 m_displayMarginEnd = end;
161 if (isComponentComplete()) {
162 polish();
163 }
164 emit displayMarginEndChanged();
167void AbstractDashView::createDelegateModel()
169 m_delegateModel = new QQmlDelegateModel(qmlContext(this), this);
170 connect(m_delegateModel, &QQmlDelegateModel::createdItem, this, &AbstractDashView::itemCreated);
171 if (isComponentComplete())
172 m_delegateModel->componentComplete();
175void AbstractDashView::refill()
177 if (!isComponentComplete() || height() < 0) {
178 return;
179 }
181 const qreal from = -m_displayMarginBeginning;
182 const qreal to = height() + m_displayMarginEnd;
183 const qreal bufferFrom = from - m_buffer;
184 const qreal bufferTo = to + m_buffer;
186 bool added = addVisibleItems(from, to, false);
187 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
188 added |= addVisibleItems(bufferFrom, bufferTo, true);
190 if (added || removed) {
191 m_implicitHeightDirty = true;
192 polish();
193 }
196bool AbstractDashView::addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous)
198 if (fillToY <= fillFromY)
199 return false;
201 if (!delegate())
202 return false;
204 if (m_delegateModel->count() == 0)
205 return false;
207 int modelIndex;
208 qreal yPos;
209 findBottomModelIndexToAdd(&modelIndex, &yPos);
210 bool changed = false;
211 while (modelIndex < m_delegateModel->count() && yPos <= fillToY) {
212 if (!createItem(modelIndex, asynchronous))
213 break;
215 changed = true;
216 findBottomModelIndexToAdd(&modelIndex, &yPos);
217 }
219 findTopModelIndexToAdd(&modelIndex, &yPos);
220 while (modelIndex >= 0 && yPos > fillFromY) {
221 if (!createItem(modelIndex, asynchronous))
222 break;
224 changed = true;
225 findTopModelIndexToAdd(&modelIndex, &yPos);
226 }
228 return changed;
231QQuickItem *AbstractDashView::createItem(int modelIndex, bool asynchronous)
233 if (asynchronous && m_asyncRequestedIndex != -1)
234 return nullptr;
236 m_asyncRequestedIndex = -1;
237 QObject* object = m_delegateModel->object(modelIndex, asynchronous);
238 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
239 if (!item) {
240 if (object) {
241 m_delegateModel->release(object);
242 if (!m_delegateValidated) {
243 m_delegateValidated = true;
244 QObject* delegateObj = delegate();
245 qmlInfo(delegateObj ? delegateObj : this) << "Delegate must be of Item type";
246 }
247 } else {
248 m_asyncRequestedIndex = modelIndex;
249 }
250 return nullptr;
251 } else {
252 addItemToView(modelIndex, item);
253 return item;
254 }
257void AbstractDashView::releaseItem(QQuickItem *item)
259 QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item);
260 if (flags & QQmlDelegateModel::Destroyed) {
261 item->setParentItem(nullptr);
262 }
265void AbstractDashView::setImplicitHeightDirty()
267 m_implicitHeightDirty = true;
270void AbstractDashView::itemCreated(int modelIndex, QObject *object)
272 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
273 if (!item) {
274 qWarning() << "AbstractDashView::itemCreated got a non item for index" << modelIndex;
275 return;
276 }
277 item->setParentItem(this);
279 // We only need to call createItem if we are here because of an asynchronous generation
280 // otherwise we are in this slot because createItem is creating the item sync
281 // and thus we don't need to call createItem again, nor need to set m_implicitHeightDirty
282 // and call polish because the sync createItem was called from addVisibleItems that
283 // is called from refill that will already do those if an item was added
284 if (modelIndex == m_asyncRequestedIndex) {
285 createItem(modelIndex, false);
286 m_implicitHeightDirty = true;
287 polish();
288 }
291void AbstractDashView::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
293 if (reset) {
294 cleanupExistingItems();
295 } else {
296 processModelRemoves(changeSet.removes());
297 }
298 polish();
302void AbstractDashView::relayout()
304 m_needsRelayout = true;
305 polish();
308void AbstractDashView::onHeightChanged()
310 polish();
313void AbstractDashView::updatePolish()
315 if (!model())
316 return;
318 if (m_needsRelayout) {
319 doRelayout();
320 m_needsRelayout = false;
321 m_implicitHeightDirty = true;
322 }
324 refill();
326 const qreal from = -m_displayMarginBeginning;
327 const qreal to = height() + m_displayMarginEnd;
328 updateItemCulling(from, to);
330 if (m_implicitHeightDirty) {
331 calculateImplicitHeight();
332 m_implicitHeightDirty = false;
333 }
336void AbstractDashView::componentComplete()
338 if (m_delegateModel)
339 m_delegateModel->componentComplete();
341 QQuickItem::componentComplete();
343 m_needsRelayout = true;
345 polish();