Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquicktumblerview.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtCore/qloggingcategory.h>
7#include <QtQuick/private/qquickitem_p.h>
8#include <QtQuick/private/qquicklistview_p.h>
9#include <QtQuick/private/qquickpathview_p.h>
10
11#include <QtQuickTemplates2/private/qquicktumbler_p.h>
12#include <QtQuickTemplates2/private/qquicktumbler_p_p.h>
13
15
16Q_LOGGING_CATEGORY(lcTumblerView, "qt.quick.controls.tumblerview")
17
19 QQuickItem(parent)
20{
21 // We don't call createView() here because we don't know what the wrap flag is set to
22 // yet, and we don't want to create a view that might never get used.
23}
24
26{
27 return m_model;
28}
29
31{
32 qCDebug(lcTumblerView) << "setting model to:" << model << "on"
33 << (m_pathView ? static_cast<QObject*>(m_pathView) : static_cast<QObject*>(m_listView));
34 if (model == m_model)
35 return;
36
37 m_model = model;
38
39 if (m_pathView) {
40 m_pathView->setModel(m_model);
41 } else if (m_listView) {
42 // QQuickItemView::setModel() resets the current index,
43 // but if we're still creating the Tumbler, it should be maintained.
44 const int oldCurrentIndex = m_listView->currentIndex();
45 m_listView->setModel(m_model);
47 m_listView->setCurrentIndex(oldCurrentIndex);
48 }
49
51}
52
54{
55 return m_delegate;
56}
57
59{
60 qCDebug(lcTumblerView) << "setting delegate to:" << delegate << "on"
61 << (m_pathView ? static_cast<QObject*>(m_pathView) : static_cast<QObject*>(m_listView));
62 if (delegate == m_delegate)
63 return;
64
65 m_delegate = delegate;
66
67 if (m_pathView)
68 m_pathView->setDelegate(m_delegate);
69 else if (m_listView)
70 m_listView->setDelegate(m_delegate);
71
73}
74
76{
77 return m_path;
78}
79
81{
82 if (path == m_path)
83 return;
84
85 m_path = path;
87}
88
89void QQuickTumblerView::createView()
90{
91 Q_ASSERT(m_tumbler);
92
93 // We create a view regardless of whether or not we know
94 // the count yet, because we rely on the view to tell us the count.
95 if (m_tumbler->wrap()) {
96 if (m_listView) {
97 // It's necessary to call deleteLater() rather than delete,
98 // as this code is most likely being run in rensponse to a signal
99 // emission somewhere in the list view's internals, so we need to
100 // wait until that has finished.
101 m_listView->deleteLater();
102 QQml_setParent_noEvent(m_listView, nullptr);
103 // The auto tests pass with unparenting the list view alone, but
104 // just to be sure, we unset some other things as well.
105 m_listView->setParentItem(nullptr);
106 m_listView->setVisible(false);
107 m_listView->setModel(QVariant());
108 m_listView = nullptr;
109 }
110
111 if (!m_pathView) {
112 qCDebug(lcTumblerView) << "creating PathView";
113
114 m_pathView = new QQuickPathView;
116 QQml_setParent_noEvent(m_pathView, this);
117 m_pathView->setParentItem(this);
118 m_pathView->setPath(m_path);
119 m_pathView->setDelegate(m_delegate);
120 m_pathView->setPreferredHighlightBegin(0.5);
121 m_pathView->setPreferredHighlightEnd(0.5);
122 m_pathView->setHighlightMoveDuration(1000);
123 m_pathView->setClip(true);
124
125 // Give the view a size.
126 updateView();
127 // Set the model.
128 updateModel();
129
130 qCDebug(lcTumblerView) << "finished creating PathView";
131 }
132 } else {
133 if (m_pathView) {
134 m_pathView->deleteLater();
135 QQml_setParent_noEvent(m_pathView, nullptr);
136 m_pathView->setParentItem(nullptr);
137 m_pathView->setVisible(false);
138 m_pathView->setModel(QVariant());
139 m_pathView = nullptr;
140 }
141
142 if (!m_listView) {
143 qCDebug(lcTumblerView) << "creating ListView";
144
145 m_listView = new QQuickListView;
147 QQml_setParent_noEvent(m_listView, this);
148 m_listView->setParentItem(this);
150 m_listView->setClip(true);
151
152 // Give the view a size.
153 updateView();
154 // Set the model.
155 updateModel();
156
157 // Set these after the model is set so that the currentItem animation
158 // happens instantly on startup/after switching models. If we set them too early,
159 // the view animates any potential currentIndex change over one second,
160 // which we don't want when the contentItem has just been created.
161 m_listView->setDelegate(m_delegate);
162 // Set this after setting the delegate to avoid unexpected currentIndex changes: QTBUG-79150
164 m_listView->setHighlightMoveDuration(1000);
165
166 qCDebug(lcTumblerView) << "finished creating ListView";
167 }
168 }
169}
170
171// Called whenever the size or visibleItemCount changes.
172void QQuickTumblerView::updateView()
173{
174 QQuickItem *theView = view();
175 if (!theView)
176 return;
177
178 theView->setSize(QSizeF(width(), height()));
179
180 // Can be called in geometryChange when it might not have a parent item yet.
181 if (!m_tumbler)
182 return;
183
184 // Set view-specific properties that have a dependency on the size, etc.
185 if (m_pathView) {
186 m_pathView->setPathItemCount(m_tumbler->visibleItemCount() + 1);
187 m_pathView->setDragMargin(width() / 2);
188 } else {
189 m_listView->setPreferredHighlightBegin(height() / 2 - (height() / m_tumbler->visibleItemCount() / 2));
190 m_listView->setPreferredHighlightEnd(height() / 2 + (height() / m_tumbler->visibleItemCount() / 2));
191 }
192}
193
194void QQuickTumblerView::updateModel()
195{
196 if (m_pathView && !m_pathView->model().isValid() && m_model.isValid()) {
197 // QQuickPathView::setPathItemCount() resets the offset animation,
198 // so we just skip the animation while constructing the view.
199 const int oldHighlightMoveDuration = m_pathView->highlightMoveDuration();
200 m_pathView->setHighlightMoveDuration(0);
201
202 // Setting model can change the count, which can affect the wrap, which can cause
203 // the current view to be deleted before setModel() is finished, which causes a crash.
204 // Since QQuickTumbler can't know about QQuickTumblerView, we use its private API to
205 // inform it that it should delay setting wrap.
206 QQuickTumblerPrivate *tumblerPrivate = QQuickTumblerPrivate::get(m_tumbler);
207 tumblerPrivate->beginSetModel();
208 m_pathView->setModel(m_model);
209 tumblerPrivate->endSetModel();
210
211 // The count-depends-on-wrap behavior could cause wrap to change after
212 // the call above, so we must check that we're still using a PathView.
213 if (m_pathView)
214 m_pathView->setHighlightMoveDuration(oldHighlightMoveDuration);
215 } else if (m_listView && !m_listView->model().isValid() && m_model.isValid()) {
216 const int currentIndex = m_tumbler->currentIndex();
217 QQuickTumblerPrivate *tumblerPrivate = QQuickTumblerPrivate::get(m_tumbler);
218
219 // setModel() causes QQuickTumblerPrivate::_q_onViewCountChanged() to
220 // be called, which calls QQuickTumbler::setCurrentIndex(),
221 // which results in QQuickItemViewPrivate::createHighlightItem() being
222 // called. When the highlight item is created,
223 // QQuickTumblerPrivate::itemChildAdded() is notified and
224 // QQuickTumblerPrivate::_q_updateItemHeights() is called, which causes
225 // a geometry change in the item and createHighlight() is called again.
226 // However, since the highlight item hadn't been assigned yet in the
227 // previous call frame, the "if (highlight) { delete highlight; }"
228 // check doesn't succeed, so the item is never deleted.
229 //
230 // To avoid this, we tell QQuickTumblerPrivate to ignore signals while
231 // setting the model, and manually call _q_onViewCountChanged() to
232 // ensure the correct sequence of calls happens (_q_onViewCountChanged()
233 // has to be within the ignoreSignals scope, because it also generates
234 // recursion otherwise).
235 tumblerPrivate->ignoreSignals = true;
236 m_listView->setModel(m_model);
237 m_listView->setCurrentIndex(currentIndex);
238
239 tumblerPrivate->_q_onViewCountChanged();
240 tumblerPrivate->ignoreSignals = false;
241 }
242}
243
244void QQuickTumblerView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
245{
246 QQuickItem::geometryChange(newGeometry, oldGeometry);
247 updateView();
248}
249
255
257{
259
260 if (change == QQuickItem::ItemParentHasChanged && data.item) {
261 if (m_tumbler)
262 m_tumbler->disconnect(this);
263
264 m_tumbler = qobject_cast<QQuickTumbler*>(parentItem());
265
266 if (m_tumbler) {
267 // We assume that the parentChanged() signal of the tumbler will be emitted before its wrap property is set...
268 connect(m_tumbler, &QQuickTumbler::wrapChanged, this, &QQuickTumblerView::createView);
269 connect(m_tumbler, &QQuickTumbler::visibleItemCountChanged, this, &QQuickTumblerView::updateView);
270 }
271 }
272}
273
274QQuickItem *QQuickTumblerView::view()
275{
276 if (!m_tumbler)
277 return nullptr;
278
279 if (m_tumbler->wrap())
280 return m_pathView;
281
282 return m_listView;
283}
284
286
287#include "moc_qquicktumblerview_p.cpp"
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
void deleteLater()
\threadsafe
Definition qobject.cpp:2435
The QQmlComponent class encapsulates a QML component definition.
static void setContextForObject(QObject *, QQmlContext *)
Sets the QQmlContext for the object to context.
void setPreferredHighlightEnd(qreal)
void setModel(const QVariant &)
void setDelegate(QQmlComponent *)
void setPreferredHighlightBegin(qreal)
void setHighlightRangeMode(HighlightRangeMode mode)
void setCurrentIndex(int idx)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
void setParentItem(QQuickItem *parent)
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
qreal width
This property holds the width of this item.
Definition qquickitem.h:75
QQuickItem * parentItem() const
void setVisible(bool)
virtual void itemChange(ItemChange, const ItemChangeData &)
Called when change occurs for this item.
bool isComponentComplete() const
Returns true if construction of the QML component is complete; otherwise returns false.
qreal height
This property holds the height of this item.
Definition qquickitem.h:76
ItemChange
Used in conjunction with QQuickItem::itemChange() to notify the item about certain types of changes.
Definition qquickitem.h:144
@ ItemParentHasChanged
Definition qquickitem.h:149
void setClip(bool)
void setHighlightMoveDuration(int) override
void setSnapMode(SnapMode mode)
void setPreferredHighlightBegin(qreal)
void setModel(const QVariant &)
void setDelegate(QQmlComponent *)
void setHighlightMoveDuration(int)
void setPath(QQuickPath *)
void setDragMargin(qreal margin)
void setPathItemCount(int)
void setPreferredHighlightEnd(qreal)
static QQuickTumblerPrivate * get(QQuickTumbler *tumbler)
QQmlComponent * delegate
void setPath(QQuickPath *path)
void setModel(const QVariant &model)
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
void itemChange(ItemChange change, const ItemChangeData &data) override
Called when change occurs for this item.
void setDelegate(QQmlComponent *delegate)
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
void visibleItemCountChanged()
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore
Definition qsize.h:208
\inmodule QtCore
Definition qvariant.h:65
bool isValid() const
Returns true if the storage type of this variant is not QMetaType::UnknownType; otherwise returns fal...
Definition qvariant.h:714
Combined button and popup list for selecting options.
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLsizei const GLchar *const * path
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:75
void QQml_setParent_noEvent(QObject *object, QObject *parent)
Makes the object a child of parent.
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
QSqlQueryModel * model
[16]
\inmodule QtQuick
Definition qquickitem.h:159