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