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