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=(const QMetaObject::Connection &connection) noexcept
67 {
68 Q_ASSERT(std::holds_alternative<PropertyChangedHandler *>(storage));
69 std::get<PropertyChangedHandler *>(storage)->connection = 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 = 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 Values
727 \row
728 \li static constexpr RowCategory rowCategory
729 \li RowCategory
730 \endtable
731
732 \snippet qrangemodel/main.cpp color_gadget_decl
733 \dots
734 \snippet qrangemodel/main.cpp color_gadget_end
735 \snippet qrangemodel/main.cpp color_gadget_multi_role_gadget
736
737*/
738
739/*!
740 \enum QRangeModel::RowCategory
741
742 This enum describes how QRangeModel should present the elements of the
743 range it was constructed with.
744
745 \value Default
746 QRangeModel decides how to present the rows.
747 \value MultiRoleItem
748 QRangeModel will present items with a meta object as multi-role
749 items, also when used in a one-dimensional range.
750
751 Specialize the RowOptions template for your type, and add a public member
752 variable \c{static constexpr auto rowCategory} with one of the values from
753 this enum.
754
755 \sa RowOptions
756*/
757
758/*!
759 \class QRangeModel::ItemAccess
760 \inmodule QtCore
761 \ingroup model-view
762 \brief The ItemAccess template provides a customization point to control
763 how QRangeModel accesses role data of individual items.
764 \since 6.11
765
766 ItemAccess<T> is a struct template where \a T specifies the item type.
767 Specialize this template for the type used in your data structure, and
768 implement \c{readRole()} and \c{writeRole()} members to access the role-
769 specific data of your type.
770
771 \code
772 template <>
773 struct QRangeModel::ItemAccess<ItemType>
774 {
775 static QVariant readRole(const ItemType &item, int role)
776 {
777 switch (role) {
778 // ...
779 }
780 return {};
781 }
782
783 static bool writeRole(ItemType &item, const QVariant &data, int role)
784 {
785 bool ok = false;
786 switch (role) {
787 // ...
788 }
789
790 return ok;
791 }
792 };
793 \endcode
794
795 A specialization of this type will take precedence over any predefined
796 behavior. Do not specialize this template for types you do not own. Types
797 for which ItemAccess is specialized are implicitly interpreted as
798 \l{RowCategory}{multi-role items}.
799*/
800
801/*!
802 \fn template <typename Range, QRangeModelDetails::if_table_range<Range>> QRangeModel::QRangeModel(Range &&range, QObject *parent)
803 \fn template <typename Range, QRangeModelDetails::if_tree_range<Range>> QRangeModel::QRangeModel(Range &&range, QObject *parent)
804 \fn template <typename Range, typename Protocol, QRangeModelDetails::if_tree_range<Range, Protocol>> QRangeModel::QRangeModel(Range &&range, Protocol &&protocol, QObject *parent)
805
806 Constructs a QRangeModel instance that operates on the data in \a range.
807 The \a range has to be a sequential range for which the compiler finds
808 \c{begin} and \c{end} overloads through
809 \l{https://en.cppreference.com/w/cpp/language/adl.html}{argument dependent
810 lookup}, or for which \c{std::begin} and \c{std::end} are implemented. If
811 \a protocol is provided, then the model will represent the range as a tree
812 using the protocol implementation. The model instance becomes a child of \a
813 parent.
814
815 The \a range can be a pointer or reference wrapper, in which case mutating
816 model APIs (such as \l{setData()} or \l{insertRow()}) will modify the data
817 in the referenced range instance. If \a range is a value (or moved into the
818 model), then connect to the signals emitted by the model to respond to
819 changes to the data.
820
821 QRangeModel will not access the \a range while being constructed. This
822 makes it legal to pass a pointer or reference to a range object that is not
823 fully constructed yet to this constructor, for example when \l{Subclassing
824 QRangeModel}{subclassing QRangeModel}.
825
826 If the \a range was moved into the model, then the range and all data in it
827 will be destroyed upon destruction of the model.
828
829 \note While the model does not take ownership of the range object otherwise,
830 you must not modify the \a range directly once the model has been constructed
831 and and passed on to a view. Such modifications will not emit signals
832 necessary to keep model users (other models or views) synchronized with the
833 model, resulting in inconsistent results, undefined behavior, and crashes.
834 Use QRangeModelAdapter to safely interact with the underlying range while
835 keeping the model updated.
836
837 \sa QRangeModelAdapter
838*/
839
840/*!
841 Destroys the QRangeModel.
842
843 The range that the model was constructed from is not accessed, and only
844 destroyed if the model was constructed from a moved-in range.
845*/
846QRangeModel::~QRangeModel() = default;
847
848/*!
849 \reimp
850
851 Returns the index of the model item at \a row and \a column in \a parent.
852
853 Passing a valid parent produces an invalid index for models that operate on
854 list and table ranges.
855
856 \sa parent()
857*/
858QModelIndex QRangeModel::index(int row, int column, const QModelIndex &parent) const
859{
860 Q_D(const QRangeModel);
861 return d->impl->call<QRangeModelImplBase::Index>(row, column, parent);
862}
863
864/*!
865 \reimp
866
867 Returns the parent of the item at the \a child index.
868
869 This function always produces an invalid index for models that operate on
870 list and table ranges. For models operation on a tree, this function
871 returns the index for the row item returned by the parent() implementation
872 of the tree traversal protocol.
873
874 \sa index(), hasChildren()
875*/
876QModelIndex QRangeModel::parent(const QModelIndex &child) const
877{
878 Q_D(const QRangeModel);
879 return d->impl->call<QRangeModelImplBase::Parent>(child);
880}
881
882/*!
883 \reimp
884
885 Returns the sibling at \a row and \a column for the item at \a index, or an
886 invalid QModelIndex if there is no sibling at that location.
887
888 This implementation is significantly faster than going through the parent()
889 of the \a index.
890
891 \sa index(), QModelIndex::row(), QModelIndex::column()
892*/
893QModelIndex QRangeModel::sibling(int row, int column, const QModelIndex &index) const
894{
895 Q_D(const QRangeModel);
896 return d->impl->call<QRangeModelImplBase::Sibling>(row, column, index);
897}
898
899/*!
900 \reimp
901
902 Returns the number of rows under the given \a parent. This is the number of
903 items in the root range for an invalid \a parent index.
904
905 If the \a parent index is valid, then this function always returns 0 for
906 models that operate on list and table ranges. For trees, this returns the
907 size of the range returned by the childRows() implementation of the tree
908 traversal protocol.
909
910 \sa columnCount(), insertRows(), hasChildren()
911*/
912int QRangeModel::rowCount(const QModelIndex &parent) const
913{
914 Q_D(const QRangeModel);
915 return d->impl->call<QRangeModelImplBase::RowCount>(parent);
916}
917
918/*!
919 \reimp
920
921 Returns the number of columns of the model. This function returns the same
922 value for all \a parent indexes.
923
924 For models operating on a statically sized row type, this returned value is
925 always the same throughout the lifetime of the model. For models operating
926 on dynamically sized row type, the model returns the number of items in the
927 first row, or 0 if the model has no rows.
928
929 \sa rowCount, insertColumns()
930*/
931int QRangeModel::columnCount(const QModelIndex &parent) const
932{
933 Q_D(const QRangeModel);
934 return d->impl->call<QRangeModelImplBase::ColumnCount>(parent);
935}
936
937/*!
938 \reimp
939
940 Returns the item flags for the given \a index.
941
942 The implementation returns a combination of flags that enables the item
943 (\c ItemIsEnabled) and allows it to be selected (\c ItemIsSelectable). For
944 models operating on a range with mutable data, it also sets the flag
945 that allows the item to be editable (\c ItemIsEditable).
946
947 \sa Qt::ItemFlags
948*/
949Qt::ItemFlags QRangeModel::flags(const QModelIndex &index) const
950{
951 Q_D(const QRangeModel);
952 return d->impl->call<QRangeModelImplBase::Flags>(index);
953}
954
955/*!
956 \reimp
957
958 Returns the data for the given \a role and \a section in the header with
959 the specified \a orientation.
960
961 For horizontal headers, the section number corresponds to the column
962 number. Similarly, for vertical headers, the section number corresponds to
963 the row number.
964
965 For the horizontal header and the Qt::DisplayRole \a role, models that
966 operate on a range that uses an array as the row type return \a section. If
967 the row type is a tuple, then the implementation returns the name of the
968 type at \a section. For rows that are a gadget or QObject type, this
969 function returns the name of the property at the index of \a section.
970
971 For the vertical header, this function always returns the result of the
972 default implementation in QAbstractItemModel.
973
974 \sa Qt::ItemDataRole, setHeaderData(), QHeaderView
975*/
976QVariant QRangeModel::headerData(int section, Qt::Orientation orientation, int role) const
977{
978 Q_D(const QRangeModel);
979 return d->impl->call<QRangeModelImplBase::HeaderData>(section, orientation, role);
980}
981
982/*!
983 \reimp
984*/
985bool QRangeModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &data,
986 int role)
987{
988 return QAbstractItemModel::setHeaderData(section, orientation, data, role);
989}
990
991/*!
992 \reimp
993
994 Returns the data stored under the given \a role for the value in the
995 range referred to by the \a index.
996
997 If the item type for that index is an associative container that maps from
998 either \c{int}, Qt::ItemDataRole, or QString to a QVariant, then the role
999 data is looked up in that container and returned.
1000
1001 If the item is a gadget or QObject, then the implementation returns the
1002 value of the item's property matching the \a role entry in the roleNames()
1003 mapping.
1004
1005 Otherwise, the implementation returns a QVariant constructed from the item
1006 via \c{QVariant::fromValue()} for \c{Qt::DisplayRole} or \c{Qt::EditRole}.
1007 For other roles, the implementation returns an \b invalid
1008 (default-constructed) QVariant.
1009
1010 \sa Qt::ItemDataRole, setData(), headerData()
1011*/
1012QVariant QRangeModel::data(const QModelIndex &index, int role) const
1013{
1014 Q_D(const QRangeModel);
1015 return d->impl->call<QRangeModelImplBase::Data>(index, role);
1016}
1017
1018/*!
1019 \reimp
1020
1021 Sets the \a role data for the item at \a index to \a data.
1022
1023 If the item type for that \a index is an associative container that maps
1024 from either \c{int}, Qt::ItemDataRole, or QString to a QVariant, then
1025 \a data is stored in that container for the key specified by \a role.
1026
1027 If the item is a gadget or QObject, then \a data is written to the item's
1028 property matching the \a role entry in the the roleNames() mapping. The
1029 function returns \c{true} if a property was found and if \a data stored a
1030 value that could be converted to the required type, otherwise returns
1031 \c{false}.
1032
1033 Otherwise, this implementation assigns the value in \a data to the item at
1034 the \a index in the range for \c{Qt::DisplayRole} and \c{Qt::EditRole},
1035 and returns \c{true}. For other roles, the implementation returns
1036 \c{false}.
1037
1038//! [read-only-setData]
1039 For models operating on a read-only range, or on a read-only column in
1040 a row type that implements \l{the C++ tuple protocol}, this implementation
1041 returns \c{false} immediately.
1042//! [read-only-setData]
1043*/
1044bool QRangeModel::setData(const QModelIndex &index, const QVariant &data, int role)
1045{
1046 Q_D(QRangeModel);
1047 return d->impl->call<QRangeModelImplBase::SetData>(index, data, role);
1048}
1049
1050/*!
1051 \reimp
1052
1053 Returns a map with values for all predefined roles in the model for the
1054 item at the given \a index.
1055
1056 If the item type for that \a index is an associative container that maps
1057 from either \c{int}, Qt::ItemDataRole, or QString to a QVariant, then the
1058 data from that container is returned.
1059
1060 If the item type is a gadget or QObject subclass, then the values of those
1061 properties that match a \l{roleNames()}{role name} are returned.
1062
1063 If the item is not an associative container, gadget, or QObject subclass,
1064 then this calls the base class implementation.
1065
1066 \sa setItemData(), Qt::ItemDataRole, data()
1067*/
1068QMap<int, QVariant> QRangeModel::itemData(const QModelIndex &index) const
1069{
1070 Q_D(const QRangeModel);
1071 return d->impl->call<QRangeModelImplBase::ItemData>(index);
1072}
1073
1074/*!
1075 \reimp
1076
1077 If the item type for that \a index is an associative container that maps
1078 from either \c{int} or Qt::ItemDataRole to a QVariant, then the entries in
1079 \a data are stored in that container. If the associative container maps from
1080 QString to QVariant, then only those values in \a data are stored for which
1081 there is a mapping in the \l{roleNames()}{role names} table.
1082
1083 If the item type is a gadget or QObject subclass, then those properties that
1084 match a \l{roleNames()}{role name} are set to the corresponding value in
1085 \a data.
1086
1087 Roles for which there is no entry in \a data are not modified.
1088
1089 For item types that can be copied, this implementation is transactional,
1090 and returns true if all the entries from \a data could be stored. If any
1091 entry could not be updated, then the original container is not modified at
1092 all, and the function returns false.
1093
1094 If the item is not an associative container, gadget, or QObject subclass,
1095 then this calls the base class implementation, which calls setData() for
1096 each entry in \a data.
1097
1098 \sa itemData(), setData(), Qt::ItemDataRole
1099*/
1100bool QRangeModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)
1101{
1102 Q_D(QRangeModel);
1103 return d->impl->call<QRangeModelImplBase::SetItemData>(index, data);
1104}
1105
1106/*!
1107 \reimp
1108
1109 Replaces the value stored in the range at \a index with a default-
1110 constructed value.
1111
1112 \include qrangemodel.cpp read-only-setData
1113*/
1114bool QRangeModel::clearItemData(const QModelIndex &index)
1115{
1116 Q_D(QRangeModel);
1117 return d->impl->call<QRangeModelImplBase::ClearItemData>(index);
1118}
1119
1120/*
1121//! [column-change-requirement]
1122 \note A dynamically sized row type needs to provide a \c{\1} member function.
1123
1124 For models operating on a read-only range, or on a range with a
1125 statically sized row type (such as a tuple, array, or struct), this
1126 implementation does nothing and returns \c{false} immediately. This is
1127 always the case for tree models.
1128//! [column-change-requirement]
1129*/
1130
1131/*!
1132 \reimp
1133
1134 Inserts \a count empty columns before the item at \a column in all rows
1135 of the range at \a parent. Returns \c{true} if successful; otherwise
1136 returns \c{false}.
1137
1138 \include qrangemodel.cpp {column-change-requirement} {insert(const_iterator, size_t, value_type)}
1139*/
1140bool QRangeModel::insertColumns(int column, int count, const QModelIndex &parent)
1141{
1142 Q_D(QRangeModel);
1143 return d->impl->call<QRangeModelImplBase::InsertColumns>(column, count, parent);
1144}
1145
1146/*!
1147 \reimp
1148
1149 Removes \a count columns from the item at \a column on in all rows of the
1150 range at \a parent. Returns \c{true} if successful, otherwise returns
1151 \c{false}.
1152
1153 \include qrangemodel.cpp {column-change-requirement} {erase(const_iterator, size_t)}
1154*/
1155bool QRangeModel::removeColumns(int column, int count, const QModelIndex &parent)
1156{
1157 Q_D(QRangeModel);
1158 return d->impl->call<QRangeModelImplBase::RemoveColumns>(column, count, parent);
1159}
1160
1161/*!
1162 \reimp
1163
1164 Moves \a count columns starting with the given \a sourceColumn under parent
1165 \a sourceParent to column \a destinationColumn under parent \a destinationParent.
1166
1167 Returns \c{true} if the columns were successfully moved; otherwise returns
1168 \c{false}.
1169*/
1170bool QRangeModel::moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count,
1171 const QModelIndex &destinationParent, int destinationColumn)
1172{
1173 Q_D(QRangeModel);
1174 return d->impl->call<QRangeModelImplBase::MoveColumns>(
1175 sourceParent, sourceColumn, count,
1176 destinationParent, destinationColumn);
1177}
1178
1179/*
1180//! [row-change-requirement]
1181 \note The range needs to be dynamically sized and provide a \c{\1}
1182 member function.
1183
1184 For models operating on a read-only or statically-sized range (such as
1185 an array), this implementation does nothing and returns \c{false}
1186 immediately.
1187//! [row-change-requirement]
1188*/
1189
1190/*!
1191 \reimp
1192
1193 Inserts \a count empty rows before the given \a row into the range at
1194 \a parent. Returns \c{true} if successful; otherwise returns \c{false}.
1195
1196 \include qrangemodel.cpp {row-change-requirement} {insert(const_iterator, size_t, value_type)}
1197
1198 \note For ranges with a dynamically sized column type, the column needs
1199 to provide a \c{resize(size_t)} member function.
1200*/
1201bool QRangeModel::insertRows(int row, int count, const QModelIndex &parent)
1202{
1203 Q_D(QRangeModel);
1204 return d->impl->call<QRangeModelImplBase::InsertRows>(row, count, parent);
1205}
1206
1207/*!
1208 \reimp
1209
1210 Removes \a count rows from the range at \a parent, starting with the
1211 given \a row. Returns \c{true} if successful, otherwise returns \c{false}.
1212
1213 \include qrangemodel.cpp {row-change-requirement} {erase(const_iterator, size_t)}
1214*/
1215bool QRangeModel::removeRows(int row, int count, const QModelIndex &parent)
1216{
1217 Q_D(QRangeModel);
1218 return d->impl->call<QRangeModelImplBase::RemoveRows>(row, count, parent);
1219}
1220
1221/*!
1222 \reimp
1223
1224 Moves \a count rows starting with the given \a sourceRow under parent
1225 \a sourceParent to row \a destinationRow under parent \a destinationParent.
1226
1227 Returns \c{true} if the rows were successfully moved; otherwise returns
1228 \c{false}.
1229*/
1230bool QRangeModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
1231 const QModelIndex &destinationParent, int destinationRow)
1232{
1233 Q_D(QRangeModel);
1234 return d->impl->call<QRangeModelImplBase::MoveRows>(
1235 sourceParent, sourceRow, count,
1236 destinationParent, destinationRow);
1237}
1238
1239/*!
1240 \reimp
1241*/
1242bool QRangeModel::canFetchMore(const QModelIndex &parent) const
1243{
1244 return QAbstractItemModel::canFetchMore(parent);
1245}
1246
1247/*!
1248 \reimp
1249*/
1250void QRangeModel::fetchMore(const QModelIndex &parent)
1251{
1252 QAbstractItemModel::fetchMore(parent);
1253}
1254
1255/*!
1256 \reimp
1257*/
1258bool QRangeModel::hasChildren(const QModelIndex &parent) const
1259{
1260 return QAbstractItemModel::hasChildren(parent);
1261}
1262
1263/*!
1264 \reimp
1265*/
1266QModelIndex QRangeModel::buddy(const QModelIndex &index) const
1267{
1268 return QAbstractItemModel::buddy(index);
1269}
1270
1271/*!
1272 \reimp
1273*/
1274bool QRangeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
1275 int row, int column, const QModelIndex &parent) const
1276{
1277 return QAbstractItemModel::canDropMimeData(data, action, row, column, parent);
1278}
1279
1280/*!
1281 \reimp
1282*/
1283bool QRangeModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
1284 int row, int column, const QModelIndex &parent)
1285{
1286 return QAbstractItemModel::dropMimeData(data, action, row, column, parent);
1287}
1288
1289/*!
1290 \reimp
1291*/
1292QMimeData *QRangeModel::mimeData(const QModelIndexList &indexes) const
1293{
1294 return QAbstractItemModel::mimeData(indexes);
1295}
1296
1297/*!
1298 \reimp
1299*/
1300QStringList QRangeModel::mimeTypes() const
1301{
1302 return QAbstractItemModel::mimeTypes();
1303}
1304
1305/*!
1306 \reimp
1307*/
1308QModelIndexList QRangeModel::match(const QModelIndex &start, int role, const QVariant &value,
1309 int hits, Qt::MatchFlags flags) const
1310{
1311 return QAbstractItemModel::match(start, role, value, hits, flags);
1312}
1313
1314/*!
1315 \reimp
1316*/
1317void QRangeModel::multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
1318{
1319 Q_D(const QRangeModel);
1320 d->impl->call<QRangeModelImplBase::MultiData>(index, roleDataSpan);
1321}
1322
1323
1324/*!
1325 \property QRangeModel::roleNames
1326 \brief the role names for the model.
1327
1328 If all columns in the range are of the same type, and if that type provides
1329 a meta object (i.e., it is a gadget, or a QObject subclass), then this
1330 property holds the names of the properties of that type, mapped to values of
1331 Qt::ItemDataRole values from Qt::UserRole and up. In addition, a role
1332 "modelData" provides access to the gadget or QObject instance.
1333
1334 Override this default behavior by setting this property explicitly to a non-
1335 empty mapping. Setting this property to an empty mapping, or using
1336 resetRoleNames(), restores the default behavior.
1337
1338 \sa QAbstractItemModel::roleNames()
1339*/
1340
1341QHash<int, QByteArray> QRangeModelImplBase::roleNamesForMetaObject(const QAbstractItemModel &model,
1342 const QMetaObject &metaObject)
1343{
1344 const auto defaults = model.QAbstractItemModel::roleNames();
1345 QHash<int, QByteArray> result = {{Qt::RangeModelDataRole, "modelData"}};
1346 int offset = metaObject.propertyOffset();
1347 for (int i = offset; i < metaObject.propertyCount(); ++i) {
1348 const auto name = metaObject.property(i).name();
1349 const int defaultRole = defaults.key(name, -1);
1350 if (defaultRole != -1) {
1351 ++offset;
1352 result[defaultRole] = name;
1353 } else {
1354 result[Qt::UserRole + i - offset] = name;
1355 }
1356 }
1357 return result;
1358}
1359
1360QHash<int, QByteArray> QRangeModelImplBase::roleNamesForSimpleType()
1361{
1362 // just a plain value
1363 return QHash<int, QByteArray>{
1364 {Qt::DisplayRole, "display"},
1365 {Qt::EditRole, "edit"},
1366 {Qt::RangeModelDataRole, "modelData"},
1367 };
1368}
1369
1370/*!
1371 \reimp
1372
1373 \note Overriding this function in a QRangeModel subclass is possible,
1374 but might break the behavior of the property.
1375*/
1376QHash<int, QByteArray> QRangeModel::roleNames() const
1377{
1378 Q_D(const QRangeModel);
1379 if (d->m_roleNames.isEmpty())
1380 d->m_roleNames = d->impl->call<QRangeModelImplBase::RoleNames>();
1381
1382 return d->m_roleNames;
1383}
1384
1385void QRangeModel::setRoleNames(const QHash<int, QByteArray> &names)
1386{
1387 Q_D(QRangeModel);
1388 if (d->m_roleNames == names)
1389 return;
1390 beginResetModel();
1391 d->impl->call<QRangeModelImplBase::InvalidateCaches>();
1392 if (d->m_autoConnectPolicy != AutoConnectPolicy::None)
1393 d->impl->call<QRangeModelImplBase::SetAutoConnectPolicy>();
1394
1395 d->m_roleNames = names;
1396 endResetModel();
1397 Q_EMIT roleNamesChanged();
1398}
1399
1400void QRangeModel::resetRoleNames()
1401{
1402 setRoleNames({});
1403}
1404
1405/*!
1406 \enum QRangeModel::AutoConnectPolicy
1407 \since 6.11
1408
1409 This enum defines if and when QRangeModel auto-connects changed-signals for
1410 properties to the \l{QAbstractItemModel::}{dataChanged()} signal of the
1411 model. Only properties that match one of the \l{roleNames()}{role names}
1412 are connected.
1413
1414 \value None No connections are made automatically.
1415 \value Full The signals for all relevant properties are connected
1416 automatically, for all QObject items. This includes QObject
1417 items that are added to newly inserted rows and columns.
1418 \value OnRead Signals for relevant properties are connected the first time
1419 the model reads the property.
1420
1421 The memory overhead of making automatic connections can be substantial. A
1422 Full auto-connection does not require any book-keeping in addition to the
1423 connection itself, but each connection takes memory, and connecting all
1424 properties of all objects can be very costly, especially if only a few
1425 properties of a subset of objects will ever change.
1426
1427 The OnRead connection policy will not connect to objects or properties that
1428 are never read from (for instance, never rendered in a view), but remembering
1429 which connections have been made requires some book-keeping overhead, and
1430 unpredictable memory growth over time. For instance, scrolling down a long
1431 list of items can easily result in thousands of new connections being made.
1432
1433 \sa autoConnectPolicy, roleNames()
1434*/
1435
1436/*!
1437 \property QRangeModel::autoConnectPolicy
1438 \since 6.11
1439 \brief if and when the model auto-connects to property changed notifications.
1440
1441 If QRangeModel operates on a data structure that holds the same type of
1442 QObject subclass as its row or item type, then it can automatically connect
1443 the properties of the QObjects to the dataChanged() signal. For QObject
1444 rows, this is done for each column, mapping to the Qt::DisplayRole
1445 property. For items, this is done for those properties that match one of
1446 the \l{roleNames()}{role names}.
1447
1448 By default, the value of this property is \l{QRangeModel::AutoConnectPolicy::}
1449 {None}, so no such connections are made. Changing the value of this property
1450 always breaks all existing connections.
1451
1452 \note Connections are not broken or created if QObjects in the data
1453 structure that QRangeModel operates on are swapped out.
1454
1455 \sa roleNames()
1456*/
1457
1458QRangeModel::AutoConnectPolicy QRangeModel::autoConnectPolicy() const
1459{
1460 Q_D(const QRangeModel);
1461 return d->m_autoConnectPolicy;
1462}
1463
1464void QRangeModel::setAutoConnectPolicy(QRangeModel::AutoConnectPolicy policy)
1465{
1466 Q_D(QRangeModel);
1467 if (d->m_autoConnectPolicy == policy)
1468 return;
1469
1470 d->m_autoConnectPolicy = policy;
1471 d->impl->call<QRangeModelImplBase::SetAutoConnectPolicy>();
1472 Q_EMIT autoConnectPolicyChanged(policy);
1473}
1474
1475/*!
1476 \reimp
1477*/
1478void QRangeModel::sort(int column, Qt::SortOrder order)
1479{
1480 return QAbstractItemModel::sort(column, order);
1481}
1482
1483/*!
1484 \reimp
1485*/
1486QSize QRangeModel::span(const QModelIndex &index) const
1487{
1488 return QAbstractItemModel::span(index);
1489}
1490
1491/*!
1492 \reimp
1493*/
1494Qt::DropActions QRangeModel::supportedDragActions() const
1495{
1496 return QAbstractItemModel::supportedDragActions();
1497}
1498
1499/*!
1500 \reimp
1501*/
1502Qt::DropActions QRangeModel::supportedDropActions() const
1503{
1504 return QAbstractItemModel::supportedDropActions();
1505}
1506
1507/*!
1508 \reimp
1509*/
1510void QRangeModel::resetInternalData()
1511{
1512 QAbstractItemModel::resetInternalData();
1513}
1514
1515/*!
1516 \reimp
1517*/
1518bool QRangeModel::event(QEvent *event)
1519{
1520 return QAbstractItemModel::event(event);
1521}
1522
1523/*!
1524 \reimp
1525*/
1526bool QRangeModel::eventFilter(QObject *object, QEvent *event)
1527{
1528 return QAbstractItemModel::eventFilter(object, event);
1529}
1530
1531QT_END_NAMESPACE
1532
1533#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 & operator=(const QMetaObject::Connection &connection) noexcept
PropertyChangedHandler(const QPersistentModelIndex &index, int role)