Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qrangemodel.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 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// Qt-Security score:significant reason:default
4
5#include "qrangemodel.h"
6#include <QtCore/qsize.h>
7
8#include <QtCore/private/qabstractitemmodel_p.h>
9
10#include <variant>
11
12QT_BEGIN_NAMESPACE
13
14class QRangeModelPrivate : QAbstractItemModelPrivate
15{
16 Q_DECLARE_PUBLIC(QRangeModel)
17
18public:
19 explicit QRangeModelPrivate(std::unique_ptr<QRangeModelImplBase, QRangeModelImplBase::Deleter> impl)
20 : impl(std::move(impl))
21 {}
22
23 std::unique_ptr<QRangeModelImplBase, QRangeModelImplBase::Deleter> impl;
24 friend class QRangeModelImplBase;
25
26 static QRangeModelPrivate *get(QRangeModel *model) { return model->d_func(); }
27 static const QRangeModelPrivate *get(const QRangeModel *model) { return model->d_func(); }
28
29 mutable QHash<int, QByteArray> m_roleNames;
30 QRangeModel::AutoConnectPolicy m_autoConnectPolicy = QRangeModel::AutoConnectPolicy::None;
31 bool m_dataChangedDispatchBlocked = false;
32
33 static void emitDataChanged(const QModelIndex &index, int role)
34 {
35 const auto *model = static_cast<const QRangeModel *>(index.model());
36 if (!get(model)->m_dataChangedDispatchBlocked) {
37 const auto *emitter = QRangeModelImplBase::getImplementation(model);
38 const_cast<QRangeModelImplBase *>(emitter)->dataChanged(index, index, {role});
39 }
40 }
41};
42
44{
45 PropertyChangedHandler(const QPersistentModelIndex &index, int role)
47 {}
48
49 // move-only
53 {
54 Q_ASSERT(std::holds_alternative<Data>(storage));
55 // A moved-from handler is essentially a reference to the moved-to
56 // handler (which lives inside QSlotObject/QCallableObject). This
57 // way we can update the stored handler with the created connection.
58 other.storage = this;
59 }
63
64 // we can assign a connection to a moved-from handler to update the
65 // handler stored in the QSlotObject/QCallableObject.
66 PropertyChangedHandler &operator=(QMetaObject::Connection &&connection)
67 {
68 Q_ASSERT(std::holds_alternative<PropertyChangedHandler *>(storage));
69 std::get<PropertyChangedHandler *>(storage)->connection = std::move(connection);
70 return *this;
71 }
72
73 void operator()();
74
75private:
76 QMetaObject::Connection connection;
77 struct Data
78 {
79 QPersistentModelIndex index;
80 int role = -1;
81 };
82 std::variant<PropertyChangedHandler *, Data> storage;
83};
84
86{
87 Q_ASSERT(std::holds_alternative<Data>(storage));
88 const auto &data = std::get<Data>(storage);
89 if (!data.index.isValid()) {
90 if (!QObject::disconnect(connection))
91 qWarning() << "Failed to break connection for" << Qt::ItemDataRole(data.role);
92 } else {
93 QRangeModelPrivate::emitDataChanged(data.index, data.role);
94 }
95}
96
98{
99 ConstPropertyChangedHandler(const QModelIndex &index, int role)
100 : index(index), role(role)
101 {}
102
103 // move-only
106
107 void operator()() { QRangeModelPrivate::emitDataChanged(index, role); }
108
109private:
110 QModelIndex index;
111 int role = -1;
112};
113
114QRangeModel::QRangeModel(QRangeModelImplBase *impl, QObject *parent)
115 : QAbstractItemModel(*new QRangeModelPrivate({impl, {}}), parent)
116{
117}
118
119QRangeModelImplBase *QRangeModelImplBase::getImplementation(QRangeModel *model)
120{
121 return model->d_func()->impl.get();
122}
123
124const QRangeModelImplBase *QRangeModelImplBase::getImplementation(const QRangeModel *model)
125{
126 return model->d_func()->impl.get();
127}
128
129QScopedValueRollback<bool> QRangeModelImplBase::blockDataChangedDispatch()
130{
131 return QScopedValueRollback(m_rangeModel->d_func()->m_dataChangedDispatchBlocked, true);
132}
133
134/*!
135 \internal
136
137 Using \a metaObject, return a mapping of roles to the matching QMetaProperties.
138*/
139QHash<int, QMetaProperty> QRangeModelImplBase::roleProperties(const QAbstractItemModel &model,
140 const QMetaObject &metaObject)
141{
142 const auto roles = model.roleNames();
143 QHash<int, QMetaProperty> result;
144 for (auto &&[role, roleName] : roles.asKeyValueRange()) {
145 if (role == Qt::RangeModelDataRole)
146 continue;
147 result[role] = metaObject.property(metaObject.indexOfProperty(roleName));
148 }
149 return result;
150}
151
152QHash<int, QMetaProperty> QRangeModelImplBase::columnProperties(const QMetaObject &metaObject)
153{
154 QHash<int, QMetaProperty> result;
155 const int propertyOffset = metaObject.propertyOffset();
156 for (int p = propertyOffset; p < metaObject.propertyCount(); ++p)
157 result[p - propertyOffset] = metaObject.property(p);
158 return result;
159}
160
161QRangeModelDetails::AutoConnectContext::~AutoConnectContext() = default;
162
163template <auto Handler>
164static bool connectPropertiesHelper(const QModelIndex &index, const QObject *item,
165 QRangeModelDetails::AutoConnectContext *context,
166 const QHash<int, QMetaProperty> &properties)
167{
168 if (!item)
169 return true;
170
171 auto connect = [item, context](const QModelIndex &cell, int role, const QMetaProperty &property) {
172 if (property.hasNotifySignal()) {
173 if (!Handler(cell, item, context, role, property))
174 return false;
175 } else {
176 qWarning() << "Property" << property.name() << "for" << Qt::ItemDataRole(role)
177 << "at" << cell << "has no notify signal";
178 }
179 return true;
180 };
181
182 if (context->mapping == QRangeModelDetails::AutoConnectContext::AutoConnectMapping::Roles) {
183 for (auto &&[role, property] : properties.asKeyValueRange())
184 connect(index, role, property);
185 } else {
186 for (auto &&[column, property] : properties.asKeyValueRange())
187 connect(index.siblingAtColumn(column), Qt::DisplayRole, property);
188 }
189 return true;
190}
191
192bool QRangeModelImplBase::connectProperty(const QModelIndex &index, const QObject *item,
193 QRangeModelDetails::AutoConnectContext *context,
194 int role, const QMetaProperty &property)
195{
196 if (!item)
197 return true; // nothing to do, continue
198 PropertyChangedHandler handler{index, role};
199 auto connection = property.enclosingMetaObject()->connect(item, property.notifySignal(),
200 context, std::move(handler));
201 if (!connection) {
202 qWarning() << "Failed to connect to" << item << property.name();
203 return false;
204 } else {
205 // handler is now in moved-from state, and acts like a reference to
206 // the handler that is stored in the QSlotObject/QCallableObject.
207 // This assignment updates the stored handler's connection with the
208 // QMetaObject::Connection handle, and should look harmless for
209 // static analyzers.
210 handler = std::move(connection);
211 }
212 return true;
213}
214
215bool QRangeModelImplBase::connectProperties(const QModelIndex &index, const QObject *item,
216 QRangeModelDetails::AutoConnectContext *context,
217 const QHash<int, QMetaProperty> &properties)
218{
219 return connectPropertiesHelper<QRangeModelImplBase::connectProperty>(index, item, context, properties);
220}
221
222bool QRangeModelImplBase::connectPropertyConst(const QModelIndex &index, const QObject *item,
223 QRangeModelDetails::AutoConnectContext *context,
224 int role, const QMetaProperty &property)
225{
226 if (!item)
227 return true; // nothing to do, continue
228 ConstPropertyChangedHandler handler{index, role};
229 if (!property.enclosingMetaObject()->connect(item, property.notifySignal(),
230 context, std::move(handler))) {
231 qWarning() << "Failed to connect to" << item << property.name();
232 return false;
233 } else {
234 return true;
235 }
236}
237
238bool QRangeModelImplBase::connectPropertiesConst(const QModelIndex &index, const QObject *item,
239 QRangeModelDetails::AutoConnectContext *context,
240 const QHash<int, QMetaProperty> &properties)
241{
242 return connectPropertiesHelper<QRangeModelImplBase::connectPropertyConst>(index, item, context, properties);
243}
244
246{
247Q_CORE_EXPORT QVariant qVariantAtIndex(const QModelIndex &index)
248{
253 };
256 size_t r = 0;
257 do {
258 variant = result[r].data();
259 ++r;
260 } while (!variant.isValid() && r < std::size(result));
261
262 return variant;
263}
264}
265
266/*!
267 \class QRangeModel
268 \inmodule QtCore
269 \since 6.10
270 \ingroup model-view
271 \brief QRangeModel implements QAbstractItemModel for any C++ range.
272 \reentrant
273
274 QRangeModel can make the data in any sequentially iterable C++ type
275 available to the \l{Model/View Programming}{model/view framework} of Qt.
276 This makes it easy to display existing data structures in the Qt Widgets
277 and Qt Quick item views, and to allow the user of the application to
278 manipulate the data using a graphical user interface.
279
280 To use QRangeModel, instantiate it with a C++ range and set it as
281 the model of one or more views:
282
283 \snippet qrangemodel/main.cpp array
284
285 \section1 Constructing the model
286
287 The range can be any C++ type for which the standard methods
288 \c{std::begin} and \c{std::end} are implemented, and for which the
289 returned iterator type satisfies \c{std::forward_iterator}. Certain model
290 operations will perform better if \c{std::size} is available, and if the
291 iterator satisfies \c{std::random_access_iterator}.
292
293 The range must be provided when constructing the model and can be provided
294 by value, reference wrapper, or pointer. How the model was constructed
295 defines whether changes through the model API will modify the original
296 data. Use QRangeModelAdapter to implicitly construct a model while also
297 having direct, type-safe, and convenient access to the model as a range.
298
299 When constructed by value, the model makes a copy of the range, and
300 QAbstractItemModel APIs that modify the model, such as setData() or
301 insertRows(), have no impact on the original range.
302
303 \snippet qrangemodel/main.cpp value
304
305 Changes made to the data can be monitored by connecting to the signals
306 emitted by the model, such as \l{QAbstractItemModel}{dataChanged()}.
307
308 To make modifications of the model affect the original range, provide the
309 range either by pointer:
310
311 \snippet qrangemodel/main.cpp pointer
312
313 or through a reference wrapper:
314
315 \snippet qrangemodel/main.cpp reference_wrapper
316
317 In this case, QAbstractItemModel APIs that modify the model also modify the
318 range. Methods that modify the structure of the range, such as insertRows()
319 or removeColumns(), use standard C++ container APIs \c{resize()},
320 \c{insert()}, \c{erase()}, in addition to dereferencing a mutating iterator
321 to set or clear the data.
322
323 \note Once the model has been constructed and passed on to a view, the
324 range that the model operates on must no longer be modified directly. Views
325 on the model wouldn't be informed about the changes, and structural changes
326 are likely to corrupt instances of QPersistentModelIndex that the model
327 maintains. Use QRangeModelAdapter to safely interact with the underlying
328 range while keeping the model updated.
329
330 The caller must make sure that the range's lifetime exceeds the lifetime of
331 the model.
332
333 Use smart pointers to make sure that the range is only deleted when all
334 clients are done with it.
335
336 \snippet qrangemodel/main.cpp smart_pointer
337
338 QRangeModel supports both shared and unique pointers.
339
340 \section2 Read-only or mutable
341
342 For ranges that are const objects, for which access always yields constant
343 values, or where the required container APIs are not available,
344 QRangeModel implements write-access APIs to do nothing and return
345 \c{false}. In the example using \c{std::array}, the model cannot add or
346 remove rows, as the number of entries in a C++ array is fixed. But the
347 values can be changed using setData(), and the user can trigger editing of
348 the values in the list view. By making the array const, the values also
349 become read-only.
350
351 \snippet qrangemodel/main.cpp const_array
352
353 The values are also read-only if the element type is const, like in
354
355 \snippet qrangemodel/main.cpp const_values
356
357 In the above examples using \c{std::vector}, the model can add or remove
358 rows, and the data can be changed. Passing the range as a constant
359 reference will make the model read-only.
360
361 \snippet qrangemodel/main.cpp const_ref
362
363 \note If the values in the range are const, then it's also not possible
364 to remove or insert columns and rows through the QAbstractItemModel API.
365 For more granular control, implement \l{the C++ tuple protocol}.
366
367 \section1 Rows and columns
368
369 The elements in the range are interpreted as rows of the model. Depending
370 on the type of these row elements, QRangeModel exposes the range as a
371 list, a table, or a tree.
372
373 If the row elements are simple values, then the range gets represented as a
374 list.
375
376 \snippet qrangemodel/main.cpp list_of_int
377
378 If the type of the row elements is an iterable range, such as a vector,
379 list, or array, then the range gets represented as a table.
380
381 \snippet qrangemodel/main.cpp grid_of_numbers
382
383 If the row type provides the standard C++ container APIs \c{resize()},
384 \c{insert()}, \c{erase()}, then columns can be added and removed via
385 insertColumns() and removeColumns(). All rows are required to have
386 the same number of columns.
387
388 \section2 Structs and gadgets as rows
389
390 If the row type implements \l{the C++ tuple protocol}, then the range gets
391 represented as a table with a fixed number of columns.
392
393 \snippet qrangemodel/main.cpp pair_int_QString
394
395 An easier and more flexible alternative to implementing the tuple protocol
396 for a C++ type is to use Qt's \l{Meta-Object System}{meta-object system} to
397 declare a type with \l{Qt's Property System}{properties}. This can be a
398 value type that is declared as a \l{Q_GADGET}{gadget}, or a QObject subclass.
399
400 \snippet qrangemodel/main.cpp gadget
401
402 Using QObject subclasses allows properties to be \l{Qt Bindable Properties}
403 {bindable}, or to have change notification signals. However, using QObject
404 instances for items has significant memory overhead.
405
406 Using Qt gadgets or objects is more convenient and can be more flexible
407 than implementing the tuple protocol. Those types are also directly
408 accessible from within QML. However, the access through \l{the property system}
409 comes with some runtime overhead. For performance critical models, consider
410 implementing the tuple protocol for compile-time generation of the access
411 code.
412
413 \section2 Multi-role items
414
415 The type of the items that the implementations of data(), setData(),
416 clearItemData() etc. operate on can be the same across the entire model -
417 like in the \c{gridOfNumbers} example above. But the range can also have
418 different item types for different columns, like in the \c{numberNames}
419 case.
420
421 By default, the value gets used for the Qt::DisplayRole and Qt::EditRole
422 roles. Most views expect the value to be
423 \l{QVariant::canConvert}{convertible to and from a QString} (but a custom
424 delegate might provide more flexibility).
425
426 \section3 Associative containers with multiple roles
427
428 If the item is an associative container that uses \c{int},
429 \l{Qt::ItemDataRole}, or QString as the key type, and QVariant as the
430 mapped type, then QRangeModel interprets that container as the storage
431 of the data for multiple roles. The data() and setData() functions return
432 and modify the mapped value in the container, and setItemData() modifies all
433 provided values, itemData() returns all stored values, and clearItemData()
434 clears the entire container.
435
436 \snippet qrangemodel/main.cpp color_map
437
438 The most efficient data type to use as the key is Qt::ItemDataRole or
439 \c{int}. When using \c{int}, itemData() returns the container as is, and
440 doesn't have to create a copy of the data.
441
442 \section3 Gadgets and Objects as multi-role items
443
444 Gadgets and QObject types can also be represented as multi-role items. The
445 \l{The Property System}{properties} of those items will be used for the
446 role for which the \l{roleNames()}{name of a role} matches. If all items
447 hold the same type of gadget or QObject, then the \l{roleNames()}
448 implementation in QRangeModel will return the list of properties of that
449 type.
450
451 \snippet qrangemodel/main.cpp color_gadget_decl
452 \snippet qrangemodel/main.cpp color_gadget_impl
453 \snippet qrangemodel/main.cpp color_gadget_end
454
455 When used in a table, this is the default representation for gadgets:
456
457 \snippet qrangemodel/main.cpp color_gadget_table
458
459 When used in a list, these types are however by default represented as
460 multi-column rows, with each property represented as a separate column. To
461 force a gadget to be represented as a multi-role item in a list, declare
462 the gadget as a multi-role type by specializing QRoleModel::RowOptions,
463 with a \c{static constexpr auto rowCategory} member variable set to
464 MultiRoleItem.
465
466 \snippet qrangemodel/main.cpp color_gadget_decl
467 \dots
468 \snippet qrangemodel/main.cpp color_gadget_end
469 \snippet qrangemodel/main.cpp color_gadget_multi_role_gadget
470
471 You can also wrap such types into a single-element tuple, turning the list
472 into a table with a single column:
473
474 \snippet qrangemodel/main.cpp color_gadget_single_column
475
476 In this case, note that direct access to the elements in the list data
477 needs to use \c{std::get}:
478
479 \snippet qrangemodel/main.cpp color_gadget_single_column_access_get
480
481 or alternatively a structured binding:
482
483 \snippet qrangemodel/main.cpp color_gadget_single_column_access_sb
484
485 \section2 Rows as values or pointers
486
487 In the examples so far, we have always used QRangeModel with ranges that
488 hold values. QRangeModel can also operate on ranges that hold pointers,
489 including smart pointers. This allows QRangeModel to operate on ranges of
490 polymorph types, such as QObject subclasses.
491
492 \snippet qrangemodel/main.cpp object_0
493 \dots
494 \snippet qrangemodel/main.cpp object_1
495
496 \snippet qrangemodel/main.cpp vector_of_objects_0
497 \dots
498 \snippet qrangemodel/main.cpp vector_of_objects_1
499 \snippet qrangemodel/main.cpp vector_of_objects_2
500
501 As with values, the type of the row defines whether the range is
502 represented as a list, table, or tree. Rows that are QObjects will present
503 each property as a column, unless the QRangeModel::RowOptions template is
504 specialized to declare the type as a multi-role item.
505
506 \snippet qrangemodel/main.cpp vector_of_multirole_objects_0
507 \snippet qrangemodel/main.cpp vector_of_multirole_objects_1
508 \dots
509 \snippet qrangemodel/main.cpp vector_of_multirole_objects_2
510
511 \note If the range holds raw pointers, then you have to construct
512 QRangeModel from a pointer or reference wrapper of the range. Otherwise the
513 ownership of the data becomes ambiguous, and a copy of the range would
514 still be operating on the same actual row data, resulting in unexpected
515 side effects.
516
517 \section2 Subclassing QRangeModel
518
519 Subclassing QRangeModel makes it possible to add convenient APIs that take
520 the data type and structure of the range into account.
521
522 \snippet qrangemodel/main.cpp subclass_header
523
524 When doing so, add the range as a private member, and call the QRangeModel
525 constructor with a reference wrapper or pointer to that member. This
526 properly encapsulates the data and avoids direct access.
527
528 \snippet qrangemodel/main.cpp subclass_API
529
530 Add member functions to provide type-safe access to the data, using the
531 QAbstractItemModel API to perform any operation that modifies the range.
532 Read-only access can directly operate on the data structure.
533
534 \section1 Trees of data
535
536 QRangeModel can represent a data structure as a tree model. Such a
537 tree data structure needs to be homomorphic: on all levels of the tree, the
538 list of child rows needs to use the exact same representation as the tree
539 itself. In addition, the row type needs be of a static size: either a gadget
540 or QObject type, or a type that implements \l{the C++ tuple protocol}.
541
542 To represent such data as a tree, QRangeModel has to be able to traverse the
543 data structure: for any given row, the model needs to be able to retrieve
544 the parent row, and the optional span of children. These traversal functions
545 can be provided implicitly through the row type, or through an explicit
546 protocol type.
547
548 \section2 Implicit tree traversal protocol
549
550 \snippet qrangemodel/main.cpp tree_protocol_0
551
552 The tree itself is a vector of \c{TreeRow} values. See \l{Tree Rows as
553 pointers or values} for the considerations on whether to use values or
554 pointers of items for the rows.
555
556 \snippet qrangemodel/main.cpp tree_protocol_1
557
558 The row class can be of any fixed-size type described above: a type that
559 implements the tuple protocol, a gadget, or a QObject. In this example, we
560 use a gadget.
561
562 Each row item needs to maintain a pointer to the parent row, as well as an
563 optional range of child rows. That range has to be identical to the range
564 structure used for the tree itself.
565
566 Making the row type default constructible is optional, and allows the model
567 to construct new row data elements, for instance in the insertRow() or
568 moveRows() implementations.
569
570 \snippet qrangemodel/main.cpp tree_protocol_2
571
572 The tree traversal protocol can then be implemented as member functions of
573 the row data type. A const \c{parentRow()} function has to return a pointer
574 to a row item; and the \c{childRows()} function has to return a reference
575 to a const \c{std::optional} that can hold the optional child range.
576
577 These two functions are sufficient for the model to navigate the tree as a
578 read-only data structure. To allow the user to edit data in a view, and the
579 model to implement mutating model APIs such as insertRows(), removeRows(),
580 and moveRows(), we have to implement additional functions for write-access:
581
582 \snippet qrangemodel/main.cpp tree_protocol_3
583
584 The model calls the \c{setParentRow()} function and mutable \c{childRows()}
585 overload to move or insert rows into an existing tree branch, and to update
586 the parent pointer should the old value have become invalid. The non-const
587 overload of \c{childRows()} provides in addition write-access to the row
588 data.
589
590 \note The model performs setting the parent of a row, removing that row
591 from the old parent, and adding it to the list of the new parent's children,
592 as separate steps. This keeps the protocol interface small.
593
594 \dots
595 \snippet qrangemodel/main.cpp tree_protocol_4
596
597 The rest of the class implementation is not relevant for the model, but
598 a \c{addChild()} helper provides us with a convenient way to construct the
599 initial state of the tree.
600
601 \snippet qrangemodel/main.cpp tree_protocol_5
602
603 A QRangeModel instantiated with an instance of such a range will
604 represent the data as a tree.
605
606 \snippet qrangemodel/main.cpp tree_protocol_6
607
608 \section2 Tree traversal protocol in a separate class
609
610 The tree traversal protocol can also be implemented in a separate class.
611
612 \snippet qrangemodel/main.cpp explicit_tree_protocol_0
613
614 Pass an instance of this protocol implementation to the QRangeModel
615 constructor:
616
617 \snippet qrangemodel/main.cpp explicit_tree_protocol_1
618
619 \section2 Tree Rows as pointers or values
620
621 The row type of the data range can be either a value, or a pointer. In
622 the code above we have been using the tree rows as values in a vector,
623 which avoids that we have to deal with explicit memory management. However,
624 a vector as a contiguous block of memory invalidates all iterators and
625 references when it has to reallocate the storage, or when inserting or
626 removing elements. This impacts the pointer to the parent item, which is
627 the location of the parent row within the vector. Making sure that this
628 parent (and QPersistentModelIndex instances referring to items within it)
629 stays valid can incurr substantial performance overhead. The
630 QRangeModel implementation has to assume that all references into the
631 range become invalid when modifying the range.
632
633 Alternatively, we can also use a range of row pointers as the tree type:
634
635 \snippet qrangemodel/main.cpp tree_of_pointers_0
636
637 In this case, we have to allocate all TreeRow instances explicitly using
638 operator \c{new}, and implement the destructor to \c{delete} all items in
639 the vector of children.
640
641 \snippet qrangemodel/main.cpp tree_of_pointers_1
642 \snippet qrangemodel/main.cpp tree_of_pointers_2
643
644 Before we can construct a model that represents this data as a tree, we need
645 to also implement the tree traversal protocol.
646
647 \snippet qrangemodel/main.cpp tree_of_pointers_3
648
649 An explicit protocol implementation for mutable trees of pointers has to
650 provide two additional member functions, \c{newRow()} and
651 \c{deleteRow(RowType *)}.
652
653 \snippet qrangemodel/main.cpp tree_of_pointers_4
654
655 The model will call those functions when creating new rows in insertRows(),
656 and when removing rows in removeRows(). In addition, if the model has
657 ownership of the data, then it will also delete all top-level rows upon
658 destruction. Note how in this example, we move the tree into the model, so
659 we must no longer perform any operations on it. QRangeModel, when
660 constructed by moving tree-data with row-pointers into it, will take
661 ownership of the data, and delete the row pointers in it's destructor.
662
663 Using pointers as rows comes with some memory allocation and management
664 overhead. However, the references to the row items remain stable, even when
665 they are moved around in the range, or when the range reallocates. This can
666 significantly reduce the cost of making modifications to the model's
667 structure when using insertRows(), removeRows(), or moveRows().
668
669 Each choice has different performance and memory overhead trade-offs. The
670 best option depends on the exact use case and data structure used.
671
672 \section2 The C++ tuple protocol
673
674 As seen in the \c{numberNames} example above, the row type can be a tuple,
675 and in fact any type that implements the tuple protocol. This protocol is
676 implemented by specializing \c{std::tuple_size} and \c{std::tuple_element},
677 and overloading the unqualified \c{get} function. Do so for your custom row
678 type to make existing structured data available to the model/view framework
679 in Qt.
680
681 \snippet qrangemodel/main.cpp tuple_protocol
682
683 In the above implementation, the \c{title} and \c{author} values of the
684 \c{Book} type are returned as \c{const}, so the model flags items in those
685 two columns as read-only. The user won't be able to trigger editing, and
686 setData() does nothing and returns false. For \c{summary} and \c{rating}
687 the implementation returns the same value category as the book, so when
688 \c{get} is called with a mutable reference to a \c{Book}, then it will
689 return a mutable reference of the respective variable. The model makes
690 those columns editable, both for the user and for programmatic access.
691
692 \note The implementation of \c{get} above requires C++23.
693
694 \section2 Binary compatibility considerations
695
696 QRangeModel is not a template class. Passing QRangeModel instances (by
697 pointer or reference, as with all QObject classes) through library APIs, or
698 storing QRangeModel by value in a public class of a library, is safe.
699
700 However, the QRangeModel constructor is a template and inline, and the
701 internal implementation that is specialized on the type of the range the
702 model operates on is instantiated in the constructor. You should not call
703 the constructor in an inline-implementation of a library API. It results in
704 ODR violations, and might break binary compatibility of that library if the
705 Qt version it gets built against is different from the Qt version an
706 application using that library is built against.
707
708 \sa {Model/View Programming}
709*/
710
711/*!
712 \class QRangeModel::RowOptions
713 \inmodule QtCore
714 \ingroup model-view
715 \brief The RowOptions template provides a customization point to control
716 how QRangeModel represents types used as rows.
717 \since 6.10
718
719 RowOptions<T> is a struct template where \a T specifies the row type.
720 Specialize this template for the type used in your range, and add the
721 relevant members.
722
723 \table
724 \header
725 \li Member
726 \li Documentation
727 \row
728 \li static constexpr RowCategory rowCategory
729 \li RowCategory
730 \row
731 \li static QVariant headerData(int section, int role)
732 \li \l{QAbstractItemModel::headerData}{Header data} with \a role for
733 the \c section of the horizontal header.
734 \endtable
735
736 \snippet qrangemodel/main.cpp color_gadget_decl
737 \dots
738 \snippet qrangemodel/main.cpp color_gadget_end
739 \snippet qrangemodel/main.cpp color_gadget_multi_role_gadget
740
741*/
742
743/*!
744 \enum QRangeModel::RowCategory
745
746 This enum describes how QRangeModel should present the elements of the
747 range it was constructed with.
748
749 \value Default
750 QRangeModel decides how to present the rows.
751 \value MultiRoleItem
752 QRangeModel will present items with a meta object as multi-role
753 items, also when used in a one-dimensional range.
754
755 Specialize the RowOptions template for your type, and add a public member
756 variable \c{static constexpr auto rowCategory} with one of the values from
757 this enum.
758
759 \sa RowOptions
760*/
761
762/*!
763 \class QRangeModel::ItemAccess
764 \inmodule QtCore
765 \ingroup model-view
766 \brief The ItemAccess template provides a customization point to control
767 how QRangeModel accesses role data of individual items.
768 \since 6.11
769
770 ItemAccess<T> is a struct template where \a T specifies the item type.
771 Specialize this template for the type used in your data structure, and
772 implement \c{readRole()} and \c{writeRole()} members to access the role-
773 specific data of your type.
774
775 \code
776 template <>
777 struct QRangeModel::ItemAccess<ItemType>
778 {
779 static QVariant readRole(const ItemType &item, int role)
780 {
781 switch (role) {
782 // ...
783 }
784 return {};
785 }
786
787 static bool writeRole(ItemType &item, const QVariant &data, int role)
788 {
789 bool ok = false;
790 switch (role) {
791 // ...
792 }
793
794 return ok;
795 }
796 };
797 \endcode
798
799 Optionally, a \c{flags()} implementation can return a combination of
800 Qt::ItemFlags:
801
802 \code
803 static Qt::ItemFlags flags(const ItemType &item)
804 {
805 return item.flags();
806 }
807 \endcode
808
809 A specialization of this type will take precedence over any predefined
810 behavior. Do not specialize this template for types you do not own. Types
811 for which ItemAccess is specialized are implicitly interpreted as
812 \l{RowCategory}{multi-role items}.
813*/
814
815/*!
816 \fn template <typename Range, QRangeModelDetails::if_table_range<Range>> QRangeModel::QRangeModel(Range &&range, QObject *parent)
817 \fn template <typename Range, QRangeModelDetails::if_tree_range<Range>> QRangeModel::QRangeModel(Range &&range, QObject *parent)
818 \fn template <typename Range, typename Protocol, QRangeModelDetails::if_tree_range<Range, Protocol>> QRangeModel::QRangeModel(Range &&range, Protocol &&protocol, QObject *parent)
819
820 Constructs a QRangeModel instance that operates on the data in \a range.
821 The \a range has to be a sequential range for which the compiler finds
822 \c{begin} and \c{end} overloads through
823 \l{https://en.cppreference.com/w/cpp/language/adl.html}{argument dependent
824 lookup}, or for which \c{std::begin} and \c{std::end} are implemented. If
825 \a protocol is provided, then the model will represent the range as a tree
826 using the protocol implementation. The model instance becomes a child of \a
827 parent.
828
829 The \a range can be a pointer or reference wrapper, in which case mutating
830 model APIs (such as \l{setData()} or \l{insertRow()}) will modify the data
831 in the referenced range instance. If \a range is a value (or moved into the
832 model), then connect to the signals emitted by the model to respond to
833 changes to the data.
834
835 QRangeModel will not access the \a range while being constructed. This
836 makes it legal to pass a pointer or reference to a range object that is not
837 fully constructed yet to this constructor, for example when \l{Subclassing
838 QRangeModel}{subclassing QRangeModel}.
839
840 If the \a range was moved into the model, then the range and all data in it
841 will be destroyed upon destruction of the model.
842
843 \note While the model does not take ownership of the range object otherwise,
844 you must not modify the \a range directly once the model has been constructed
845 and and passed on to a view. Such modifications will not emit signals
846 necessary to keep model users (other models or views) synchronized with the
847 model, resulting in inconsistent results, undefined behavior, and crashes.
848 Use QRangeModelAdapter to safely interact with the underlying range while
849 keeping the model updated.
850
851 \sa QRangeModelAdapter
852*/
853
854/*!
855 Destroys the QRangeModel.
856
857 The range that the model was constructed from is not accessed, and only
858 destroyed if the model was constructed from a moved-in range.
859*/
860QRangeModel::~QRangeModel() = default;
861
862/*!
863 \reimp
864
865 Returns the index of the model item at \a row and \a column in \a parent.
866
867 Passing a valid parent produces an invalid index for models that operate on
868 list and table ranges.
869
870 \sa parent()
871*/
872QModelIndex QRangeModel::index(int row, int column, const QModelIndex &parent) const
873{
874 Q_D(const QRangeModel);
875 return d->impl->call<QRangeModelImplBase::Index>(row, column, parent);
876}
877
878/*!
879 \reimp
880
881 Returns the parent of the item at the \a child index.
882
883 This function always produces an invalid index for models that operate on
884 list and table ranges. For models operation on a tree, this function
885 returns the index for the row item returned by the parent() implementation
886 of the tree traversal protocol.
887
888 \sa index(), hasChildren()
889*/
890QModelIndex QRangeModel::parent(const QModelIndex &child) const
891{
892 Q_D(const QRangeModel);
893 return d->impl->call<QRangeModelImplBase::Parent>(child);
894}
895
896/*!
897 \reimp
898
899 Returns the sibling at \a row and \a column for the item at \a index, or an
900 invalid QModelIndex if there is no sibling at that location.
901
902 This implementation is significantly faster than going through the parent()
903 of the \a index.
904
905 \sa index(), QModelIndex::row(), QModelIndex::column()
906*/
907QModelIndex QRangeModel::sibling(int row, int column, const QModelIndex &index) const
908{
909 Q_D(const QRangeModel);
910 return d->impl->call<QRangeModelImplBase::Sibling>(row, column, index);
911}
912
913/*!
914 \reimp
915
916 Returns the number of rows under the given \a parent. This is the number of
917 items in the root range for an invalid \a parent index.
918
919 If the \a parent index is valid, then this function always returns 0 for
920 models that operate on list and table ranges. For trees, this returns the
921 size of the range returned by the childRows() implementation of the tree
922 traversal protocol.
923
924 \sa columnCount(), insertRows(), hasChildren()
925*/
926int QRangeModel::rowCount(const QModelIndex &parent) const
927{
928 Q_D(const QRangeModel);
929 return d->impl->call<QRangeModelImplBase::RowCount>(parent);
930}
931
932/*!
933 \reimp
934
935 Returns the number of columns of the model. This function returns the same
936 value for all \a parent indexes.
937
938 For models operating on a statically sized row type, this returned value is
939 always the same throughout the lifetime of the model. For models operating
940 on dynamically sized row type, the model returns the number of items in the
941 first row, or 0 if the model has no rows.
942
943 \sa rowCount, insertColumns()
944*/
945int QRangeModel::columnCount(const QModelIndex &parent) const
946{
947 Q_D(const QRangeModel);
948 return d->impl->call<QRangeModelImplBase::ColumnCount>(parent);
949}
950
951/*!
952 \reimp
953
954 Returns the item flags for the given \a index.
955
956 The implementation returns a combination of flags that enables the item
957 (\c ItemIsEnabled) and allows it to be selected (\c ItemIsSelectable). For
958 models operating on a range with mutable data, it also sets the flag
959 that allows the item to be editable (\c ItemIsEditable).
960
961 \sa Qt::ItemFlags
962*/
963Qt::ItemFlags QRangeModel::flags(const QModelIndex &index) const
964{
965 Q_D(const QRangeModel);
966 return d->impl->call<QRangeModelImplBase::Flags>(index);
967}
968
969/*!
970 \reimp
971
972 Returns the data for the given \a role and \a section in the header with
973 the specified \a orientation.
974
975 For horizontal headers, the section number corresponds to the column
976 number. Similarly, for vertical headers, the section number corresponds to
977 the row number.
978
979 For the horizontal header and the Qt::DisplayRole \a role, models that
980 operate on a range that uses an array as the row type return \a section. If
981 the row type is a tuple, then the implementation returns the name of the
982 type at \a section. For rows that are a gadget or QObject type, this
983 function returns the name of the property at the index of \a section.
984
985 For the vertical header, this function always returns the result of the
986 default implementation in QAbstractItemModel.
987
988 \sa Qt::ItemDataRole, setHeaderData(), QHeaderView
989*/
990QVariant QRangeModel::headerData(int section, Qt::Orientation orientation, int role) const
991{
992 Q_D(const QRangeModel);
993 return d->impl->call<QRangeModelImplBase::HeaderData>(section, orientation, role);
994}
995
996/*!
997 \reimp
998*/
999bool QRangeModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &data,
1000 int role)
1001{
1002 return QAbstractItemModel::setHeaderData(section, orientation, data, role);
1003}
1004
1005/*!
1006 \reimp
1007
1008 Returns the data stored under the given \a role for the value in the
1009 range referred to by the \a index.
1010
1011 If the item type for that index is an associative container that maps from
1012 either \c{int}, Qt::ItemDataRole, or QString to a QVariant, then the role
1013 data is looked up in that container and returned.
1014
1015 If the item is a gadget or QObject, then the implementation returns the
1016 value of the item's property matching the \a role entry in the roleNames()
1017 mapping.
1018
1019 Otherwise, the implementation returns a QVariant constructed from the item
1020 via \c{QVariant::fromValue()} for \c{Qt::DisplayRole} or \c{Qt::EditRole}.
1021 For other roles, the implementation returns an \b invalid
1022 (default-constructed) QVariant.
1023
1024 \sa Qt::ItemDataRole, setData(), headerData()
1025*/
1026QVariant QRangeModel::data(const QModelIndex &index, int role) const
1027{
1028 Q_D(const QRangeModel);
1029 return d->impl->call<QRangeModelImplBase::Data>(index, role);
1030}
1031
1032/*!
1033 \reimp
1034
1035 Sets the \a role data for the item at \a index to \a data.
1036
1037 If the item type for that \a index is an associative container that maps
1038 from either \c{int}, Qt::ItemDataRole, or QString to a QVariant, then
1039 \a data is stored in that container for the key specified by \a role.
1040
1041 If the item is a gadget or QObject, then \a data is written to the item's
1042 property matching the \a role entry in the the roleNames() mapping. The
1043 function returns \c{true} if a property was found and if \a data stored a
1044 value that could be converted to the required type, otherwise returns
1045 \c{false}.
1046
1047 Otherwise, this implementation assigns the value in \a data to the item at
1048 the \a index in the range for \c{Qt::DisplayRole} and \c{Qt::EditRole},
1049 and returns \c{true}. For other roles, the implementation returns
1050 \c{false}.
1051
1052//! [read-only-setData]
1053 For models operating on a read-only range, or on a read-only column in
1054 a row type that implements \l{the C++ tuple protocol}, this implementation
1055 returns \c{false} immediately.
1056//! [read-only-setData]
1057*/
1058bool QRangeModel::setData(const QModelIndex &index, const QVariant &data, int role)
1059{
1060 Q_D(QRangeModel);
1061 return d->impl->call<QRangeModelImplBase::SetData>(index, data, role);
1062}
1063
1064/*!
1065 \reimp
1066
1067 Returns a map with values for all predefined roles in the model for the
1068 item at the given \a index.
1069
1070 If the item type for that \a index is an associative container that maps
1071 from either \c{int}, Qt::ItemDataRole, or QString to a QVariant, then the
1072 data from that container is returned.
1073
1074 If the item type is a gadget or QObject subclass, then the values of those
1075 properties that match a \l{roleNames()}{role name} are returned.
1076
1077 If the item is not an associative container, gadget, or QObject subclass,
1078 then this calls the base class implementation.
1079
1080 \sa setItemData(), Qt::ItemDataRole, data()
1081*/
1082QMap<int, QVariant> QRangeModel::itemData(const QModelIndex &index) const
1083{
1084 Q_D(const QRangeModel);
1085 return d->impl->call<QRangeModelImplBase::ItemData>(index);
1086}
1087
1088/*!
1089 \reimp
1090
1091 If the item type for that \a index is an associative container that maps
1092 from either \c{int} or Qt::ItemDataRole to a QVariant, then the entries in
1093 \a data are stored in that container. If the associative container maps from
1094 QString to QVariant, then only those values in \a data are stored for which
1095 there is a mapping in the \l{roleNames()}{role names} table.
1096
1097 If the item type is a gadget or QObject subclass, then those properties that
1098 match a \l{roleNames()}{role name} are set to the corresponding value in
1099 \a data.
1100
1101 Roles for which there is no entry in \a data are not modified.
1102
1103 For item types that can be copied, this implementation is transactional,
1104 and returns true if all the entries from \a data could be stored. If any
1105 entry could not be updated, then the original container is not modified at
1106 all, and the function returns false.
1107
1108 If the item is not an associative container, gadget, or QObject subclass,
1109 then this calls the base class implementation, which calls setData() for
1110 each entry in \a data.
1111
1112 \sa itemData(), setData(), Qt::ItemDataRole
1113*/
1114bool QRangeModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)
1115{
1116 Q_D(QRangeModel);
1117 return d->impl->call<QRangeModelImplBase::SetItemData>(index, data);
1118}
1119
1120/*!
1121 \reimp
1122
1123 Replaces the value stored in the range at \a index with a default-
1124 constructed value.
1125
1126 \include qrangemodel.cpp read-only-setData
1127*/
1128bool QRangeModel::clearItemData(const QModelIndex &index)
1129{
1130 Q_D(QRangeModel);
1131 return d->impl->call<QRangeModelImplBase::ClearItemData>(index);
1132}
1133
1134/*
1135//! [column-change-requirement]
1136 \note A dynamically sized row type needs to provide a \c{\1} member function.
1137
1138 For models operating on a read-only range, or on a range with a
1139 statically sized row type (such as a tuple, array, or struct), this
1140 implementation does nothing and returns \c{false} immediately. This is
1141 always the case for tree models.
1142//! [column-change-requirement]
1143*/
1144
1145/*!
1146 \reimp
1147
1148 Inserts \a count empty columns before the item at \a column in all rows
1149 of the range at \a parent. Returns \c{true} if successful; otherwise
1150 returns \c{false}.
1151
1152 \include qrangemodel.cpp {column-change-requirement} {insert(const_iterator, size_t, value_type)}
1153*/
1154bool QRangeModel::insertColumns(int column, int count, const QModelIndex &parent)
1155{
1156 Q_D(QRangeModel);
1157 return d->impl->call<QRangeModelImplBase::InsertColumns>(column, count, parent);
1158}
1159
1160/*!
1161 \reimp
1162
1163 Removes \a count columns from the item at \a column on in all rows of the
1164 range at \a parent. Returns \c{true} if successful, otherwise returns
1165 \c{false}.
1166
1167 \include qrangemodel.cpp {column-change-requirement} {erase(const_iterator, size_t)}
1168*/
1169bool QRangeModel::removeColumns(int column, int count, const QModelIndex &parent)
1170{
1171 Q_D(QRangeModel);
1172 return d->impl->call<QRangeModelImplBase::RemoveColumns>(column, count, parent);
1173}
1174
1175/*!
1176 \reimp
1177
1178 Moves \a count columns starting with the given \a sourceColumn under parent
1179 \a sourceParent to column \a destinationColumn under parent \a destinationParent.
1180
1181 Returns \c{true} if the columns were successfully moved; otherwise returns
1182 \c{false}.
1183*/
1184bool QRangeModel::moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count,
1185 const QModelIndex &destinationParent, int destinationColumn)
1186{
1187 Q_D(QRangeModel);
1188 return d->impl->call<QRangeModelImplBase::MoveColumns>(
1189 sourceParent, sourceColumn, count,
1190 destinationParent, destinationColumn);
1191}
1192
1193/*
1194//! [row-change-requirement]
1195 \note The range needs to be dynamically sized and provide a \c{\1}
1196 member function.
1197
1198 For models operating on a read-only or statically-sized range (such as
1199 an array), this implementation does nothing and returns \c{false}
1200 immediately.
1201//! [row-change-requirement]
1202*/
1203
1204/*!
1205 \reimp
1206
1207 Inserts \a count empty rows before the given \a row into the range at
1208 \a parent. Returns \c{true} if successful; otherwise returns \c{false}.
1209
1210 \include qrangemodel.cpp {row-change-requirement} {insert(const_iterator, size_t, value_type)}
1211
1212 \note For ranges with a dynamically sized column type, the column needs
1213 to provide a \c{resize(size_t)} member function.
1214*/
1215bool QRangeModel::insertRows(int row, int count, const QModelIndex &parent)
1216{
1217 Q_D(QRangeModel);
1218 return d->impl->call<QRangeModelImplBase::InsertRows>(row, count, parent);
1219}
1220
1221/*!
1222 \reimp
1223
1224 Removes \a count rows from the range at \a parent, starting with the
1225 given \a row. Returns \c{true} if successful, otherwise returns \c{false}.
1226
1227 \include qrangemodel.cpp {row-change-requirement} {erase(const_iterator, size_t)}
1228*/
1229bool QRangeModel::removeRows(int row, int count, const QModelIndex &parent)
1230{
1231 Q_D(QRangeModel);
1232 return d->impl->call<QRangeModelImplBase::RemoveRows>(row, count, parent);
1233}
1234
1235/*!
1236 \reimp
1237
1238 Moves \a count rows starting with the given \a sourceRow under parent
1239 \a sourceParent to row \a destinationRow under parent \a destinationParent.
1240
1241 Returns \c{true} if the rows were successfully moved; otherwise returns
1242 \c{false}.
1243*/
1244bool QRangeModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
1245 const QModelIndex &destinationParent, int destinationRow)
1246{
1247 Q_D(QRangeModel);
1248 return d->impl->call<QRangeModelImplBase::MoveRows>(
1249 sourceParent, sourceRow, count,
1250 destinationParent, destinationRow);
1251}
1252
1253/*!
1254 \reimp
1255*/
1256bool QRangeModel::canFetchMore(const QModelIndex &parent) const
1257{
1258 return QAbstractItemModel::canFetchMore(parent);
1259}
1260
1261/*!
1262 \reimp
1263*/
1264void QRangeModel::fetchMore(const QModelIndex &parent)
1265{
1266 QAbstractItemModel::fetchMore(parent);
1267}
1268
1269/*!
1270 \reimp
1271*/
1272bool QRangeModel::hasChildren(const QModelIndex &parent) const
1273{
1274 return QAbstractItemModel::hasChildren(parent);
1275}
1276
1277/*!
1278 \reimp
1279*/
1280QModelIndex QRangeModel::buddy(const QModelIndex &index) const
1281{
1282 return QAbstractItemModel::buddy(index);
1283}
1284
1285/*!
1286 \reimp
1287*/
1288bool QRangeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
1289 int row, int column, const QModelIndex &parent) const
1290{
1291 return QAbstractItemModel::canDropMimeData(data, action, row, column, parent);
1292}
1293
1294/*!
1295 \reimp
1296*/
1297bool QRangeModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
1298 int row, int column, const QModelIndex &parent)
1299{
1300 return QAbstractItemModel::dropMimeData(data, action, row, column, parent);
1301}
1302
1303/*!
1304 \reimp
1305*/
1306QMimeData *QRangeModel::mimeData(const QModelIndexList &indexes) const
1307{
1308 return QAbstractItemModel::mimeData(indexes);
1309}
1310
1311/*!
1312 \reimp
1313*/
1314QStringList QRangeModel::mimeTypes() const
1315{
1316 return QAbstractItemModel::mimeTypes();
1317}
1318
1319/*!
1320 \reimp
1321*/
1322QModelIndexList QRangeModel::match(const QModelIndex &start, int role, const QVariant &value,
1323 int hits, Qt::MatchFlags flags) const
1324{
1325 return QAbstractItemModel::match(start, role, value, hits, flags);
1326}
1327
1328/*!
1329 \reimp
1330*/
1331void QRangeModel::multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
1332{
1333 Q_D(const QRangeModel);
1334 d->impl->call<QRangeModelImplBase::MultiData>(index, roleDataSpan);
1335}
1336
1337
1338/*!
1339 \property QRangeModel::roleNames
1340 \brief the role names for the model.
1341
1342 If all columns in the range are of the same type, and if that type provides
1343 a meta object (i.e., it is a gadget, or a QObject subclass), then this
1344 property holds the names of the properties of that type, mapped to values of
1345 Qt::ItemDataRole values from Qt::UserRole and up. In addition, a role
1346 "modelData" provides access to the gadget or QObject instance.
1347
1348 Override this default behavior by setting this property explicitly to a non-
1349 empty mapping. Setting this property to an empty mapping, or using
1350 resetRoleNames(), restores the default behavior.
1351
1352 \sa QAbstractItemModel::roleNames()
1353*/
1354
1355QHash<int, QByteArray> QRangeModelImplBase::roleNamesForMetaObject(const QAbstractItemModel &model,
1356 const QMetaObject &metaObject)
1357{
1358 const auto defaults = model.QAbstractItemModel::roleNames();
1359 QHash<int, QByteArray> result = {{Qt::RangeModelDataRole, "modelData"}};
1360 int offset = metaObject.propertyOffset();
1361 for (int i = offset; i < metaObject.propertyCount(); ++i) {
1362 const auto name = metaObject.property(i).name();
1363 const int defaultRole = defaults.key(name, -1);
1364 if (defaultRole != -1) {
1365 ++offset;
1366 result[defaultRole] = name;
1367 } else {
1368 result[Qt::UserRole + i - offset] = name;
1369 }
1370 }
1371 return result;
1372}
1373
1374QHash<int, QByteArray> QRangeModelImplBase::roleNamesForSimpleType()
1375{
1376 // just a plain value
1377 return QHash<int, QByteArray>{
1378 {Qt::DisplayRole, "display"},
1379 {Qt::EditRole, "edit"},
1380 {Qt::RangeModelDataRole, "modelData"},
1381 };
1382}
1383
1384/*!
1385 \reimp
1386
1387 \note Overriding this function in a QRangeModel subclass is possible,
1388 but might break the behavior of the property.
1389*/
1390QHash<int, QByteArray> QRangeModel::roleNames() const
1391{
1392 Q_D(const QRangeModel);
1393 if (d->m_roleNames.isEmpty())
1394 d->m_roleNames = d->impl->call<QRangeModelImplBase::RoleNames>();
1395
1396 return d->m_roleNames;
1397}
1398
1399void QRangeModel::setRoleNames(const QHash<int, QByteArray> &names)
1400{
1401 Q_D(QRangeModel);
1402 if (d->m_roleNames == names)
1403 return;
1404 beginResetModel();
1405 d->impl->call<QRangeModelImplBase::InvalidateCaches>();
1406 if (d->m_autoConnectPolicy != AutoConnectPolicy::None)
1407 d->impl->call<QRangeModelImplBase::SetAutoConnectPolicy>();
1408
1409 d->m_roleNames = names;
1410 endResetModel();
1411 Q_EMIT roleNamesChanged();
1412}
1413
1414void QRangeModel::resetRoleNames()
1415{
1416 setRoleNames({});
1417}
1418
1419/*!
1420 \enum QRangeModel::AutoConnectPolicy
1421 \since 6.11
1422
1423 This enum defines if and when QRangeModel auto-connects changed-signals for
1424 properties to the \l{QAbstractItemModel::}{dataChanged()} signal of the
1425 model. Only properties that match one of the \l{roleNames()}{role names}
1426 are connected.
1427
1428 \value None No connections are made automatically.
1429 \value Full The signals for all relevant properties are connected
1430 automatically, for all QObject items. This includes QObject
1431 items that are added to newly inserted rows and columns.
1432 \value OnRead Signals for relevant properties are connected the first time
1433 the model reads the property.
1434
1435 The memory overhead of making automatic connections can be substantial. A
1436 Full auto-connection does not require any book-keeping in addition to the
1437 connection itself, but each connection takes memory, and connecting all
1438 properties of all objects can be very costly, especially if only a few
1439 properties of a subset of objects will ever change.
1440
1441 The OnRead connection policy will not connect to objects or properties that
1442 are never read from (for instance, never rendered in a view), but remembering
1443 which connections have been made requires some book-keeping overhead, and
1444 unpredictable memory growth over time. For instance, scrolling down a long
1445 list of items can easily result in thousands of new connections being made.
1446
1447 \sa autoConnectPolicy, roleNames()
1448*/
1449
1450/*!
1451 \property QRangeModel::autoConnectPolicy
1452 \since 6.11
1453 \brief if and when the model auto-connects to property changed notifications.
1454
1455 If QRangeModel operates on a data structure that holds the same type of
1456 QObject subclass as its row or item type, then it can automatically connect
1457 the properties of the QObjects to the dataChanged() signal. For QObject
1458 rows, this is done for each column, mapping to the Qt::DisplayRole
1459 property. For items, this is done for those properties that match one of
1460 the \l{roleNames()}{role names}.
1461
1462 By default, the value of this property is \l{QRangeModel::AutoConnectPolicy::}
1463 {None}, so no such connections are made. Changing the value of this property
1464 always breaks all existing connections.
1465
1466 \note Connections are not broken or created if QObjects in the data
1467 structure that QRangeModel operates on are swapped out.
1468
1469 \sa roleNames()
1470*/
1471
1472QRangeModel::AutoConnectPolicy QRangeModel::autoConnectPolicy() const
1473{
1474 Q_D(const QRangeModel);
1475 return d->m_autoConnectPolicy;
1476}
1477
1478void QRangeModel::setAutoConnectPolicy(QRangeModel::AutoConnectPolicy policy)
1479{
1480 Q_D(QRangeModel);
1481 if (d->m_autoConnectPolicy == policy)
1482 return;
1483
1484 d->m_autoConnectPolicy = policy;
1485 d->impl->call<QRangeModelImplBase::SetAutoConnectPolicy>();
1486 Q_EMIT autoConnectPolicyChanged(policy);
1487}
1488
1489/*!
1490 \reimp
1491*/
1492void QRangeModel::sort(int column, Qt::SortOrder order)
1493{
1494 return QAbstractItemModel::sort(column, order);
1495}
1496
1497/*!
1498 \reimp
1499*/
1500QSize QRangeModel::span(const QModelIndex &index) const
1501{
1502 return QAbstractItemModel::span(index);
1503}
1504
1505/*!
1506 \reimp
1507*/
1508Qt::DropActions QRangeModel::supportedDragActions() const
1509{
1510 return QAbstractItemModel::supportedDragActions();
1511}
1512
1513/*!
1514 \reimp
1515*/
1516Qt::DropActions QRangeModel::supportedDropActions() const
1517{
1518 return QAbstractItemModel::supportedDropActions();
1519}
1520
1521/*!
1522 \reimp
1523*/
1524void QRangeModel::resetInternalData()
1525{
1526 QAbstractItemModel::resetInternalData();
1527}
1528
1529/*!
1530 \reimp
1531*/
1532bool QRangeModel::event(QEvent *event)
1533{
1534 return QAbstractItemModel::event(event);
1535}
1536
1537/*!
1538 \reimp
1539*/
1540bool QRangeModel::eventFilter(QObject *object, QEvent *event)
1541{
1542 return QAbstractItemModel::eventFilter(object, event);
1543}
1544
1545QT_END_NAMESPACE
1546
1547#include "moc_qrangemodel.cpp"
static bool connectPropertiesHelper(const QModelIndex &index, const QObject *item, QRangeModelDetails::AutoConnectContext *context, const QHash< int, QMetaProperty > &properties)
~ConstPropertyChangedHandler()=default
ConstPropertyChangedHandler(ConstPropertyChangedHandler &&other) noexcept=default
ConstPropertyChangedHandler(const QModelIndex &index, int role)
PropertyChangedHandler & operator=(PropertyChangedHandler &&)=delete
PropertyChangedHandler(PropertyChangedHandler &&other) noexcept
PropertyChangedHandler(const PropertyChangedHandler &)=delete
PropertyChangedHandler & operator=(const PropertyChangedHandler &)=delete
~PropertyChangedHandler()=default
PropertyChangedHandler(const QPersistentModelIndex &index, int role)
PropertyChangedHandler & operator=(QMetaObject::Connection &&connection)