1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqmldelegatecomponent_p.h"
41#include <QtQmlModels/private/qqmladaptormodel_p.h>
42
43QT_BEGIN_NAMESPACE
44
45/*!
46 \qmltype DelegateChoice
47 \instantiates QQmlDelegateChoice
48 \inqmlmodule Qt.labs.qmlmodels
49 \brief Encapsulates a delegate and when to use it.
50
51 The DelegateChoice type wraps a delegate and defines the circumstances
52 in which it should be chosen.
53
54 DelegateChoices can be nested inside a DelegateChooser.
55
56 \sa DelegateChooser
57*/
58
59/*!
60 \qmlproperty variant QtQml.Models::DelegateChoice::roleValue
61 This property holds the value used to match the role data for the role provided by \l DelegateChooser::role.
62*/
63QVariant QQmlDelegateChoice::roleValue() const
64{
65 return m_value;
66}
67
68void QQmlDelegateChoice::setRoleValue(const QVariant &value)
69{
70 if (m_value == value)
71 return;
72 m_value = value;
73 emit roleValueChanged();
74 emit changed();
75}
76
77/*!
78 \qmlproperty int QtQml.Models::DelegateChoice::row
79 This property holds the value used to match the row value of model elements.
80 With models that have only the index property (and thus only one column), this property
81 should be intended as an index, and set to the desired index value.
82
83 \note Setting both row and index has undefined behavior. The two are equivalent and only
84 one should be used.
85
86 \sa index
87*/
88
89/*!
90 \qmlproperty int QtQml.Models::DelegateChoice::index
91 This property holds the value used to match the index value of model elements.
92 This is effectively an alias for \l row.
93
94 \sa row
95*/
96int QQmlDelegateChoice::row() const
97{
98 return m_row;
99}
100
101void QQmlDelegateChoice::setRow(int r)
102{
103 if (m_row == r)
104 return;
105 m_row = r;
106 emit rowChanged();
107 emit indexChanged();
108 emit changed();
109}
110
111/*!
112 \qmlproperty int QtQml.Models::DelegateChoice::column
113 This property holds the value used to match the column value of model elements.
114*/
115int QQmlDelegateChoice::column() const
116{
117 return m_column;
118}
119
120void QQmlDelegateChoice::setColumn(int c)
121{
122 if (m_column == c)
123 return;
124 m_column = c;
125 emit columnChanged();
126 emit changed();
127}
128
129QQmlComponent *QQmlDelegateChoice::delegate() const
130{
131 return m_delegate;
132}
133
134/*!
135 \qmlproperty Component QtQml.Models::DelegateChoice::delegate
136 This property holds the delegate to use if this choice matches the model item.
137*/
138void QQmlDelegateChoice::setDelegate(QQmlComponent *delegate)
139{
140 if (m_delegate == delegate)
141 return;
142 QQmlAbstractDelegateComponent *adc = static_cast<QQmlAbstractDelegateComponent *>(m_delegate);
143 if (adc)
144 disconnect(sender: adc, signal: &QQmlAbstractDelegateComponent::delegateChanged, receiver: this, slot: &QQmlDelegateChoice::delegateChanged);
145 m_delegate = delegate;
146 adc = static_cast<QQmlAbstractDelegateComponent *>(delegate);
147 if (adc)
148 connect(sender: adc, signal: &QQmlAbstractDelegateComponent::delegateChanged, receiver: this, slot: &QQmlDelegateChoice::delegateChanged);
149 emit delegateChanged();
150 emit changed();
151}
152
153bool QQmlDelegateChoice::match(int row, int column, const QVariant &value) const
154{
155 if (!m_value.isValid() && m_row < 0 && m_column < 0)
156 return true;
157
158 const bool roleMatched = (m_value.isValid()) ? value == m_value : true;
159 const bool rowMatched = (m_row < 0 ) ? true : m_row == row;
160 const bool columnMatched = (m_column < 0 ) ? true : m_column == column;
161 return roleMatched && rowMatched && columnMatched;
162}
163
164/*!
165 \qmltype DelegateChooser
166 \instantiates QQmlDelegateChooser
167 \inqmlmodule Qt.labs.qmlmodels
168 \brief Allows a view to use different delegates for different types of items in the model.
169
170 The DelegateChooser is a special \l Component type intended for those scenarios where a Component is required
171 by a view and used as a delegate.
172 DelegateChooser encapsulates a set of \l {DelegateChoice}s.
173 These choices are used to determine the delegate that will be instantiated for each
174 item in the model.
175 The selection of the choice is performed based on the value that a model item has for \l role,
176 and also based on index.
177
178 DelegateChooser is commonly used when a view needs to display a set of delegates that are significantly
179 different from each other. For example, a typical phone settings view might include toggle switches,
180 sliders, radio buttons, and other visualizations based on the type of each setting. In this case, DelegateChooser
181 could provide an easy way to associate a different type of delegate with each setting:
182
183 \qml \QtMinorVersion
184 import QtQuick 2.\1
185 import QtQuick.Controls 2.\1
186 import Qt.labs.qmlmodels 1.0
187
188 ListView {
189 width: 200; height: 400
190
191 ListModel {
192 id: listModel
193 ListElement { type: "info"; ... }
194 ListElement { type: "switch"; ... }
195 ListElement { type: "swipe"; ... }
196 ListElement { type: "switch"; ... }
197 }
198
199 DelegateChooser {
200 id: chooser
201 role: "type"
202 DelegateChoice { roleValue: "info"; ItemDelegate { ... } }
203 DelegateChoice { roleValue: "switch"; SwitchDelegate { ... } }
204 DelegateChoice { roleValue: "swipe"; SwipeDelegate { ... } }
205 }
206
207 model: listModel
208 delegate: chooser
209 }
210 \endqml
211
212 \note This type is intended to transparently work only with TableView and any DelegateModel-based view.
213 Views (including user-defined views) that aren't internally based on a DelegateModel need to explicitly support
214 this type of component to make it function as described.
215
216 \sa DelegateChoice
217*/
218
219/*!
220 \qmlproperty string QtQml.Models::DelegateChooser::role
221 This property holds the role or the property name used to determine the delegate for a given model item.
222
223 \sa DelegateChoice
224*/
225void QQmlDelegateChooser::setRole(const QString &role)
226{
227 if (m_role == role)
228 return;
229 m_role = role;
230 emit roleChanged();
231}
232
233/*!
234 \qmlproperty list<DelegateChoice> QtQml.Models::DelegateChooser::choices
235 \default
236
237 The list of DelegateChoices for the chooser.
238
239 The list is treated as an ordered list, where the first DelegateChoice to match
240 will be used be a view.
241
242 It should not generally be necessary to refer to the \c choices property,
243 as it is the default property for DelegateChooser and thus all child items are
244 automatically assigned to this property.
245*/
246
247QQmlListProperty<QQmlDelegateChoice> QQmlDelegateChooser::choices()
248{
249 return QQmlListProperty<QQmlDelegateChoice>(this, nullptr,
250 QQmlDelegateChooser::choices_append,
251 QQmlDelegateChooser::choices_count,
252 QQmlDelegateChooser::choices_at,
253 QQmlDelegateChooser::choices_clear,
254 QQmlDelegateChooser::choices_replace,
255 QQmlDelegateChooser::choices_removeLast);
256}
257
258void QQmlDelegateChooser::choices_append(QQmlListProperty<QQmlDelegateChoice> *prop, QQmlDelegateChoice *choice)
259{
260 QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
261 q->m_choices.append(t: choice);
262 connect(sender: choice, signal: &QQmlDelegateChoice::changed, receiver: q, slot: &QQmlAbstractDelegateComponent::delegateChanged);
263 q->delegateChanged();
264}
265
266int QQmlDelegateChooser::choices_count(QQmlListProperty<QQmlDelegateChoice> *prop)
267{
268 QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object);
269 return q->m_choices.count();
270}
271
272QQmlDelegateChoice *QQmlDelegateChooser::choices_at(QQmlListProperty<QQmlDelegateChoice> *prop, int index)
273{
274 QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object);
275 return q->m_choices.at(i: index);
276}
277
278void QQmlDelegateChooser::choices_clear(QQmlListProperty<QQmlDelegateChoice> *prop)
279{
280 QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
281 for (QQmlDelegateChoice *choice : q->m_choices)
282 disconnect(sender: choice, signal: &QQmlDelegateChoice::changed, receiver: q, slot: &QQmlAbstractDelegateComponent::delegateChanged);
283 q->m_choices.clear();
284 q->delegateChanged();
285}
286
287void QQmlDelegateChooser::choices_replace(QQmlListProperty<QQmlDelegateChoice> *prop, int index,
288 QQmlDelegateChoice *choice)
289{
290 QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
291 disconnect(sender: q->m_choices[index], signal: &QQmlDelegateChoice::changed,
292 receiver: q, slot: &QQmlAbstractDelegateComponent::delegateChanged);
293 q->m_choices[index] = choice;
294 connect(sender: choice, signal: &QQmlDelegateChoice::changed, receiver: q,
295 slot: &QQmlAbstractDelegateComponent::delegateChanged);
296 q->delegateChanged();
297}
298
299void QQmlDelegateChooser::choices_removeLast(QQmlListProperty<QQmlDelegateChoice> *prop)
300{
301 QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
302 disconnect(sender: q->m_choices.takeLast(), signal: &QQmlDelegateChoice::changed,
303 receiver: q, slot: &QQmlAbstractDelegateComponent::delegateChanged);
304 q->delegateChanged();
305}
306
307QQmlComponent *QQmlDelegateChooser::delegate(QQmlAdaptorModel *adaptorModel, int row, int column) const
308{
309 QVariant v;
310 if (!m_role.isNull())
311 v = value(adaptorModel, row, column, role: m_role);
312 if (!v.isValid()) { // check if the row only has modelData, for example if the row is a QVariantMap
313 v = value(adaptorModel, row, column, QStringLiteral("modelData"));
314
315 if (v.isValid()) {
316 if (v.canConvert(targetTypeId: QMetaType::QVariantMap))
317 v = v.toMap().value(akey: m_role);
318 else if (v.canConvert(targetTypeId: QMetaType::QObjectStar))
319 v = v.value<QObject*>()->property(name: m_role.toUtf8());
320 }
321 }
322
323 // loop through choices, finding first one that fits
324 for (int i = 0; i < m_choices.count(); ++i) {
325 const QQmlDelegateChoice *choice = m_choices.at(i);
326 if (choice->match(row, column, value: v))
327 return choice->delegate();
328 }
329
330 return nullptr;
331}
332
333QT_END_NAMESPACE
334

source code of qtdeclarative/src/imports/labsmodels/qqmldelegatecomponent.cpp