diff options
Diffstat (limited to 'src/corelib/itemmodels')
| -rw-r--r-- | src/corelib/itemmodels/qrangemodel.cpp | 1 | ||||
| -rw-r--r-- | src/corelib/itemmodels/qrangemodel_impl.h | 227 | ||||
| -rw-r--r-- | src/corelib/itemmodels/qrangemodeladapter.h | 232 | ||||
| -rw-r--r-- | src/corelib/itemmodels/qrangemodeladapter.qdoc | 69 |
4 files changed, 305 insertions, 224 deletions
diff --git a/src/corelib/itemmodels/qrangemodel.cpp b/src/corelib/itemmodels/qrangemodel.cpp index d72722f063d..f37812876ea 100644 --- a/src/corelib/itemmodels/qrangemodel.cpp +++ b/src/corelib/itemmodels/qrangemodel.cpp @@ -1385,6 +1385,7 @@ void QRangeModel::resetRoleNames() /*! \property QRangeModel::autoConnectPolicy + \since 6.11 \brief if and when the model auto-connects to property changed notifications. If QRangeModel operates on a data structure that holds the same type of diff --git a/src/corelib/itemmodels/qrangemodel_impl.h b/src/corelib/itemmodels/qrangemodel_impl.h index 3e35ad3981c..7eca3094a66 100644 --- a/src/corelib/itemmodels/qrangemodel_impl.h +++ b/src/corelib/itemmodels/qrangemodel_impl.h @@ -23,6 +23,7 @@ #include <QtCore/qmap.h> #include <QtCore/qscopedvaluerollback.h> #include <QtCore/qset.h> +#include <QtCore/qvarlengtharray.h> #include <algorithm> #include <functional> @@ -238,17 +239,15 @@ namespace QRangeModelDetails : std::true_type {}; - // we use std::rotate in moveRows/Columns, which requires std::swap and the - // iterators to be at least a forward iterator - template <typename It, typename = void> - struct test_rotate : std::false_type {}; - + // we use std::rotate in moveRows/Columns, which requires the values (which + // might be const if we only get a const iterator) to be swappable, and the + // iterator type to be at least a forward iterator template <typename It> - struct test_rotate<It, std::void_t<decltype(std::swap(*std::declval<It>(), - *std::declval<It>()))>> - : std::is_base_of<std::forward_iterator_tag, - typename std::iterator_traits<It>::iterator_category> - {}; + using test_rotate = std::conjunction< + std::is_swappable<decltype(*std::declval<It>())>, + std::is_base_of<std::forward_iterator_tag, + typename std::iterator_traits<It>::iterator_category> + >; template <typename C, typename = void> struct test_splice : std::false_type {}; @@ -1203,76 +1202,48 @@ public: return std::move(result.data()); } + static constexpr bool isRangeModelRole(int role) + { + return role == Qt::RangeModelDataRole + || role == Qt::RangeModelAdapterRole; + } + + static constexpr bool isPrimaryRole(int role) + { + return role == Qt::DisplayRole || role == Qt::EditRole; + } + QMap<int, QVariant> itemData(const QModelIndex &index) const { QMap<int, QVariant> result; - bool tried = false; - const auto readItemData = [this, &index, &result, &tried](const auto &value){ - Q_UNUSED(this); - Q_UNUSED(index); - using value_type = q20::remove_cvref_t<decltype(value)>; - using multi_role = QRangeModelDetails::is_multi_role<value_type>; - using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>; - if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) { - using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>; - tried = true; - const auto roles = this->itemModel().roleNames().keys(); - for (auto &role : roles) { - if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole) - continue; - QVariant data = ItemAccess::readRole(value, role); - if (data.isValid()) - result[role] = std::move(data); - } - } else if constexpr (multi_role()) { - tried = true; - if constexpr (std::is_convertible_v<value_type, decltype(result)>) { + if (index.isValid()) { + bool tried = false; + + // optimisation for items backed by a QMap<int, QVariant> or equivalent + readAt(index, [&result, &tried](const auto &value) { + if constexpr (std::is_convertible_v<decltype(value), decltype(result)>) { + tried = true; result = value; - } else { - const auto roleNames = [this]() -> QHash<int, QByteArray> { - Q_UNUSED(this); - if constexpr (!multi_role::int_key) - return this->itemModel().roleNames(); - else - return {}; - }(); - for (auto it = std::begin(value); it != std::end(value); ++it) { - const int role = [&roleNames, key = QRangeModelDetails::key(it)]() { - Q_UNUSED(roleNames); - if constexpr (multi_role::int_key) - return int(key); - else - return roleNames.key(key.toUtf8(), -1); - }(); - - if (role != -1 && role != Qt::RangeModelDataRole && role != Qt::RangeModelAdapterRole) - result.insert(role, QRangeModelDetails::value(it)); - } } - } else if constexpr (has_metaobject<value_type>) { - if (row_traits::fixed_size() <= 1) { - tried = true; - const auto roleNames = this->itemModel().roleNames(); - const auto end = roleNames.keyEnd(); - for (auto it = roleNames.keyBegin(); it != end; ++it) { - const int role = *it; - if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole) - continue; - QVariant data = readRole(index, role, QRangeModelDetails::pointerTo(value)); - if (data.isValid()) - result[role] = std::move(data); - } + }); + if (!tried) { + const auto roles = this->itemModel().roleNames().keys(); + QVarLengthArray<QModelRoleData, 16> roleDataArray; + roleDataArray.reserve(roles.size()); + for (auto role : roles) { + if (isRangeModelRole(role)) + continue; + roleDataArray.emplace_back(role); } - } - }; - - if (index.isValid()) { - readAt(index, readItemData); + QModelRoleDataSpan roleDataSpan(roleDataArray); + multiData(index, roleDataSpan); - if (!tried) { // no multi-role item found - result = this->itemModel().QAbstractItemModel::itemData(index); - result.remove(Qt::RangeModelAdapterRole); + for (auto &&roleData : std::move(roleDataSpan)) { + QVariant data = roleData.data(); + if (data.isValid()) + result[roleData.role()] = std::move(data); + } } } return result; @@ -1288,27 +1259,34 @@ public: using multi_role = QRangeModelDetails::is_multi_role<value_type>; using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>; + const auto readModelData = [&value](QModelRoleData &roleData){ + const int role = roleData.role(); + if (role == Qt::RangeModelDataRole) { + // Qt QML support: "modelData" role returns the entire multi-role item. + // QML can only use raw pointers to QObject (so we unwrap), and gadgets + // only by value (so we take the reference). + if constexpr (std::is_copy_assignable_v<wrapped_value_type>) + roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value))); + else + roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value))); + } else if (role == Qt::RangeModelAdapterRole) { + // for QRangeModelAdapter however, we want to respect smart pointer wrappers + if constexpr (std::is_copy_assignable_v<value_type>) + roleData.setData(QVariant::fromValue(value)); + else + roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value))); + } else { + return false; + } + return true; + }; + if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) { using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>; tried = true; for (auto &roleData : roleDataSpan) { - if (roleData.role() == Qt::RangeModelDataRole) { - // Qt QML support: "modelData" role returns the entire multi-role item. - // QML can only use raw pointers to QObject (so we unwrap), and gadgets - // only by value (so we take the reference). - if constexpr (std::is_copy_assignable_v<wrapped_value_type>) - roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value))); - else - roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value))); - } else if (roleData.role() == Qt::RangeModelAdapterRole) { - // for QRangeModelAdapter however, we want to respect smart pointer wrappers - if constexpr (std::is_copy_assignable_v<value_type>) - roleData.setData(QVariant::fromValue(value)); - else - roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value))); - } else { + if (!readModelData(roleData)) roleData.setData(ItemAccess::readRole(value, roleData.role())); - } } } else if constexpr (multi_role()) { tried = true; @@ -1337,21 +1315,7 @@ public: if (row_traits::fixed_size() <= 1) { tried = true; for (auto &roleData : roleDataSpan) { - if (roleData.role() == Qt::RangeModelDataRole) { - // Qt QML support: "modelData" role returns the entire multi-role item. - // QML can only use raw pointers to QObject (so we unwrap), and gadgets - // only by value (so we take the reference). - if constexpr (std::is_copy_assignable_v<wrapped_value_type>) - roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value))); - else - roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value))); - } else if (roleData.role() == Qt::RangeModelAdapterRole) { - // for QRangeModelAdapter however, we want to respect smart pointer wrappers - if constexpr (std::is_copy_assignable_v<value_type>) - roleData.setData(QVariant::fromValue(value)); - else - roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value))); - } else { + if (!readModelData(roleData)) { roleData.setData(readRole(index, roleData.role(), QRangeModelDetails::pointerTo(value))); } @@ -1360,7 +1324,7 @@ public: tried = true; for (auto &roleData : roleDataSpan) { const int role = roleData.role(); - if (role == Qt::DisplayRole || role == Qt::EditRole) { + if (isPrimaryRole(role)) { roleData.setData(readProperty(index.column(), QRangeModelDetails::pointerTo(value))); } else { @@ -1372,12 +1336,10 @@ public: tried = true; for (auto &roleData : roleDataSpan) { const int role = roleData.role(); - if (role == Qt::DisplayRole || role == Qt::EditRole - || role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole) { + if (isPrimaryRole(role) || isRangeModelRole(role)) roleData.setData(read(value)); - } else { + else roleData.clearData(); - } } } }); @@ -1395,7 +1357,8 @@ public: auto emitDataChanged = qScopeGuard([&success, this, &index, role]{ if (success) { Q_EMIT this->dataChanged(index, index, - role == Qt::EditRole || role == Qt::RangeModelDataRole || role == Qt::RangeModelDataRole + role == Qt::EditRole || role == Qt::RangeModelDataRole + || role == Qt::RangeModelAdapterRole ? QList<int>{} : QList<int>{role}); } }); @@ -1408,25 +1371,34 @@ public: using multi_role = QRangeModelDetails::is_multi_role<value_type>; auto setRangeModelDataRole = [&target, &data]{ - auto &targetRef = QRangeModelDetails::refTo(target); constexpr auto targetMetaType = QMetaType::fromType<value_type>(); const auto dataMetaType = data.metaType(); constexpr bool isWrapped = QRangeModelDetails::is_wrapped<value_type>(); if constexpr (!std::is_copy_assignable_v<wrapped_value_type>) { // we don't support replacing objects that are stored as raw pointers, // as this makes object ownership very messy. But we can replace objects - // stored in smart pointers. - if constexpr (isWrapped && !std::is_pointer_v<value_type> - && std::is_copy_assignable_v<value_type>) { - if (data.canConvert(targetMetaType)) { - target = data.value<value_type>(); - return true; + // stored in smart pointers, and we can initialize raw nullptr objects. + if constexpr (isWrapped) { + constexpr bool is_raw_pointer = std::is_pointer_v<value_type>; + if constexpr (!is_raw_pointer && std::is_copy_assignable_v<value_type>) { + if (data.canConvert(targetMetaType)) { + target = data.value<value_type>(); + return true; + } + } else if constexpr (is_raw_pointer) { + if (!QRangeModelDetails::isValid(target) && data.canConvert(targetMetaType)) { + target = data.value<value_type>(); + return true; + } + } else { + Q_UNUSED(target); } } // Otherwise we have a move-only or polymorph type. fall through to // error handling. } else if constexpr (isWrapped) { if (QRangeModelDetails::isValid(target)) { + auto &targetRef = QRangeModelDetails::refTo(target); // we need to get a wrapped value type out of the QVariant, which // might carry a pointer. We have to try all alternatives. if (const auto mt = QMetaType::fromType<wrapped_value_type>(); @@ -1440,10 +1412,10 @@ public: } } } else if (targetMetaType == dataMetaType) { - targetRef = data.value<value_type>(); + QRangeModelDetails::refTo(target) = data.value<value_type>(); return true; } else if (dataMetaType.flags() & QMetaType::PointerToGadget) { - targetRef = *data.value<value_type *>(); + QRangeModelDetails::refTo(target) = *data.value<value_type *>(); return true; } #ifndef QT_NO_DEBUG @@ -1455,17 +1427,16 @@ public: if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) { using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>; - if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole) + if (isRangeModelRole(role)) return setRangeModelDataRole(); return ItemAccess::writeRole(target, data, role); } if constexpr (has_metaobject<value_type>) { if (row_traits::fixed_size() <= 1) { // multi-role value - if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole) + if (isRangeModelRole(role)) return setRangeModelDataRole(); return writeRole(role, QRangeModelDetails::pointerTo(target), data); } else if (column <= row_traits::fixed_size() // multi-column - && (role == Qt::DisplayRole || role == Qt::EditRole - || role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)) { + && (isPrimaryRole(role) || isRangeModelRole(role))) { return writeProperty(column, QRangeModelDetails::pointerTo(target), data); } } else if constexpr (multi_role::value) { @@ -1492,14 +1463,20 @@ public: return write(target[roleToSet], data); else return write(target[roleNames.value(roleToSet)], data); - } else if (role == Qt::DisplayRole || role == Qt::EditRole - || role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole) { + } else if (isPrimaryRole(role) || isRangeModelRole(role)) { return write(target, data); } return false; }; success = writeAt(index, writeData); + + if constexpr (itemsAreQObjects) { + if (success && isRangeModelRole(role) && this->autoConnectPolicy() == AutoConnectPolicy::Full) { + if (QObject *item = data.value<QObject *>()) + Self::connectProperties(index, item, m_data.context, m_data.properties); + } + } } return success; } @@ -1607,7 +1584,7 @@ public: tried = true; auto targetCopy = makeCopy(target); for (auto &&[role, value] : data.asKeyValueRange()) { - if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole) + if (isRangeModelRole(role)) continue; if (!writeRole(role, QRangeModelDetails::pointerTo(targetCopy), value)) { const QByteArray roleName = roleNames.value(role); diff --git a/src/corelib/itemmodels/qrangemodeladapter.h b/src/corelib/itemmodels/qrangemodeladapter.h index bd3342e6185..0234c402248 100644 --- a/src/corelib/itemmodels/qrangemodeladapter.h +++ b/src/corelib/itemmodels/qrangemodeladapter.h @@ -19,13 +19,11 @@ class QT_TECH_PREVIEW_API QRangeModelAdapter #ifdef Q_QDOC using range_type = Range; - using const_row_reference = typename std::iterator_traits<Range>::const_reference; - using row_reference = typename std::iterator_traits<Range>::reference; #else using range_type = QRangeModelDetails::wrapped_t<Range>; +#endif using const_row_reference = typename Impl::const_row_reference; using row_reference = typename Impl::row_reference; -#endif using range_features = typename QRangeModelDetails::range_traits<range_type>; using row_type = std::remove_reference_t<row_reference>; using row_features = QRangeModelDetails::range_traits<typename Impl::wrapped_row_type>; @@ -78,16 +76,22 @@ class QT_TECH_PREVIEW_API QRangeModelAdapter template <typename C> using if_compatible_row_range = std::enable_if_t<is_compatible_row_range<C>, bool>; template <typename Data> - static constexpr bool is_compatible_data = true; - // std::is_convertible_v<Data, decltype(*std::begin(std::declval<const_row_reference>()))>; + static constexpr bool is_compatible_data = std::is_convertible_v<Data, data_type>; template <typename Data> using if_compatible_data = std::enable_if_t<is_compatible_data<Data>, bool>; template <typename C> static constexpr bool is_compatible_data_range = is_compatible_data< + typename QRangeModelDetails::data_type< + typename QRangeModelDetails::row_traits< decltype(*std::begin(std::declval<C&>())) - >; + >::item_type + >::type + >; template <typename C> - using if_compatible_data_range = std::enable_if_t<is_compatible_data_range<C>, bool>; + using if_compatible_column_data = std::enable_if_t<is_compatible_data<C> + || is_compatible_data_range<C>, bool>; + template <typename C> + using if_compatible_column_range = std::enable_if_t<is_compatible_data_range<C>, bool>; template <typename R> using if_assignable_range = std::enable_if_t<std::is_assignable_v<range_type, R>, bool>; @@ -141,6 +145,7 @@ public: } DataReference(const DataReference &other) = default; + DataReference(DataReference &&other) = default; // reference (not std::reference_wrapper) semantics DataReference &operator=(const DataReference &other) @@ -149,29 +154,23 @@ public: return *this; } + DataReference &operator=(DataReference &&other) + { + *this = other.get(); + return *this; + } + ~DataReference() = default; DataReference &operator=(const value_type &value) { - constexpr Qt::ItemDataRole dataRole = Qt::RangeModelAdapterRole; + assign(value); + return *this; + } - if (m_index.isValid()) { - auto model = const_cast<QAbstractItemModel *>(m_index.model()); - [[maybe_unused]] bool couldWrite = false; - if constexpr (std::is_same_v<q20::remove_cvref_t<value_type>, QVariant>) - couldWrite = model->setData(m_index, value, dataRole); - else - couldWrite = model->setData(m_index, QVariant::fromValue(value), dataRole); -#ifndef QT_NO_DEBUG - if (!couldWrite) { - qWarning() << "Writing value of type" << QMetaType::fromType<value_type>().name() - << "to role" << dataRole << "at index" << m_index - << "of the model failed"; - } - } else { - qCritical("Data reference for invalid index, can't write to model"); -#endif - } + DataReference &operator=(value_type &&value) + { + assign(std::move(value)); return *this; } @@ -196,6 +195,33 @@ public: private: QModelIndex m_index; + template <typename Value> + void assign(Value &&value) + { + constexpr Qt::ItemDataRole dataRole = Qt::RangeModelAdapterRole; + + if (m_index.isValid()) { + auto model = const_cast<QAbstractItemModel *>(m_index.model()); + [[maybe_unused]] bool couldWrite = false; + if constexpr (std::is_same_v<q20::remove_cvref_t<Value>, QVariant>) { + couldWrite = model->setData(m_index, value, dataRole); + } else { + couldWrite = model->setData(m_index, + QVariant::fromValue(std::forward<Value>(value)), + dataRole); + } +#ifndef QT_NO_DEBUG + if (!couldWrite) { + qWarning() << "Writing value of type" + << QMetaType::fromType<q20::remove_cvref_t<Value>>().name() + << "to role" << dataRole << "at index" << m_index << "failed"; + } + } else { + qCritical("Data reference for invalid index, can't write to model"); +#endif + } + } + friend inline bool comparesEqual(const DataReference &lhs, const DataReference &rhs) { return lhs.m_index == rhs.m_index @@ -1020,60 +1046,55 @@ public: template <typename NewRange = range_type, if_assignable_range<NewRange> = true> void setRange(NewRange &&newRange) { - using namespace QRangeModelDetails; - - auto *impl = storage.implementation(); - const QModelIndex root = storage.root(); - const qsizetype newLastRow = qsizetype(Impl::size(refTo(newRange))) - 1; - auto *oldRange = impl->childRange(root); - const qsizetype oldLastRow = qsizetype(Impl::size(oldRange)) - 1; - - if (!root.isValid()) { - impl->beginResetModel(); - impl->deleteOwnedRows(); - } else if constexpr (is_tree<Impl>) { - if (oldLastRow > 0) { - impl->beginRemoveRows(root, 0, model()->rowCount(root) - 1); - impl->deleteRemovedRows(refTo(oldRange)); - impl->endRemoveRows(); - } - if (newLastRow > 0) - impl->beginInsertRows(root, 0, newLastRow); - } else { - Q_ASSERT_X(false, "QRangeModelAdapter::setRange", - "Internal error: The root index in a table or list must be invalid."); - } - refTo(oldRange) = std::forward<NewRange>(newRange); - if (!root.isValid()) { - impl->endResetModel(); - } else if constexpr (is_tree<Impl>) { - if (newLastRow > 0) { - Q_ASSERT(model()->hasChildren(root)); - // if it was moved, then newRange is now likely to be empty. Get - // the inserted row. - impl->setParentRow(refTo(impl->childRange(storage.root())), - pointerTo(impl->rowData(root))); - impl->endInsertRows(); - } - } - if constexpr (Impl::itemsAreQObjects) { - if (model()->autoConnectPolicy() == QRangeModel::AutoConnectPolicy::Full) { - const auto begin = QRangeModelDetails::begin(refTo(oldRange)); - const auto end = QRangeModelDetails::end(refTo(oldRange)); - int rowIndex = 0; - for (auto it = begin; it != end; ++it, ++rowIndex) - impl->autoConnectPropertiesInRow(*it, rowIndex, root); - } - } + setRangeImpl(qsizetype(Impl::size(QRangeModelDetails::refTo(newRange))) - 1, + [&newRange](auto &oldRange) { + oldRange = std::forward<NewRange>(newRange); + }); } - template <typename NewRange = range_type, if_assignable_range<NewRange> = true> + template <typename NewRange = range_type, if_assignable_range<NewRange> = true, + unless_adapter<NewRange> = true> QRangeModelAdapter &operator=(NewRange &&newRange) { setRange(std::forward<NewRange>(newRange)); return *this; } + template <typename Row, if_assignable_range<std::initializer_list<Row>> = true> + void setRange(std::initializer_list<Row> newRange) + { + setRangeImpl(qsizetype(newRange.size() - 1), [&newRange](auto &oldRange) { + oldRange = newRange; + }); + } + + template <typename Row, if_assignable_range<std::initializer_list<Row>> = true> + QRangeModelAdapter &operator=(std::initializer_list<Row> newRange) + { + setRange(newRange); + return *this; + } + + template <typename Row, if_assignable_range<std::initializer_list<Row>> = true> + void assign(std::initializer_list<Row> newRange) + { + setRange(newRange); + } + + template <typename InputIterator, typename Sentinel, typename I = Impl, if_writable<I> = true> + void setRange(InputIterator first, Sentinel last) + { + setRangeImpl(qsizetype(std::distance(first, last) - 1), [first, last](auto &oldRange) { + oldRange.assign(first, last); + }); + } + + template <typename InputIterator, typename Sentinel, typename I = Impl, if_writable<I> = true> + void assign(InputIterator first, Sentinel last) + { + setRange(first, last); + } + // iterator API ConstRowIterator cbegin() const { @@ -1245,12 +1266,12 @@ public: decltype(auto) operator[](int row) const { return at(row); } template <typename I = Impl, if_table<I> = true, if_writable<I> = true> - decltype(auto) at(int row) + auto at(int row) { return RowReference{index(row, 0), this}; } template <typename I = Impl, if_table<I> = true, if_writable<I> = true> - decltype(auto) operator[](int row) { return at(row); } + auto operator[](int row) { return at(row); } // at/operator[int, int] for table: returns value at row/column template <typename I = Impl, unless_list<I> = true> @@ -1428,15 +1449,15 @@ public: return storage.m_model->insertColumn(before); } - template <typename D = row_type, typename I = Impl, - if_canInsertColumns<I> = true, if_compatible_data<D> = true> + template <typename D, typename I = Impl, + if_canInsertColumns<I> = true, if_compatible_column_data<D> = true> bool insertColumn(int before, D &&data) { return insertColumnImpl(before, storage.root(), std::forward<D>(data)); } template <typename C, typename I = Impl, - if_canInsertColumns<I> = true, if_compatible_data_range<C> = true> + if_canInsertColumns<I> = true, if_compatible_column_range<C> = true> bool insertColumns(int before, C &&data) { return insertColumnsImpl(before, storage.root(), std::forward<C>(data)); @@ -1503,6 +1524,65 @@ private: Q_EMIT storage.implementation()->dataChanged(topLeft, bottomRight, {}); } + void beginSetRangeImpl(Impl *impl, range_type *oldRange, qsizetype newLastRow) + { + const QModelIndex root = storage.root(); + const qsizetype oldLastRow = qsizetype(Impl::size(oldRange)) - 1; + + if (!root.isValid()) { + impl->beginResetModel(); + impl->deleteOwnedRows(); + } else if constexpr (is_tree<Impl>) { + if (oldLastRow > 0) { + impl->beginRemoveRows(root, 0, model()->rowCount(root) - 1); + impl->deleteRemovedRows(QRangeModelDetails::refTo(oldRange)); + impl->endRemoveRows(); + } + if (newLastRow > 0) + impl->beginInsertRows(root, 0, newLastRow); + } else { + Q_ASSERT_X(false, "QRangeModelAdapter::setRange", + "Internal error: The root index in a table or list must be invalid."); + } + } + + void endSetRangeImpl(Impl *impl, qsizetype newLastRow) + { + const QModelIndex root = storage.root(); + if (!root.isValid()) { + impl->endResetModel(); + } else if constexpr (is_tree<Impl>) { + if (newLastRow > 0) { + Q_ASSERT(model()->hasChildren(root)); + // if it was moved, then newRange is now likely to be empty. Get + // the inserted row. + impl->setParentRow(QRangeModelDetails::refTo(impl->childRange(root)), + QRangeModelDetails::pointerTo(impl->rowData(root))); + impl->endInsertRows(); + } + } + } + + template <typename Assigner> + void setRangeImpl(qsizetype newLastRow, Assigner &&assigner) + { + auto *impl = storage.implementation(); + auto *oldRange = impl->childRange(storage.root()); + beginSetRangeImpl(impl, oldRange, newLastRow); + assigner(QRangeModelDetails::refTo(oldRange)); + endSetRangeImpl(impl, newLastRow); + + if constexpr (Impl::itemsAreQObjects) { + if (model()->autoConnectPolicy() == QRangeModel::AutoConnectPolicy::Full) { + const auto begin = QRangeModelDetails::begin(QRangeModelDetails::refTo(oldRange)); + const auto end = QRangeModelDetails::end(QRangeModelDetails::refTo(oldRange)); + int rowIndex = 0; + for (auto it = begin; it != end; ++it, ++rowIndex) + impl->autoConnectPropertiesInRow(*it, rowIndex, storage.root()); + } + } + } + template <typename P> static auto setParentRow(P protocol, row_type &newRow, row_ptr parentRow) -> decltype(protocol.setParentRow(std::declval<row_type&>(), std::declval<row_ptr>())) diff --git a/src/corelib/itemmodels/qrangemodeladapter.qdoc b/src/corelib/itemmodels/qrangemodeladapter.qdoc index 5ab128a8c5f..88872589299 100644 --- a/src/corelib/itemmodels/qrangemodeladapter.qdoc +++ b/src/corelib/itemmodels/qrangemodeladapter.qdoc @@ -118,11 +118,9 @@ would bypass the QAbstractItemModel notification protocol, those reference objects prevent direct modifications of the items. - \note Calling mutable overloads generates some overhead. Make a const copy - of the adapter (which will not copy the data), or use \c{std::as_const} to - make sure that only the const overloads are used in performance critical, - read-heavy code. This is the same technique as when accessing elements in - an implicitly shared Qt container. + \note Accessing the reference object always makes a call to the model to get + a copy of the value. This can be expensive; for performance critical access + to data, store a copy. \section3 Item access @@ -279,14 +277,6 @@ */ /*! - \typedef QRangeModelAdapter::const_row_reference -*/ - -/*! - \typedef QRangeModelAdapter::row_reference -*/ - -/*! \typedef QRangeModelAdapter::range_type */ @@ -331,16 +321,39 @@ /*! \fn template <typename Range, typename Protocol, typename Model> template <typename NewRange, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<NewRange>> void QRangeModelAdapter<Range, Protocol, Model>::setRange(NewRange &&newRange) - \fn template <typename Range, typename Protocol, typename Model> template <typename NewRange, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<NewRange>> QRangeModelAdapter &QRangeModelAdapter<Range, Protocol, Model>::operator=(NewRange &&newRange) + \fn template <typename Range, typename Protocol, typename Model> template <typename NewRange, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<NewRange>, QRangeModelAdapter<Range, Protocol, Model>::unless_adapter<NewRange>> QRangeModelAdapter &QRangeModelAdapter<Range, Protocol, Model>::operator=(NewRange &&newRange) + + Replaces the contents of the model with the rows in \a newRange, possibly + using move semantics. - Assigns \a newRange to the stored range, possibly using move semantics. This function makes the model() emit the \l{QAbstractItemModel::}{modelAboutToBeReset()} and \l{QAbstractItemModel::}{modelReset()} signals. \constraints \c Range is mutable, and \a newRange is of a type that can be - assigned to \c Range. + assigned to \c Range, but not a QRangeModelAdapter. + + \sa range(), at(), model(), assign() +*/ + +/*! + \fn template <typename Range, typename Protocol, typename Model> template <typename Row, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<std::initializer_list<Row>>> void QRangeModelAdapter<Range, Protocol, Model>::setRange(std::initializer_list<Row> newRange) + \fn template <typename Range, typename Protocol, typename Model> template <typename Row, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<std::initializer_list<Row>>> QRangeModelAdapter &QRangeModelAdapter<Range, Protocol, Model>::assign(std::initializer_list<Row> newRange) + \fn template <typename Range, typename Protocol, typename Model> template <typename Row, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<std::initializer_list<Row>>> QRangeModelAdapter &QRangeModelAdapter<Range, Protocol, Model>::operator=(std::initializer_list<Row> newRange) + + Replaces the contents of the model with the rows in \a newRange. - \sa range(), at(), model() + \constraints \c Range is mutable, and \a newRange can be assigned to \c Range. + + \sa range(), at, model() +*/ + +/*! + \fn template <typename Range, typename Protocol, typename Model> template <typename InputIterator, typename Sentinel, typename I = Impl, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> void QRangeModelAdapter<Range, Protocol, Model>::setRange(InputIterator first, Sentinel last) + \fn template <typename Range, typename Protocol, typename Model> template <typename InputIterator, typename Sentinel, typename I = Impl, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> void QRangeModelAdapter<Range, Protocol, Model>::assign(InputIterator first, Sentinel last) + + Replaces the contents of the models with the rows in the range [\a first, \a last). + + \sa range(), at, model() */ /*! @@ -510,8 +523,8 @@ */ /*! - \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> QRangeModelAdapter<Range, Protocol, Model>::const_row_reference QRangeModelAdapter<Range, Protocol, Model>::at(int row) const - \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> QRangeModelAdapter<Range, Protocol, Model>::const_row_reference QRangeModelAdapter<Range, Protocol, Model>::operator[](int row) const + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> decltype(auto) QRangeModelAdapter<Range, Protocol, Model>::at(int row) const + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> decltype(auto) QRangeModelAdapter<Range, Protocol, Model>::operator[](int row) const \return a constant reference to the row at \a row, as stored in \c Range. @@ -519,8 +532,8 @@ */ /*! - \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_table<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> QRangeModelAdapter<Range, Protocol, Model>::row_reference QRangeModelAdapter<Range, Protocol, Model>::at(int row) - \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_table<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> QRangeModelAdapter<Range, Protocol, Model>::row_reference QRangeModelAdapter<Range, Protocol, Model>::operator[](int row) + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_table<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::at(int row) + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_table<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](int row) \return a mutable reference to the row at \a row, as stored in \c Range. @@ -576,6 +589,16 @@ */ /*! + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> decltype(auto) QRangeModelAdapter<Range, Protocol, Model>::at(QSpan<const int> path) const + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> decltype(auto) QRangeModelAdapter<Range, Protocol, Model>::operator[](QSpan<const int> path) const + + \return a constant reference to the row specified by \a path, as stored in + \c Range. + + \constraints \c Range is a tree. +*/ + +/*! \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::at(QSpan<const int> path) \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](QSpan<const int> path) @@ -812,7 +835,7 @@ */ /*! - \fn template <typename Range, typename Protocol, typename Model> template <typename D, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertColumns<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_data<D>> bool QRangeModelAdapter<Range, Protocol, Model>::insertColumn(int before, D &&data) + \fn template <typename Range, typename Protocol, typename Model> template <typename D, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertColumns<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_column_data<D>> bool QRangeModelAdapter<Range, Protocol, Model>::insertColumn(int before, D &&data) \overload Inserts a single column constructed from \a data before the column specified @@ -840,7 +863,7 @@ */ /*! - \fn template <typename Range, typename Protocol, typename Model> template <typename C, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertColumns<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_data_range<C>> bool QRangeModelAdapter<Range, Protocol, Model>::insertColumns(int before, C &&data) + \fn template <typename Range, typename Protocol, typename Model> template <typename C, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertColumns<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_column_range<C>> bool QRangeModelAdapter<Range, Protocol, Model>::insertColumns(int before, C &&data) Inserts columns constructed from the elements in \a data before the column specified by \a before into all rows, and returns whether the insertion was |
