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