on bcacecf help sign in
unity8/plugins/Dash/abstractdashview.cpp
327 lines 8.6 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"
19static const qreal bufferRatio = 0.5;
21AbstractDashView::AbstractDashView()
22 : m_delegateModel(nullptr)
23 , m_asyncRequestedIndex(-1)
24 , m_columnSpacing(0)
25 , m_rowSpacing(0)
26 , m_displayMarginBeginning(0)
27 , m_displayMarginEnd(0)
28 , m_needsRelayout(false)
29 , m_delegateValidated(false)
30 , m_implicitHeightDirty(false)
32 connect(this, SIGNAL(widthChanged()), this, SLOT(relayout()));
33 connect(this, SIGNAL(heightChanged()), this, SLOT(onHeightChanged()));
36QAbstractItemModel *AbstractDashView::model() const
38 return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
41void AbstractDashView::setModel(QAbstractItemModel *model)
43 if (model != this->model()) {
44 if (!m_delegateModel) {
45 createDelegateModel();
46 } else {
47 disconnect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
48 }
49 m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
50 connect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
52 cleanupExistingItems();
54 Q_EMIT modelChanged();
55 polish();
56 }
59QQmlComponent *AbstractDashView::delegate() const
61 return m_delegateModel ? m_delegateModel->delegate() : nullptr;
64void AbstractDashView::setDelegate(QQmlComponent *delegate)
66 if (delegate != this->delegate()) {
67 if (!m_delegateModel) {
68 createDelegateModel();
69 }
71 cleanupExistingItems();
73 m_delegateModel->setDelegate(delegate);
75 Q_EMIT delegateChanged();
76 m_delegateValidated = false;
77 polish();
78 }
81qreal AbstractDashView::columnSpacing() const
83 return m_columnSpacing;
86void AbstractDashView::setColumnSpacing(qreal columnSpacing)
88 if (columnSpacing != m_columnSpacing) {
89 m_columnSpacing = columnSpacing;
90 Q_EMIT columnSpacingChanged();
92 if (isComponentComplete()) {
93 relayout();
94 }
95 }
98qreal AbstractDashView::rowSpacing() const
100 return m_rowSpacing;
103void AbstractDashView::setRowSpacing(qreal rowSpacing)
105 if (rowSpacing != m_rowSpacing) {
106 m_rowSpacing = rowSpacing;
107 Q_EMIT rowSpacingChanged();
109 if (isComponentComplete()) {
110 relayout();
111 }
112 }
115qreal AbstractDashView::displayMarginBeginning() const
117 return m_displayMarginBeginning;
120void AbstractDashView::setDisplayMarginBeginning(qreal begin)
122 if (m_displayMarginBeginning == begin)
123 return;
124 m_displayMarginBeginning = begin;
125 if (isComponentComplete()) {
126 polish();
127 }
128 emit displayMarginBeginningChanged();
131qreal AbstractDashView::displayMarginEnd() const
133 return m_displayMarginEnd;
136void AbstractDashView::setDisplayMarginEnd(qreal end)
138 if (m_displayMarginEnd == end)
139 return;
140 m_displayMarginEnd = end;
141 if (isComponentComplete()) {
142 polish();
143 }
144 emit displayMarginEndChanged();
147void AbstractDashView::createDelegateModel()
149 m_delegateModel = new QQmlDelegateModel(qmlContext(this), this);
150 connect(m_delegateModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(itemCreated(int,QObject*)));
151 if (isComponentComplete())
152 m_delegateModel->componentComplete();
155void AbstractDashView::refill()
157 if (!isComponentComplete() || height() < 0) {
158 return;
159 }
161 const qreal from = -m_displayMarginBeginning;
162 const qreal to = height() + m_displayMarginEnd;
163 const qreal buffer = (to - from) * bufferRatio;
164 const qreal bufferFrom = from - buffer;
165 const qreal bufferTo = to + buffer;
167 bool added = addVisibleItems(from, to, false);
168 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
169 added |= addVisibleItems(bufferFrom, bufferTo, true);
171 if (added || removed) {
172 m_implicitHeightDirty = true;
173 polish();
174 }
177bool AbstractDashView::addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous)
179 if (fillToY <= fillFromY)
180 return false;
182 if (!delegate())
183 return false;
185 if (m_delegateModel->count() == 0)
186 return false;
188 int modelIndex;
189 qreal yPos;
190 findBottomModelIndexToAdd(&modelIndex, &yPos);
191 bool changed = false;
192 while (modelIndex < m_delegateModel->count() && yPos <= fillToY) {
193 if (!createItem(modelIndex, asynchronous))
194 break;
196 changed = true;
197 findBottomModelIndexToAdd(&modelIndex, &yPos);
198 }
200 findTopModelIndexToAdd(&modelIndex, &yPos);
201 while (modelIndex >= 0 && yPos > fillFromY) {
202 if (!createItem(modelIndex, asynchronous))
203 break;
205 changed = true;
206 findTopModelIndexToAdd(&modelIndex, &yPos);
207 }
209 return changed;
212QQuickItem *AbstractDashView::createItem(int modelIndex, bool asynchronous)
214 if (asynchronous && m_asyncRequestedIndex != -1)
215 return nullptr;
217 m_asyncRequestedIndex = -1;
218 QObject* object = m_delegateModel->object(modelIndex, asynchronous);
219 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
220 if (!item) {
221 if (object) {
222 m_delegateModel->release(object);
223 if (!m_delegateValidated) {
224 m_delegateValidated = true;
225 QObject* delegateObj = delegate();
226 qmlInfo(delegateObj ? delegateObj : this) << "Delegate must be of Item type";
227 }
228 } else {
229 m_asyncRequestedIndex = modelIndex;
230 }
231 return nullptr;
232 } else {
233 addItemToView(modelIndex, item);
234 return item;
235 }
238void AbstractDashView::releaseItem(QQuickItem *item)
240 QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item);
241 if (flags & QQmlDelegateModel::Destroyed) {
242 item->setParentItem(nullptr);
243 }
246void AbstractDashView::setImplicitHeightDirty()
248 m_implicitHeightDirty = true;
251void AbstractDashView::itemCreated(int modelIndex, QObject *object)
253 QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
254 if (!item) {
255 qWarning() << "AbstractDashView::itemCreated got a non item for index" << modelIndex;
256 return;
257 }
258 item->setParentItem(this);
260 // We only need to call createItem if we are here because of an asynchronous generation
261 // otherwise we are in this slot because createItem is creating the item sync
262 // and thus we don't need to call createItem again, nor need to set m_implicitHeightDirty
263 // and call polish because the sync createItem was called from addVisibleItems that
264 // is called from refill that will already do those if an item was added
265 if (modelIndex == m_asyncRequestedIndex) {
266 createItem(modelIndex, false);
267 m_implicitHeightDirty = true;
268 polish();
269 }
272void AbstractDashView::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
274 if (reset) {
275 cleanupExistingItems();
276 } else {
277 processModelRemoves(changeSet.removes());
278 }
279 polish();
283void AbstractDashView::relayout()
285 m_needsRelayout = true;
286 polish();
289void AbstractDashView::onHeightChanged()
291 polish();
294void AbstractDashView::updatePolish()
296 if (!model())
297 return;
299 if (m_needsRelayout) {
300 doRelayout();
301 m_needsRelayout = false;
302 m_implicitHeightDirty = true;
303 }
305 refill();
307 const qreal from = -m_displayMarginBeginning;
308 const qreal to = height() + m_displayMarginEnd;
309 updateItemCulling(from, to);
311 if (m_implicitHeightDirty) {
312 calculateImplicitHeight();
313 m_implicitHeightDirty = false;
314 }
317void AbstractDashView::componentComplete()
319 if (m_delegateModel)
320 m_delegateModel->componentComplete();
322 QQuickItem::componentComplete();
324 m_needsRelayout = true;
326 polish();