diff options
Diffstat (limited to 'src/corelib/itemmodels')
| -rw-r--r-- | src/corelib/itemmodels/qrangemodel_impl.h | 18 | ||||
| -rw-r--r-- | src/corelib/itemmodels/qrangemodeladapter.h | 232 | ||||
| -rw-r--r-- | src/corelib/itemmodels/qrangemodeladapter.qdoc | 61 |
3 files changed, 207 insertions, 104 deletions
diff --git a/src/corelib/itemmodels/qrangemodel_impl.h b/src/corelib/itemmodels/qrangemodel_impl.h index f6b08099fe7..7eca3094a66 100644 --- a/src/corelib/itemmodels/qrangemodel_impl.h +++ b/src/corelib/itemmodels/qrangemodel_impl.h @@ -239,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 {}; 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 263bff0dd0c..88872589299 100644 --- a/src/corelib/itemmodels/qrangemodeladapter.qdoc +++ b/src/corelib/itemmodels/qrangemodeladapter.qdoc @@ -277,14 +277,6 @@ */ /*! - \typedef QRangeModelAdapter::const_row_reference -*/ - -/*! - \typedef QRangeModelAdapter::row_reference -*/ - -/*! \typedef QRangeModelAdapter::range_type */ @@ -329,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() */ /*! @@ -508,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. @@ -517,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. @@ -574,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) @@ -810,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 @@ -838,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 |
