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 | |
43 | QT_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 | */ |
63 | QVariant QQmlDelegateChoice::roleValue() const |
64 | { |
65 | return m_value; |
66 | } |
67 | |
68 | void 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 | */ |
96 | int QQmlDelegateChoice::row() const |
97 | { |
98 | return m_row; |
99 | } |
100 | |
101 | void 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 | */ |
115 | int QQmlDelegateChoice::column() const |
116 | { |
117 | return m_column; |
118 | } |
119 | |
120 | void QQmlDelegateChoice::setColumn(int c) |
121 | { |
122 | if (m_column == c) |
123 | return; |
124 | m_column = c; |
125 | emit columnChanged(); |
126 | emit changed(); |
127 | } |
128 | |
129 | QQmlComponent *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 | */ |
138 | void 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 | |
153 | bool 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 | */ |
225 | void 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 | |
247 | QQmlListProperty<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 | |
258 | void 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 | |
266 | int QQmlDelegateChooser::choices_count(QQmlListProperty<QQmlDelegateChoice> *prop) |
267 | { |
268 | QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object); |
269 | return q->m_choices.count(); |
270 | } |
271 | |
272 | QQmlDelegateChoice *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 | |
278 | void 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 | |
287 | void 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 | |
299 | void 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 | |
307 | QQmlComponent *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 | |
333 | QT_END_NAMESPACE |
334 | |